make #includes consistent
[hiphop-php.git] / hphp / util / light_process.cpp
blob4935761407138fe470854fccf87a369597b12a7d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
19 #include "util.h"
20 #include "hphp/util/logger.h"
22 #include <afdt.h>
23 #include <string>
24 #include <vector>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include "sys/wait.h"
28 #include <poll.h>
29 #include <pwd.h>
30 #include <signal.h>
31 #include <boost/scoped_array.hpp>
33 namespace HPHP {
35 ///////////////////////////////////////////////////////////////////////////////
36 // helper functions
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)) {
43 buf[0] = '\0';
44 return;
46 // get rid of '\n'
47 buf[strlen(buf) - 1] = '\0';
50 static bool send_fd(int afdt_fd, int fd) {
51 afdt_error_t err;
52 errno = 0;
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
56 errno = EPROTO;
58 return ret >= 0;
61 static int recv_fd(int afdt_fd) {
62 int fd;
63 afdt_error_t err;
64 uint8_t afdt_buf[AFDT_MSGLEN];
65 uint32_t afdt_len;
66 errno = 0;
67 if (afdt_recv_fd_msg(afdt_fd, afdt_buf, &afdt_len, &fd, &err) < 0) {
68 if (errno == 0) {
69 // Set non-empty errno if afdt_send_fd_msg doesn't set one on error
70 errno = EPROTO;
72 return -1;
74 return fd;
77 static char **build_envp(const vector<string> &env) {
78 char **envp = nullptr;
79 int size = env.size();
80 if (size) {
81 envp = (char **)malloc((size + 1) * sizeof(char *));
82 int j = 0;
83 for (unsigned int i = 0; i < env.size(); i++, j++) {
84 *(envp + j) = (char *)env[i].c_str();
86 *(envp + j) = nullptr;
88 return envp;
91 static void close_fds(const vector<int> &fds) {
92 for (unsigned int i = 0; i < fds.size(); i++) {
93 ::close(fds[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');
107 read_buf(fin, buf);
109 string old_cwd = Process::GetCurrentDirectory();
110 read_buf(fin, cwd);
111 if (old_cwd != cwd) {
112 if (chdir(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
125 if (f == nullptr) {
126 Logger::Error("Light process failed popen: %d (%s).", errno,
127 strerror(errno));
128 fprintf(fout, "error\n");
129 fflush(fout);
130 } else {
131 fprintf(fout, "success\n%" PRId64 "\n", (int64_t)f);
132 fflush(fout);
133 int fd = fileno(f);
134 send_fd(afdt_fd, fd);
138 static void do_pclose(FILE *fin, FILE *fout) {
139 char buf[BUFFER_SIZE];
141 int64_t fptr = 0;
142 read_buf(fin, buf);
143 sscanf(buf, "%" PRId64, &fptr);
144 FILE *f = (FILE *)fptr;
145 int ret = ::pclose(f);
147 fprintf(fout, "%d\n", ret);
148 if (ret < 0) {
149 fprintf(fout, "%d\n", errno);
151 fflush(fout);
154 static void do_proc_open(FILE *fin, FILE *fout, int afdt_fd) {
155 char cmd[BUFFER_SIZE];
156 read_buf(fin, cmd);
157 if (strlen(cmd) == 0) {
158 fprintf(fout, "error\n%d\n", ENOENT);
159 fflush(fout);
160 return;
163 char cwd[BUFFER_SIZE];
164 read_buf(fin, cwd);
166 char buf[BUFFER_SIZE];
167 int env_size = 0;
168 vector<string> env;
169 read_buf(fin, buf);
170 sscanf(buf, "%d", &env_size);
171 for (int i = 0; i < env_size; i++) {
172 read_buf(fin, buf);
173 env.push_back(buf);
176 int pipe_size = 0;
177 read_buf(fin, buf);
178 sscanf(buf, "%d", &pipe_size);
179 vector<int> pvals;
180 for (int i = 0; i < pipe_size; i++) {
181 int fd_value;
182 read_buf(fin, buf);
183 sscanf(buf, "%d", &fd_value);
184 pvals.push_back(fd_value);
187 vector<int> pkeys;
188 for (int i = 0; i < pipe_size; i++) {
189 int fd = recv_fd(afdt_fd);
190 if (fd < 0) {
191 fprintf(fout, "error\n%d\n", EPROTO);
192 fflush(fout);
193 close_fds(pkeys);
194 return;
196 pkeys.push_back(fd);
199 // now ready to start the child process
200 pid_t child = fork();
201 if (child == 0) {
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
209 if (!env.empty()) {
210 char **envp = build_envp(env);
211 execle("/bin/sh", "sh", "-c", cmd, nullptr, envp);
212 free(envp);
213 } else {
214 execl("/bin/sh", "sh", "-c", cmd, nullptr);
216 _exit(127);
217 } else if (child > 0) {
218 // successfully created the child process
219 fprintf(fout, "%" PRId64 "\n", (int64_t)child);
220 fflush(fout);
221 } else {
222 // failed creating the child process
223 fprintf(fout, "error\n%d\n", errno);
224 fflush(fout);
227 close_fds(pkeys);
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];
240 read_buf(fin, buf);
241 int64_t p = -1;
242 int options = 0;
243 int timeout = 0;
244 sscanf(buf, "%" PRId64 " %d %d", &p, &options, &timeout);
245 pid_t pid = (pid_t)p;
246 int stat;
247 if (timeout > 0) {
248 waited = pid;
249 signal(SIGALRM, kill_handler);
250 alarm(timeout);
252 pid_t ret = ::waitpid(pid, &stat, options);
253 alarm(0); // cancel the previous alarm if not triggered yet
254 waited = 0;
255 fprintf(fout, "%" PRId64 " %d\n", (int64_t)ret, stat);
256 if (ret < 0) {
257 fprintf(fout, "%d\n", errno);
259 fflush(fout);
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);
267 if (pw) {
268 if (pw->pw_gid) {
269 setgid(pw->pw_gid);
271 if (pw->pw_uid) {
272 setuid(pw->pw_uid);
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),
288 m_afdt_lfd(-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) {
297 return;
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);
306 break;
311 void LightProcess::Initialize(const std::string &prefix, int count,
312 const vector<int> &inherited_fds) {
313 if (prefix.empty() || count <= 0) {
314 return;
317 if (Available()) {
318 // already initialized
319 return;
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();
330 g_procs.reset();
331 g_procsCount = 0;
332 break;
336 if (!s_handlerInited) {
337 struct sigaction sa;
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");
343 abort();
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());
360 afdt_error_t err;
361 m_afdt_lfd = afdt_listen(m_afdtFilename.c_str(), &err);
362 if (m_afdt_lfd < 0) {
363 Logger::Warning("Unable to afdt_listen");
364 return false;
367 CPipe p1, p2;
368 if (!p1.open() || !p2.open()) {
369 Logger::Warning("Unable to create pipe: %d %s", errno,
370 Util::safe_strerror(errno).c_str());
371 return false;
374 pid_t child = fork();
375 if (child == 0) {
376 // child
377 pid_t sid = setsid();
378 if (sid < 0) {
379 Logger::Warning("Unable to setsid");
380 exit(-1);
382 m_afdt_fd =
383 afdt_connect(m_afdtFilename.c_str(), &err);
384 if (m_afdt_fd < 0) {
385 Logger::Warning("Unable to afdt_connect");
386 exit(-1);
388 int fd1 = p1.detachOut();
389 int fd2 = p2.detachIn();
390 p1.close();
391 p2.close();
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);
399 ::close(m_afdt_lfd);
401 runShadow(fd1, fd2);
402 } else if (child < 0) {
403 // failed
404 Logger::Warning("Unable to fork lightly: %d %s", errno,
405 Util::safe_strerror(errno).c_str());
406 return false;
407 } else {
408 // parent
409 m_fin = fdopen(p2.detachOut(), "r");
410 m_fout = fdopen(p1.detachIn(), "w");
411 m_shadowProcess = child;
413 sockaddr addr;
414 socklen_t addrlen;
415 m_afdt_fd = accept(m_afdt_lfd, &addr, &addrlen);
416 if (m_afdt_fd < 0) {
417 Logger::Warning("Unable to establish afdt connection");
418 closeShadow();
419 return false;
422 return true;
425 void LightProcess::Close() {
426 for (int i = 0; i < g_procsCount; i++) {
427 g_procs[i].closeShadow();
429 g_procs.reset();
430 g_procsCount = 0;
433 void LightProcess::closeShadow() {
434 Lock lock(m_procMutex);
435 if (m_shadowProcess) {
436 fprintf(m_fout, "exit\n");
437 fflush(m_fout);
438 fclose(m_fin);
439 fclose(m_fout);
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) {
447 ::close(m_afdt_fd);
448 m_afdt_fd = -1;
450 m_shadowProcess = 0;
453 void LightProcess::closeFiles() {
454 fclose(m_fin);
455 fclose(m_fout);
456 ::close(m_afdt_fd);
457 ::close(m_afdt_lfd);
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];
470 pollfd pfd[1];
471 pfd[0].fd = fdin;
472 pfd[0].events = POLLIN;
473 while (true) {
474 int ret = poll(pfd, 1, -1);
475 if (ret < 0 && errno == EINTR) {
476 continue;
478 if (pfd[0].revents & POLLHUP) {
479 // no more command can come in
480 Logger::Error("Lost parent, LightProcess exiting");
481 break;
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");
487 break;
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);
502 fclose(fin);
503 fclose(fout);
504 ::close(m_afdt_fd);
505 remove(m_afdtFilename.c_str());
506 _Exit(0);
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 */) {
515 if (!Available()) {
516 // fallback to normal popen
517 Logger::Verbose("Light-weight fork not available; "
518 "use the heavy one instead.");
519 } else {
520 FILE *f = LightPopenImpl(cmd, type, cwd);
521 if (f) {
522 return f;
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,
530 const char *cwd) {
531 if (cwd && *cwd) {
532 string old_cwd = Process::GetCurrentDirectory();
533 if (old_cwd != cwd) {
534 Lock lock(s_mutex);
535 if (chdir(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
542 return f;
545 return ::popen(cmd, type);
548 FILE *LightProcess::LightPopenImpl(const char *cmd, const char *type,
549 const char *cwd) {
550 int id = GetId();
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) {
559 return nullptr;
562 int64_t fptr = 0;
563 read_buf(g_procs[id].m_fin, buf);
564 sscanf(buf, "%" PRId64, &fptr);
565 if (!fptr) {
566 Logger::Error("Light process failed to return the file pointer.");
567 return nullptr;
570 int fd = recv_fd(g_procs[id].m_afdt_fd);
571 if (fd < 0) {
572 Logger::Error("Light process failed to send the file descriptor.");
573 return nullptr;
575 FILE *f = fdopen(fd, type);
576 g_procs[id].m_popenMap[(int64_t)f] = fptr;
578 return f;
581 int LightProcess::pclose(FILE *f) {
582 if (!Available()) {
583 return ::pclose(f);
586 int id = GetId();
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
592 return ::pclose(f);
595 int64_t f2 = it->second;
596 g_procs[id].m_popenMap.erase((int64_t)f);
597 fclose(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);
603 int ret = -1;
604 sscanf(buf, "%d", &ret);
605 if (ret < 0) {
606 read_buf(g_procs[id].m_fin, buf);
607 sscanf(buf, "%d", &errno);
609 return ret;
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) {
615 int id = GetId();
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");
622 return -1;
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;
636 int save_errno = 0;
637 for (unsigned int i = 0; i < created.size(); i++) {
638 if (!send_fd(g_procs[id].m_afdt_fd, created[i])) {
639 error_send = true;
640 save_errno = errno;
641 break;
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);
650 if (error_send) {
651 // On this error, the receiver side returns dummy errno,
652 // use the sender side errno here.
653 errno = save_errno;
655 return -1;
657 int64_t pid = -1;
658 sscanf(buf, "%" PRId64, &pid);
659 assert(pid);
660 return (pid_t)pid;
663 pid_t LightProcess::waitpid(pid_t pid, int *stat_loc, int options,
664 int timeout) {
665 if (!Available()) {
666 // light process is not really there
667 return ::waitpid(pid, stat_loc, options);
670 int id = GetId();
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,
674 timeout);
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;
680 int64_t ret;
681 int stat;
682 sscanf(buf, "%" PRId64 " %d", &ret, &stat);
683 *stat_loc = stat;
684 if (ret < 0) {
685 read_buf(g_procs[id].m_fin, buf);
686 sscanf(buf, "%d", &errno);
688 return (pid_t)ret;
691 pid_t LightProcess::pcntl_waitpid(pid_t pid, int *stat_loc, int options) {
692 if (!Available()) {
693 return ::waitpid(pid, stat_loc, options);
696 int id = GetId();
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);
705 return p;
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 ///////////////////////////////////////////////////////////////////////////////