2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/util/light_process.h"
18 #include "hphp/util/process.h"
20 #include "hphp/util/logger.h"
31 #include <boost/scoped_array.hpp>
35 ///////////////////////////////////////////////////////////////////////////////
38 static const unsigned int BUFFER_SIZE
= 4096;
39 Mutex
LightProcess::s_mutex
;
41 static void read_buf(FILE *fin
, char *buf
) {
42 if (!fgets(buf
, BUFFER_SIZE
, fin
)) {
47 buf
[strlen(buf
) - 1] = '\0';
50 static bool send_fd(int afdt_fd
, int fd
) {
53 int ret
= afdt_send_fd_msg(afdt_fd
, 0, 0, fd
, &err
);
54 if (ret
< 0 && errno
== 0) {
55 // Set non-empty errno if afdt_send_fd_msg doesn't set one on error
61 static int recv_fd(int afdt_fd
) {
64 uint8_t afdt_buf
[AFDT_MSGLEN
];
67 if (afdt_recv_fd_msg(afdt_fd
, afdt_buf
, &afdt_len
, &fd
, &err
) < 0) {
69 // Set non-empty errno if afdt_send_fd_msg doesn't set one on error
77 static char **build_envp(const vector
<string
> &env
) {
78 char **envp
= nullptr;
79 int size
= env
.size();
81 envp
= (char **)malloc((size
+ 1) * sizeof(char *));
83 for (unsigned int i
= 0; i
< env
.size(); i
++, j
++) {
84 *(envp
+ j
) = (char *)env
[i
].c_str();
86 *(envp
+ j
) = nullptr;
91 static void close_fds(const vector
<int> &fds
) {
92 for (unsigned int i
= 0; i
< fds
.size(); i
++) {
97 ///////////////////////////////////////////////////////////////////////////////
98 // shadow process tasks
100 static void do_popen(FILE *fin
, FILE *fout
, int afdt_fd
) {
101 char buf
[BUFFER_SIZE
];
102 char cwd
[BUFFER_SIZE
];
104 if (!fgets(buf
, BUFFER_SIZE
, fin
)) buf
[0] = '\0';
105 bool read_only
= (buf
[0] == 'r');
109 string old_cwd
= Process::GetCurrentDirectory();
111 if (old_cwd
!= cwd
) {
113 // Ignore chdir failures, because the compiled version might not have the
114 // directory any more.
115 Logger::Warning("Light Process failed chdir to %s.", cwd
);
119 FILE *f
= buf
[0] ? ::popen(buf
, read_only
? "r" : "w") : nullptr;
121 if (old_cwd
!= cwd
&& chdir(old_cwd
.c_str())) {
122 // only here if we can't change the cwd back
126 Logger::Error("Light process failed popen: %d (%s).", errno
,
128 fprintf(fout
, "error\n");
131 fprintf(fout
, "success\n%" PRId64
"\n", (int64_t)f
);
134 send_fd(afdt_fd
, fd
);
138 static void do_pclose(FILE *fin
, FILE *fout
) {
139 char buf
[BUFFER_SIZE
];
143 sscanf(buf
, "%" PRId64
, &fptr
);
144 FILE *f
= (FILE *)fptr
;
145 int ret
= ::pclose(f
);
147 fprintf(fout
, "%d\n", ret
);
149 fprintf(fout
, "%d\n", errno
);
154 static void do_proc_open(FILE *fin
, FILE *fout
, int afdt_fd
) {
155 char cmd
[BUFFER_SIZE
];
157 if (strlen(cmd
) == 0) {
158 fprintf(fout
, "error\n%d\n", ENOENT
);
163 char cwd
[BUFFER_SIZE
];
166 char buf
[BUFFER_SIZE
];
170 sscanf(buf
, "%d", &env_size
);
171 for (int i
= 0; i
< env_size
; i
++) {
178 sscanf(buf
, "%d", &pipe_size
);
180 for (int i
= 0; i
< pipe_size
; i
++) {
183 sscanf(buf
, "%d", &fd_value
);
184 pvals
.push_back(fd_value
);
188 for (int i
= 0; i
< pipe_size
; i
++) {
189 int fd
= recv_fd(afdt_fd
);
191 fprintf(fout
, "error\n%d\n", EPROTO
);
199 // now ready to start the child process
200 pid_t child
= fork();
202 for (int i
= 0; i
< pipe_size
; i
++) {
203 dup2(pkeys
[i
], pvals
[i
]);
205 if (strlen(cwd
) > 0 && chdir(cwd
)) {
206 // non-zero for error
207 // chdir failed, the working directory remains unchanged
210 char **envp
= build_envp(env
);
211 execle("/bin/sh", "sh", "-c", cmd
, nullptr, envp
);
214 execl("/bin/sh", "sh", "-c", cmd
, nullptr);
217 } else if (child
> 0) {
218 // successfully created the child process
219 fprintf(fout
, "%" PRId64
"\n", (int64_t)child
);
222 // failed creating the child process
223 fprintf(fout
, "error\n%d\n", errno
);
230 static pid_t waited
= 0;
232 static void kill_handler(int sig
) {
233 if (sig
== SIGALRM
&& waited
) {
234 kill(waited
, SIGKILL
);
238 static void do_waitpid(FILE *fin
, FILE *fout
) {
239 char buf
[BUFFER_SIZE
];
244 sscanf(buf
, "%" PRId64
" %d %d", &p
, &options
, &timeout
);
245 pid_t pid
= (pid_t
)p
;
249 signal(SIGALRM
, kill_handler
);
252 pid_t ret
= ::waitpid(pid
, &stat
, options
);
253 alarm(0); // cancel the previous alarm if not triggered yet
255 fprintf(fout
, "%" PRId64
" %d\n", (int64_t)ret
, stat
);
257 fprintf(fout
, "%d\n", errno
);
262 static void do_change_user(FILE *fin
, FILE *fout
) {
263 char uname
[BUFFER_SIZE
];
264 read_buf(fin
, uname
);
265 if (strlen(uname
) > 0) {
266 struct passwd
*pw
= getpwnam(uname
);
278 ///////////////////////////////////////////////////////////////////////////////
279 // light-weight process
281 static boost::scoped_array
<LightProcess
> g_procs
;
282 static int g_procsCount
= 0;
283 static bool s_handlerInited
= false;
284 static LightProcess::LostChildHandler s_lostChildHandler
;
286 LightProcess::LightProcess()
287 : m_shadowProcess(0), m_fin(nullptr), m_fout(nullptr), m_afdt_fd(-1),
290 LightProcess::~LightProcess() {
293 void LightProcess::SigChldHandler(int sig
, siginfo_t
* info
, void* ctx
) {
294 if (info
->si_code
!= CLD_EXITED
&&
295 info
->si_code
!= CLD_KILLED
&&
296 info
->si_code
!= CLD_DUMPED
) {
299 pid_t pid
= info
->si_pid
;
300 for (int i
= 0; i
< g_procsCount
; ++i
) {
301 if (g_procs
&& g_procs
[i
].m_shadowProcess
== pid
) {
302 // The exited process was a light process. Notify the callback, if any.
303 if (s_lostChildHandler
) {
304 s_lostChildHandler(pid
);
311 void LightProcess::Initialize(const std::string
&prefix
, int count
,
312 const vector
<int> &inherited_fds
) {
313 if (prefix
.empty() || count
<= 0) {
318 // already initialized
322 g_procs
.reset(new LightProcess
[count
]);
323 g_procsCount
= count
;
325 for (int i
= 0; i
< count
; i
++) {
326 if (!g_procs
[i
].initShadow(prefix
, i
, inherited_fds
)) {
327 for (int j
= 0; j
< i
; j
++) {
328 g_procs
[j
].closeShadow();
336 if (!s_handlerInited
) {
338 struct sigaction old_sa
;
339 sa
.sa_sigaction
= &LightProcess::SigChldHandler
;
340 sa
.sa_flags
= SA_SIGINFO
| SA_NOCLDSTOP
;
341 if (sigaction(SIGCHLD
, &sa
, &old_sa
) != 0) {
342 Logger::Error("Couldn't install SIGCHLD handler");
345 s_handlerInited
= true;
349 bool LightProcess::initShadow(const std::string
&prefix
, int id
,
350 const vector
<int> &inherited_fds
) {
351 Lock
lock(m_procMutex
);
353 std::ostringstream os
;
354 os
<< prefix
<< "." << getpid() << "." << id
;
355 m_afdtFilename
= os
.str();
357 // remove the possible leftover
358 remove(m_afdtFilename
.c_str());
361 m_afdt_lfd
= afdt_listen(m_afdtFilename
.c_str(), &err
);
362 if (m_afdt_lfd
< 0) {
363 Logger::Warning("Unable to afdt_listen");
368 if (!p1
.open() || !p2
.open()) {
369 Logger::Warning("Unable to create pipe: %d %s", errno
,
370 Util::safe_strerror(errno
).c_str());
374 pid_t child
= fork();
377 pid_t sid
= setsid();
379 Logger::Warning("Unable to setsid");
383 afdt_connect(m_afdtFilename
.c_str(), &err
);
385 Logger::Warning("Unable to afdt_connect");
388 int fd1
= p1
.detachOut();
389 int fd2
= p2
.detachIn();
393 // don't hold on to previous light processes' pipes, inherited
394 // fds, or the afdt listening socket
395 for (int i
= 0; i
< id
; i
++) {
396 g_procs
[i
].closeFiles();
398 close_fds(inherited_fds
);
402 } else if (child
< 0) {
404 Logger::Warning("Unable to fork lightly: %d %s", errno
,
405 Util::safe_strerror(errno
).c_str());
409 m_fin
= fdopen(p2
.detachOut(), "r");
410 m_fout
= fdopen(p1
.detachIn(), "w");
411 m_shadowProcess
= child
;
415 m_afdt_fd
= accept(m_afdt_lfd
, &addr
, &addrlen
);
417 Logger::Warning("Unable to establish afdt connection");
425 void LightProcess::Close() {
426 for (int i
= 0; i
< g_procsCount
; i
++) {
427 g_procs
[i
].closeShadow();
433 void LightProcess::closeShadow() {
434 Lock
lock(m_procMutex
);
435 if (m_shadowProcess
) {
436 fprintf(m_fout
, "exit\n");
440 // removes the "zombie" process, so not to interfere with later waits
441 ::waitpid(m_shadowProcess
, nullptr, 0);
443 if (!m_afdtFilename
.empty()) {
444 remove(m_afdtFilename
.c_str());
446 if (m_afdt_fd
>= 0) {
453 void LightProcess::closeFiles() {
460 bool LightProcess::Available() {
461 return g_procsCount
> 0;
464 void LightProcess::runShadow(int fdin
, int fdout
) {
465 FILE *fin
= fdopen(fdin
, "r");
466 FILE *fout
= fdopen(fdout
, "w");
468 char buf
[BUFFER_SIZE
];
472 pfd
[0].events
= POLLIN
;
474 int ret
= poll(pfd
, 1, -1);
475 if (ret
< 0 && errno
== EINTR
) {
478 if (pfd
[0].revents
& POLLHUP
) {
479 // no more command can come in
480 Logger::Error("Lost parent, LightProcess exiting");
483 else if (pfd
[0].revents
& POLLIN
) {
484 if (!fgets(buf
, BUFFER_SIZE
, fin
)) buf
[0] = '\0';
485 if (strncmp(buf
, "exit", 4) == 0) {
486 Logger::Info("LightProces exiting upon request");
488 } else if (strncmp(buf
, "popen", 5) == 0) {
489 do_popen(fin
, fout
, m_afdt_fd
);
490 } else if (strncmp(buf
, "pclose", 6) == 0) {
491 do_pclose(fin
, fout
);
492 } else if (strncmp(buf
, "proc_open", 9) == 0) {
493 do_proc_open(fin
, fout
, m_afdt_fd
);
494 } else if (strncmp(buf
, "waitpid", 7) == 0) {
495 do_waitpid(fin
, fout
);
496 } else if (strncmp(buf
, "change_user", 11) == 0) {
497 do_change_user(fin
, fout
);
505 remove(m_afdtFilename
.c_str());
509 int LightProcess::GetId() {
510 return (long)pthread_self() % g_procsCount
;
513 FILE *LightProcess::popen(const char *cmd
, const char *type
,
514 const char *cwd
/* = NULL */) {
516 // fallback to normal popen
517 Logger::Verbose("Light-weight fork not available; "
518 "use the heavy one instead.");
520 FILE *f
= LightPopenImpl(cmd
, type
, cwd
);
524 Logger::Verbose("Light-weight fork failed; use the heavy one instead.");
526 return HeavyPopenImpl(cmd
, type
, cwd
);
529 FILE *LightProcess::HeavyPopenImpl(const char *cmd
, const char *type
,
532 string old_cwd
= Process::GetCurrentDirectory();
533 if (old_cwd
!= cwd
) {
536 Logger::Warning("Failed to chdir to %s.", cwd
);
538 FILE *f
= ::popen(cmd
, type
);
539 if (chdir(old_cwd
.c_str())) {
540 // error occured changing cwd back
545 return ::popen(cmd
, type
);
548 FILE *LightProcess::LightPopenImpl(const char *cmd
, const char *type
,
551 Lock
lock(g_procs
[id
].m_procMutex
);
553 fprintf(g_procs
[id
].m_fout
, "popen\n%s\n%s\n%s\n", type
, cmd
, cwd
);
554 fflush(g_procs
[id
].m_fout
);
556 char buf
[BUFFER_SIZE
];
557 read_buf(g_procs
[id
].m_fin
, buf
);
558 if (strncmp(buf
, "error", 5) == 0) {
563 read_buf(g_procs
[id
].m_fin
, buf
);
564 sscanf(buf
, "%" PRId64
, &fptr
);
566 Logger::Error("Light process failed to return the file pointer.");
570 int fd
= recv_fd(g_procs
[id
].m_afdt_fd
);
572 Logger::Error("Light process failed to send the file descriptor.");
575 FILE *f
= fdopen(fd
, type
);
576 g_procs
[id
].m_popenMap
[(int64_t)f
] = fptr
;
581 int LightProcess::pclose(FILE *f
) {
587 Lock
lock(g_procs
[id
].m_procMutex
);
589 std::map
<int64_t, int64_t>::iterator it
= g_procs
[id
].m_popenMap
.find((int64_t)f
);
590 if (it
== g_procs
[id
].m_popenMap
.end()) {
591 // try to close it with normal pclose
595 int64_t f2
= it
->second
;
596 g_procs
[id
].m_popenMap
.erase((int64_t)f
);
598 fprintf(g_procs
[id
].m_fout
, "pclose\n%" PRId64
"\n", f2
);
599 fflush(g_procs
[id
].m_fout
);
601 char buf
[BUFFER_SIZE
];
602 read_buf(g_procs
[id
].m_fin
, buf
);
604 sscanf(buf
, "%d", &ret
);
606 read_buf(g_procs
[id
].m_fin
, buf
);
607 sscanf(buf
, "%d", &errno
);
612 pid_t
LightProcess::proc_open(const char *cmd
, const vector
<int> &created
,
613 const vector
<int> &desired
,
614 const char *cwd
, const vector
<string
> &env
) {
616 Lock
lock(g_procs
[id
].m_procMutex
);
617 always_assert(Available());
618 always_assert(created
.size() == desired
.size());
620 if (fprintf(g_procs
[id
].m_fout
, "proc_open\n%s\n%s\n", cmd
, cwd
) <= 0) {
621 Logger::Error("Failed to send command proc_open");
624 fprintf(g_procs
[id
].m_fout
, "%d\n", (int)env
.size());
625 for (unsigned int i
= 0; i
< env
.size(); i
++) {
626 fprintf(g_procs
[id
].m_fout
, "%s\n", env
[i
].c_str());
629 fprintf(g_procs
[id
].m_fout
, "%d\n", (int)created
.size());
631 for (unsigned int i
= 0; i
< desired
.size(); i
++) {
632 fprintf(g_procs
[id
].m_fout
, "%d\n", desired
[i
]);
634 fflush(g_procs
[id
].m_fout
);
635 bool error_send
= false;
637 for (unsigned int i
= 0; i
< created
.size(); i
++) {
638 if (!send_fd(g_procs
[id
].m_afdt_fd
, created
[i
])) {
645 char buf
[BUFFER_SIZE
];
646 read_buf(g_procs
[id
].m_fin
, buf
);
647 if (strncmp(buf
, "error", 5) == 0) {
648 read_buf(g_procs
[id
].m_fin
, buf
);
649 sscanf(buf
, "%d", &errno
);
651 // On this error, the receiver side returns dummy errno,
652 // use the sender side errno here.
658 sscanf(buf
, "%" PRId64
, &pid
);
663 pid_t
LightProcess::waitpid(pid_t pid
, int *stat_loc
, int options
,
666 // light process is not really there
667 return ::waitpid(pid
, stat_loc
, options
);
671 Lock
lock(g_procs
[id
].m_procMutex
);
673 fprintf(g_procs
[id
].m_fout
, "waitpid\n%" PRId64
" %d %d\n", (int64_t)pid
, options
,
675 fflush(g_procs
[id
].m_fout
);
677 char buf
[BUFFER_SIZE
];
678 read_buf(g_procs
[id
].m_fin
, buf
);
679 if (!buf
[0]) return -1;
682 sscanf(buf
, "%" PRId64
" %d", &ret
, &stat
);
685 read_buf(g_procs
[id
].m_fin
, buf
);
686 sscanf(buf
, "%d", &errno
);
691 pid_t
LightProcess::pcntl_waitpid(pid_t pid
, int *stat_loc
, int options
) {
693 return ::waitpid(pid
, stat_loc
, options
);
697 Lock
lock(g_procs
[id
].m_procMutex
);
699 pid_t p
= ::waitpid(pid
, stat_loc
, options
);
700 if (p
== g_procs
[id
].m_shadowProcess
) {
701 // got the shadow process, wait again
702 p
= ::waitpid(pid
, stat_loc
, options
);
708 void LightProcess::ChangeUser(const string
&username
) {
709 if (username
.empty()) return;
710 for (int i
= 0; i
< g_procsCount
; i
++) {
711 Lock
lock(g_procs
[i
].m_procMutex
);
712 fprintf(g_procs
[i
].m_fout
, "change_user\n%s\n", username
.c_str());
713 fflush(g_procs
[i
].m_fout
);
717 void LightProcess::SetLostChildHandler(const LostChildHandler
& handler
) {
718 s_lostChildHandler
= handler
;
721 ///////////////////////////////////////////////////////////////////////////////