This fixes a bug in PHP/HH's crypt_blowfish implementation that can cause a short...
[hiphop-php.git] / hphp / util / light-process.cpp
bloba295efb56afb7af9f42d32c03b8f142b64aeeab3
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
16 #include "hphp/util/light-process.h"
18 #include <memory>
19 #include <set>
20 #include <string>
21 #include <vector>
23 #include <boost/thread/barrier.hpp>
25 #include <folly/portability/SysMman.h>
26 #include <folly/portability/SysResource.h>
27 #include <folly/portability/Sockets.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
31 #include <afdt.h>
32 #include <grp.h>
33 #include <stdlib.h>
34 #include <folly/portability/Unistd.h>
36 #include <pwd.h>
37 #include <signal.h>
39 #include <folly/Memory.h>
40 #include <folly/String.h>
42 #include "hphp/util/afdt-util.h"
43 #include "hphp/util/compatibility.h"
44 #include "hphp/util/hardware-counter.h"
45 #include "hphp/util/hash.h"
46 #include "hphp/util/hugetlb.h"
47 #include "hphp/util/logger.h"
48 #include "hphp/util/process.h"
49 #include "hphp/util/struct-log.h"
50 #include "hphp/util/timer.h"
51 #include "hphp/util/user-info.h"
53 namespace HPHP {
55 ///////////////////////////////////////////////////////////////////////////////
56 // helper functions
58 bool LightProcess::g_strictUser = false;
60 namespace {
62 __thread LightProcess* tl_proc;
63 bool s_trackProcessTimes = false;
64 Mutex s_mutex;
66 using afdt::send_fd;
67 using afdt::recv_fd;
69 char **build_cstrarr(const std::vector<std::string> &vec) {
70 char **cstrarr = nullptr;
71 int size = vec.size();
72 if (size) {
73 cstrarr = (char **)malloc((size + 1) * sizeof(char *));
74 int j = 0;
75 for (unsigned int i = 0; i < vec.size(); i++, j++) {
76 *(cstrarr + j) = (char *)vec[i].c_str();
78 *(cstrarr + j) = nullptr;
80 return cstrarr;
83 void close_fds(const std::vector<int> &fds) {
84 for (auto fd : fds) {
85 ::close(fd);
89 template<class Head, class... Tail>
90 void lwp_write(int afdt_fd, const Head& h, Tail&&... args) {
91 try {
92 afdt::sendx(afdt_fd, h, std::forward<Tail>(args)...);
93 } catch (const std::runtime_error& ex) {
94 throw Exception("Failed in afdt::sendx: %s", ex.what());
98 template<class Head, class... Tail>
99 void lwp_read(int afdt_fd, Head& h, Tail&... args) {
100 try {
101 afdt::recvx(afdt_fd, h, args...);
102 } catch (const std::runtime_error& ex) {
103 throw Exception("Failed in afdt::recvx: %s", ex.what());
107 // not thread safe - needs a lock when called from the main
108 // process.
109 int popen_impl(const char* cmd, const char* mode, pid_t* out_pid) {
110 int p[2];
111 auto const read = *mode == 'r';
112 if (!read && *mode != 'w') return -1;
114 if (pipe2(p, O_CLOEXEC) < 0) {
115 return -1;
118 auto pid = fork();
119 if (pid < 0) {
120 close(p[0]);
121 close(p[1]);
122 return -1;
124 int child_pipe = read ? 1 : 0;
125 if (pid == 0) {
126 // child
127 mprotect_1g_pages(PROT_READ);
128 // If anything goes wrong, let the OOM killer kill this child process.
129 Process::OOMScoreAdj(1000);
130 // replace stdin or stdout with the appropriate end
131 // of the pipe
132 if (p[child_pipe] == child_pipe) {
133 // pretty unlikely, but if it was we must clear CLOEXEC,
134 // and the only way to do that is to dup it to a new fd,
135 // and then dup2 it back
136 p[child_pipe] = fcntl(child_pipe, F_DUPFD_CLOEXEC, 3);
138 dup2(p[child_pipe], child_pipe);
139 // no need to close p[child_pipe] because of O_CLOEXEC
141 signal(SIGINT, SIG_DFL);
142 sigset_t eset;
143 sigemptyset(&eset);
144 sigprocmask(SIG_SETMASK, &eset, nullptr);
145 execl("/bin/sh", "sh", "-c", cmd, nullptr);
146 Logger::Warning("Failed to exec: `%s'", cmd);
147 _Exit(1);
149 // parent
151 // close the pipe we're not using
152 close(p[child_pipe]);
153 *out_pid = pid;
154 return p[1-child_pipe];
157 int64_t ru2microseconds(const rusage& ru) {
158 int64_t time_us = ru.ru_utime.tv_usec;
159 time_us += ru.ru_stime.tv_usec;
160 int64_t time_s = ru.ru_utime.tv_sec;
161 time_s += ru.ru_stime.tv_sec;
162 return time_us + time_s * 1000000;
166 * Hardware counters can be configured to measure sub-process times,
167 * but note that any given LightProcess will be running jobs for
168 * multiple request threads (each request thread binds to a single
169 * LightProcess, but its a many to one mapping).
171 * This means that between the fork/exec and the waitpid, the LightProcess
172 * could fork/exec more processes for different requests. The solution
173 * is to fork/exec in a thread, and start the hardware counters there.
175 * The hardware counter will then measure time for that thread, and all
176 * its children - which is exactly what we want.
178 * HardwareCounterWrapper & co take care of that.
180 struct HardwareCounterWrapperArg {
181 boost::barrier barrier{2};
182 pthread_t thr;
183 int afdt_fd;
184 pid_t (*func)(int);
185 pid_t pid;
186 int64_t *events;
189 std::map<pid_t, std::unique_ptr<HardwareCounterWrapperArg>> s_pidToHCWMap;
191 void* hardwareCounterWrapper(void* varg) {
192 auto arg = (HardwareCounterWrapperArg*)varg;
194 HardwareCounter::s_counter.getCheck();
195 HardwareCounter::Reset();
196 arg->pid = arg->func(arg->afdt_fd);
197 // tell the main thread that pid has been set.
198 arg->barrier.wait();
199 if (arg->pid < 0) return nullptr;
201 // Wait until the main thread is ready to join us.
202 // Note that even though we have multiple threads running in the LightProcess
203 // now, at the time any of the do_* functions are called, any live threads are
204 // waiting here on this barrier; so there is no problem with fork, malloc,
205 // exec.
206 arg->barrier.wait();
207 HardwareCounter::GetPerfEvents(
208 [](const std::string& event, int64_t value, void* data) {
209 auto events = reinterpret_cast<int64_t*>(data);
210 if (event == "instructions") {
211 events[0] = value;
212 } else if (event == "loads") {
213 events[1] = value;
214 } else if (event == "stores") {
215 events[2] = value;
218 arg->events);
220 return nullptr;
223 void hardwareCounterWrapperHelper(pid_t (*func)(int), int afdt_fd) {
224 if (!s_trackProcessTimes) {
225 func(afdt_fd);
226 return;
229 auto arg = std::make_unique<HardwareCounterWrapperArg>();
230 arg->afdt_fd = afdt_fd;
231 arg->func = func;
232 if (pthread_create(&arg->thr, nullptr, hardwareCounterWrapper, arg.get())) {
233 throw Exception("Failed to pthread_create: %s",
234 folly::errnoStr(errno).c_str());
236 // Wait for the pid to be set. Note that we must not add any code here that
237 // could cause issues for the fork (eg malloc); we should wait on the barrier
238 // immediately after the thread is created.
239 arg->barrier.wait();
240 if (arg->pid > 0) {
241 // successfully forked, so don't join until waitpid.
242 auto& map_entry = s_pidToHCWMap[arg->pid];
243 map_entry = std::move(arg);
244 } else {
245 // fork failed, join now.
246 pthread_join(arg->thr, nullptr);
250 ///////////////////////////////////////////////////////////////////////////////
251 // shadow process tasks
253 pid_t do_popen_helper(int afdt_fd) {
254 std::string mode, buf, cwd;
256 lwp_read(afdt_fd, mode, buf, cwd);
258 std::string old_cwd;
260 if (!cwd.empty()) {
261 old_cwd = Process::GetCurrentDirectory();
262 if (old_cwd != cwd) {
263 if (chdir(cwd.c_str())) {
264 // Ignore chdir failures, because the compiled version might not
265 // have the directory any more.
266 Logger::Warning("Light Process failed chdir to %s.", cwd.c_str());
271 pid_t pid;
273 auto fd = buf.empty() ? -1 :
274 popen_impl(buf.c_str(), mode.c_str(), &pid);
276 if (!old_cwd.empty() && chdir(old_cwd.c_str())) {
277 // only here if we can't change the cwd back
280 if (fd < 0) {
281 Logger::Error("Light process failed popen: %d (%s).", errno,
282 folly::errnoStr(errno).c_str());
283 lwp_write(afdt_fd, "error");
284 } else {
285 lwp_write(afdt_fd, "success", pid);
286 send_fd(afdt_fd, fd);
287 // the fd is now owned by the main process, close our copy
288 close(fd);
290 return pid;
293 void do_popen(int afdt_fd) {
294 hardwareCounterWrapperHelper(do_popen_helper, afdt_fd);
297 pid_t do_fork_and_execve_helper(int afdt_fd) {
298 std::string path, cwd;
299 std::vector<std::string> argv, envp;
300 int flags, pgid;
301 size_t fd_count;
303 lwp_read(afdt_fd, path, cwd, argv, envp, flags, pgid, fd_count);
304 std::map<int, int> fds;
305 for (size_t i = 0; i < fd_count; ++i) {
306 int target_fd;
307 lwp_read(afdt_fd, target_fd);
308 int current_fd = recv_fd(afdt_fd);
309 fds[target_fd] = current_fd;
312 pid_t pid = Process::ForkAndExecve(path, argv, envp, cwd, fds, flags, pgid);
313 if (pid> 0) {
314 lwp_write(afdt_fd, "success", pid);
315 } else {
316 lwp_write(afdt_fd, "error", (int) pid, (int) errno);
319 for (auto [_target, fd] : fds) {
320 close(fd);
322 return pid;
325 void do_fork_and_execve(int afdt_fd) {
326 hardwareCounterWrapperHelper(do_fork_and_execve_helper, afdt_fd);
329 pid_t do_proc_open_helper(int afdt_fd) {
330 std::string cmd, cwd;
331 std::vector<std::string> env;
332 std::vector<int> pvals;
333 lwp_read(afdt_fd, cmd, cwd, env, pvals);
335 std::vector<int> pkeys;
336 for (int i = 0; i < pvals.size(); i++) {
337 int fd = recv_fd(afdt_fd);
338 if (fd < 0) {
339 lwp_write(afdt_fd, "error", (int32_t)EPROTO);
340 close_fds(pkeys);
341 return -1;
343 pkeys.push_back(fd);
346 // indicate error if an empty command was received
347 if (cmd.length() == 0) {
348 lwp_write(afdt_fd, "error", (int32_t)ENOENT);
349 return -1;
352 // now ready to start the child process
353 pid_t child = fork();
354 if (child == 0) {
355 mprotect_1g_pages(PROT_READ);
356 Process::OOMScoreAdj(1000);
357 std::map<int, int> dup_fds;
358 for (int i = 0; i < pvals.size(); i++) {
359 dup_fds[pvals[i]] = pkeys[i];
361 Process::RemapFDsPreExec(dup_fds);
363 if (!cwd.empty() && chdir(cwd.c_str())) {
364 // non-zero for error
365 // chdir failed, the working directory remains unchanged
367 if (!env.empty()) {
368 char **envp = build_cstrarr(env);
369 execle("/bin/sh", "sh", "-c", cmd.c_str(), nullptr, envp);
370 free(envp);
371 } else {
372 execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr);
374 _Exit(HPHP_EXIT_FAILURE);
377 if (child > 0) {
378 // successfully created the child process
379 lwp_write(afdt_fd, "success", child);
380 } else {
381 // failed creating the child process
382 lwp_write(afdt_fd, "error", errno);
385 close_fds(pkeys);
386 return child;
389 void do_proc_open(int afdt_fd) {
390 hardwareCounterWrapperHelper(do_proc_open_helper, afdt_fd);
393 pid_t waited = 0;
395 void kill_handler(int sig) {
396 if (sig == SIGALRM && waited) {
397 kill(waited, SIGKILL);
401 void do_waitpid(int afdt_fd) {
402 pid_t pid = -1;
403 int options = 0;
404 int timeout = 0;
405 lwp_read(afdt_fd, pid, options, timeout);
407 int stat;
408 if (timeout > 0) {
409 waited = pid;
410 signal(SIGALRM, kill_handler);
411 alarm(timeout);
414 rusage ru;
415 int64_t time_us = 0;
416 const auto ret = ::wait4(pid, &stat, options, &ru);
417 alarm(0); // cancel the previous alarm if not triggered yet
418 waited = 0;
419 int64_t events[] = { 0, 0, 0 };
420 if (ret > 0 && s_trackProcessTimes) {
421 time_us = ru2microseconds(ru);
422 auto it = s_pidToHCWMap.find(ret);
423 if (it == s_pidToHCWMap.end()) {
424 throw Exception("pid not in map: %s",
425 folly::errnoStr(errno).c_str());
428 auto hcw = std::move(it->second);
429 s_pidToHCWMap.erase(it);
430 hcw->events = events;
431 hcw->barrier.wait();
432 pthread_join(hcw->thr, nullptr);
435 lwp_write(afdt_fd, ret, errno, stat,
436 time_us, events[0], events[1], events[2]);
439 void do_change_user(int afdt_fd) {
440 std::string uname;
441 lwp_read(afdt_fd, uname);
442 if (!uname.length()) return;
444 StructuredLogEntry* log = nullptr;
445 int err = 0;
446 SCOPE_EXIT {
447 if (log) {
448 log->setInt("errno", err);
449 log->setStr("new_user", uname);
450 StructuredLog::log("hhvm_lightprocess_error", *log);
451 delete log;
454 auto buf = PasswdBuffer{};
455 struct passwd *pw;
456 if (getpwnam_r(uname.c_str(), &buf.ent, buf.data.get(), buf.size, &pw)) {
457 err = errno;
458 log = new StructuredLogEntry();
459 log->setStr("function", "getpwnam_r");
460 if (LightProcess::g_strictUser) {
461 throw std::runtime_error{"getpwnam_r(): " + folly::errnoStr(err)};
463 return;
465 if (!pw) {
466 log = new StructuredLogEntry();
467 log->setStr("function", "getpwnam_r");
468 if (LightProcess::g_strictUser) {
469 throw std::runtime_error{"getpwnam_r(): not found"};
471 return;
473 if (pw->pw_gid) {
474 if (initgroups(pw->pw_name, pw->pw_gid)) {
475 err = errno;
476 log = new StructuredLogEntry();
477 log->setStr("function", "initgroups");
479 if (setgid(pw->pw_gid)) {
480 if (!log) {
481 err = errno;
482 log = new StructuredLogEntry();
483 log->setStr("function", "setgid");
484 if (LightProcess::g_strictUser) {
485 throw std::runtime_error{"setgid():" + folly::errnoStr(err)};
490 if (pw->pw_uid) {
491 if (setuid(pw->pw_uid)) {
492 if (!log) {
493 err = errno;
494 log = new StructuredLogEntry();
495 log->setStr("function", "setuid");
496 if (LightProcess::g_strictUser) {
497 throw std::runtime_error{"setuid():" + folly::errnoStr(err)};
504 ///////////////////////////////////////////////////////////////////////////////
505 // light-weight process
507 std::unique_ptr<LightProcess[]> g_procs;
508 int g_procsCount = 0;
509 bool s_handlerInited = false;
510 LightProcess::LostChildHandler s_lostChildHandler;
511 std::map<FILE*, pid_t> s_popenMap;
513 } // namespace
515 LightProcess::LightProcess() : m_shadowProcess(0), m_afdt_fd(-1) { }
517 LightProcess::~LightProcess() {
520 void LightProcess::SigChldHandler(int /*sig*/, siginfo_t* info, void* /*ctx*/) {
521 if (info->si_code != CLD_EXITED &&
522 info->si_code != CLD_KILLED &&
523 info->si_code != CLD_DUMPED) {
524 return;
526 pid_t pid = info->si_pid;
527 for (int i = 0; i < g_procsCount; ++i) {
528 if (g_procs && g_procs[i].m_shadowProcess == pid) {
529 // The exited process was a light process. Notify the callback, if any.
530 if (s_lostChildHandler) {
531 s_lostChildHandler(pid);
533 break;
538 void LightProcess::Initialize(const std::string &prefix, int count,
539 bool trackProcessTimes,
540 const std::vector<int> &inherited_fds) {
541 s_trackProcessTimes = trackProcessTimes;
543 if (prefix.empty() || count <= 0) {
544 return;
547 if (Available()) {
548 // already initialized
549 return;
552 g_procs.reset(new LightProcess[count]);
553 g_procsCount = count;
555 auto afdt_filename = folly::sformat("{}.{}", prefix, getpid());
557 // remove the possible leftover
558 remove(afdt_filename.c_str());
560 afdt_error_t err = AFDT_ERROR_T_INIT;
561 auto afdt_lid = afdt_listen(afdt_filename.c_str(), &err);
562 if (afdt_lid < 0) {
563 Logger::Error("Unable to afdt_listen to %s: %d %s",
564 afdt_filename.c_str(),
565 errno, folly::errnoStr(errno).c_str());
566 abort();
569 SCOPE_EXIT {
570 ::close(afdt_lid);
571 remove(afdt_filename.c_str());
574 for (int i = 0; i < count; i++) {
575 if (!g_procs[i].initShadow(afdt_lid, afdt_filename, i, inherited_fds)) {
576 for (int j = 0; j < i; j++) {
577 g_procs[j].closeShadow();
579 g_procs.reset();
580 g_procsCount = 0;
581 break;
585 if (!s_handlerInited) {
586 struct sigaction sa = {};
587 sa.sa_sigaction = &LightProcess::SigChldHandler;
588 sa.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;
589 if (sigaction(SIGCHLD, &sa, nullptr) != 0) {
590 Logger::Error("Couldn't install SIGCHLD handler");
591 abort();
593 s_handlerInited = true;
597 bool LightProcess::initShadow(int afdt_lid,
598 const std::string &afdt_filename, int id,
599 const std::vector<int> &inherited_fds) {
600 Lock lock(m_procMutex);
602 pid_t child = fork();
603 if (child == 0) {
604 // child
605 mprotect_1g_pages(PROT_READ);
606 if (s_trackProcessTimes) {
607 HardwareCounter::RecordSubprocessTimes();
609 Logger::ResetPid();
610 pid_t sid = setsid();
611 if (sid < 0) {
612 Logger::Warning("Unable to setsid");
613 _Exit(HPHP_EXIT_FAILURE);
615 afdt_error_t err = AFDT_ERROR_T_INIT;
616 auto afdt_fd = afdt_connect(afdt_filename.c_str(), &err);
617 if (afdt_fd < 0) {
618 Logger::Warning("Unable to afdt_connect, filename %s: %d %s",
619 afdt_filename.c_str(),
620 errno, folly::errnoStr(errno).c_str());
621 _Exit(HPHP_EXIT_FAILURE);
624 // shadow process doesn't use g_procs
625 for (int i = 0; i < id; i++) {
626 g_procs[i].closeFiles();
628 g_procs.reset();
629 g_procsCount = 0;
630 close_fds(inherited_fds);
631 ::close(afdt_lid);
632 // Tell the OOM killer never to kill a light process. Killing it will cause
633 // the entire server to exit, and won't free much memory anyway.
634 Process::OOMScoreAdj(-1000);
635 runShadow(afdt_fd);
636 } else if (child < 0) {
637 // failed
638 Logger::Warning("Unable to fork lightly: %d %s", errno,
639 folly::errnoStr(errno).c_str());
640 return false;
641 } else {
642 // parent
643 m_shadowProcess = child;
645 sockaddr addr;
646 socklen_t addrlen = sizeof(addr);
647 m_afdt_fd = accept(afdt_lid, &addr, &addrlen);
648 if (m_afdt_fd < 0) {
649 Logger::Warning("Unable to establish afdt connection: %d %s",
650 errno, folly::errnoStr(errno).c_str());
651 closeShadow();
652 return false;
655 return true;
658 void LightProcess::runShadow(int afdt_fd) {
659 std::string buf;
661 pollfd pfd[1];
662 pfd[0].fd = afdt_fd;
663 pfd[0].events = POLLIN;
664 try {
665 while (true) {
666 int ret = poll(pfd, 1, -1);
667 if (ret < 0 && errno == EINTR) {
668 continue;
670 if (pfd[0].revents & POLLHUP) {
671 // no more command can come in
672 Logger::Error("Lost parent, LightProcess exiting");
673 break;
675 if (pfd[0].revents & POLLIN) {
676 lwp_read(afdt_fd, buf);
677 if (buf == "exit") {
678 Logger::Verbose("LightProcess exiting upon request");
679 break;
680 } else if (buf == "popen") {
681 do_popen(afdt_fd);
682 } else if (buf == "proc_open") {
683 do_proc_open(afdt_fd);
684 } else if (buf == "execve") {
685 do_fork_and_execve(afdt_fd);
686 } else if (buf == "waitpid") {
687 do_waitpid(afdt_fd);
688 } else if (buf == "change_user") {
689 do_change_user(afdt_fd);
690 } else if (buf[0]) {
691 Logger::Info("LightProcess got invalid command: %.20s", buf.c_str());
695 } catch (const std::exception& e) {
696 Logger::Error("LightProcess exiting due to exception: %s", e.what());
697 } catch (...) {
698 Logger::Error("LightProcess exiting due to unknown exception");
701 ::close(afdt_fd);
702 _Exit(0);
705 namespace {
707 int GetId() {
708 return hash_int64((long)pthread_self()) % g_procsCount;
711 NEVER_INLINE
712 void handleException(const char* call) {
713 try {
714 throw;
715 } catch (const std::exception& e) {
716 Logger::Error("LightProcess::%s failed due to exception: %s",
717 call, e.what());
718 } catch (...) {
719 Logger::Error("LightProcess::%s failed due to unknown exception",
720 call);
724 template <class R, class F1>
725 R runLight(const char* call, F1 body, R failureResult) {
726 try {
727 auto proc = tl_proc ? tl_proc : &g_procs[GetId()];
728 Lock lock(proc->mutex());
730 return body(proc);
731 } catch (...) {
732 handleException(call);
734 return failureResult;
739 void LightProcess::Close() {
740 std::unique_ptr<LightProcess[]> procs;
741 procs.swap(g_procs);
742 int count = g_procsCount;
743 g_procs.reset();
744 g_procsCount = 0;
746 for (int i = 0; i < count; i++) {
747 procs[i].closeShadow();
751 void LightProcess::closeShadow() {
752 Lock lock(m_procMutex);
753 if (m_shadowProcess) {
754 try {
755 lwp_write(m_afdt_fd, "exit");
756 } catch (...) {
757 handleException("closeShadow");
759 // removes the "zombie" process, so not to interfere with later waits
760 while (true) {
761 auto r = ::waitpid(m_shadowProcess, nullptr, 0);
762 // retry on EINTR
763 if (r != -1 || errno != EINTR) {
764 break;
768 m_shadowProcess = 0;
770 closeFiles();
773 void LightProcess::closeFiles() {
774 if (m_afdt_fd >= 0) {
775 ::close(m_afdt_fd);
776 m_afdt_fd = -1;
780 bool LightProcess::Available() {
781 if (tl_proc) return true;
782 return g_procsCount > 0;
785 FILE *LightProcess::popen(const char *cmd, const char *type,
786 const char *cwd /* = NULL */) {
787 if (!Available()) {
788 // fallback to normal popen
789 Logger::Verbose("Light-weight fork not available; "
790 "use the heavy one instead.");
791 } else {
792 FILE *f = LightPopenImpl(cmd, type, cwd);
793 if (f) {
794 return f;
796 if (tl_proc) {
797 Logger::Verbose("Light-weight fork failed in remote CLI mode.");
798 return nullptr;
800 Logger::Verbose("Light-weight fork failed; use the heavy one instead.");
802 return HeavyPopenImpl(cmd, type, cwd);
805 FILE *LightProcess::HeavyPopenImpl(const char *cmd, const char *type,
806 const char *cwd) {
807 int fd = -1;
808 pid_t pid;
809 Lock lock(s_mutex);
810 if (cwd && *cwd) {
811 auto old_cwd = Process::GetCurrentDirectory();
812 if (old_cwd != cwd) {
813 if (chdir(cwd)) {
814 Logger::Warning("Failed to chdir to %s.", cwd);
816 fd = popen_impl(cmd, type, &pid);
817 if (chdir(old_cwd.c_str())) {
818 // error occurred changing cwd back
820 if (fd < 0) return nullptr;
823 if (fd < 0) {
824 fd = popen_impl(cmd, type, &pid);
826 if (fd < 0) return nullptr;
827 auto f = fdopen(fd, type);
828 if (f) {
829 s_popenMap[f] = pid;
831 return f;
834 FILE *LightProcess::LightPopenImpl(const char *cmd, const char *type,
835 const char *cwd) {
836 return runLight("popen", [&] (LightProcess* proc) -> FILE* {
837 auto afdt_fd = proc->m_afdt_fd;
838 lwp_write(afdt_fd, "popen", type, cmd, cwd ? cwd : "");
840 std::string buf;
841 lwp_read(afdt_fd, buf);
842 if (buf == "error") {
843 return nullptr;
846 pid_t pid;
847 lwp_read(afdt_fd, pid);
848 assert(pid);
849 int fd = recv_fd(afdt_fd);
850 if (fd < 0) {
851 Logger::Error("Light process failed to send the file descriptor.");
852 return nullptr;
854 FILE *f = fdopen(fd, type);
855 if (f) {
856 proc->m_popenMap[f] = pid;
858 return f;
859 }, static_cast<FILE*>(nullptr));
862 int LightProcess::pclose(FILE *f) {
863 pid_t pid;
864 if (!Available()) {
865 Lock lock(s_mutex);
866 auto it = s_popenMap.find(f);
867 if (it == s_popenMap.end()) return -1;
868 pid = it->second;
869 s_popenMap.erase(it);
870 } else {
871 auto proc = [] {
872 if (tl_proc) return tl_proc;
873 return &g_procs[GetId()];
874 }();
875 Lock lock(proc->m_procMutex);
877 auto it = proc->m_popenMap.find(f);
878 if (it == proc->m_popenMap.end()) return -1;
879 pid = it->second;
880 proc->m_popenMap.erase(it);
883 fclose(f);
884 int status;
885 if (LightProcess::waitpid(pid, &status, 0, 0) < 0) return -1;
886 return status;
889 pid_t LightProcess::ForkAndExecve(const std::string& path,
890 const std::vector<std::string>& argv,
891 const std::vector<std::string>& envp,
892 const std::string& cwd,
893 const std::map<int, int>& fds,
894 int options,
895 pid_t pgid) {
896 return runLight("execve", [&] (LightProcess* proc) -> pid_t {
897 auto fin = proc->m_afdt_fd;
898 auto fout = proc->m_afdt_fd;
900 lwp_write(fout, "execve", path, cwd, argv, envp, options, pgid, (size_t) fds.size());
901 for (const auto& [target_fd, current_fd]: fds) {
902 lwp_write(fout, target_fd);
903 send_fd(fout, current_fd);
906 std::string buf;
907 lwp_read(fin, buf);
908 if (buf == "success") {
909 pid_t pid;
910 lwp_read(fin, pid);
911 return pid;
913 assert(buf == "error");
915 int ret;
916 int saved_errno;
917 lwp_read(fin, ret, saved_errno);
918 errno = saved_errno;
919 return (pid_t) ret;
920 }, static_cast<pid_t>(-1));
923 pid_t LightProcess::proc_open(const char *cmd, const std::vector<int> &created,
924 const std::vector<int> &desired,
925 const char *cwd,
926 const std::vector<std::string> &env) {
927 always_assert(Available());
928 always_assert(created.size() == desired.size());
930 return runLight("proc_open", [&] (LightProcess* proc) -> pid_t {
931 auto fout = proc->m_afdt_fd;
932 lwp_write(fout, "proc_open", cmd, cwd ? cwd : "",
933 env, desired);
935 bool error_send = false;
936 int save_errno = 0;
937 for (auto cfd : created) {
938 if (!send_fd(proc->m_afdt_fd, cfd)) {
939 error_send = true;
940 save_errno = errno;
941 break;
945 std::string buf;
946 auto fin = proc->m_afdt_fd;
947 lwp_read(fin, buf);
948 if (buf == "error") {
949 lwp_read(fin, errno);
950 if (error_send) {
951 // On this error, the receiver side returns dummy errno,
952 // use the sender side errno here.
953 errno = save_errno;
955 return -1;
957 always_assert_flog(buf == "success",
958 "Unexpected message from light process: `{}'", buf);
959 pid_t pid = -1;
960 lwp_read(fin, pid);
961 always_assert(pid);
962 return pid;
963 }, static_cast<pid_t>(-1));
966 pid_t LightProcess::waitpid(pid_t pid, int *stat_loc, int options,
967 int timeout) {
968 if (!Available()) {
969 // light process is not really there
970 rusage ru;
971 const auto ret = wait4(pid, stat_loc, options, &ru);
972 if (ret > 0 && s_trackProcessTimes) {
973 s_extra_request_nanoseconds += ru2microseconds(ru) * 1000;
975 return ret;
978 return runLight("waitpid", [&] (LightProcess* proc) -> pid_t {
979 lwp_write(proc->m_afdt_fd, "waitpid", pid, options, timeout);
981 pid_t ret;
982 int stat;
983 int err;
984 int64_t time_us, events[3];
985 lwp_read(proc->m_afdt_fd, ret, err, stat,
986 time_us, events[0], events[1], events[2]);
988 *stat_loc = stat;
989 if (ret < 0) {
990 errno = err;
991 } else if (s_trackProcessTimes) {
992 s_extra_request_nanoseconds += time_us * 1000;
993 HardwareCounter::IncInstructionCount(events[0]);
994 HardwareCounter::IncLoadCount(events[1]);
995 HardwareCounter::IncStoreCount(events[2]);
998 return ret;
999 }, static_cast<pid_t>(-1));
1002 void LightProcess::ChangeUser(int afdt, const std::string& username) {
1003 if (!username.empty()) lwp_write(afdt, "change_user", username);
1006 void LightProcess::ChangeUser(const std::string& username) {
1007 if (username.empty()) return;
1008 for (int i = 0; i < g_procsCount; i++) {
1009 Lock lock(g_procs[i].m_procMutex);
1010 lwp_write(g_procs[i].m_afdt_fd, "change_user", username);
1014 void LightProcess::SetLostChildHandler(const LostChildHandler& handler) {
1015 s_lostChildHandler = handler;
1018 std::unique_ptr<LightProcess> LightProcess::setThreadLocalAfdtOverride(
1019 std::unique_ptr<LightProcess> p
1021 auto ret = std::unique_ptr<LightProcess>(tl_proc);
1022 tl_proc = p.release();
1023 return ret;
1026 std::unique_ptr<LightProcess> LightProcess::setThreadLocalAfdtOverride(int fd) {
1027 auto ret = std::unique_ptr<LightProcess>(tl_proc);
1028 tl_proc = new LightProcess;
1029 tl_proc->m_afdt_fd = fd;
1030 return ret;
1033 int LightProcess::createDelegate() {
1034 int pair[2];
1035 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair)) {
1036 Logger::Warning("Unable to create a unix socket pair: %s",
1037 folly::errnoStr(errno).c_str());
1038 return -1;
1041 pid_t child = fork();
1043 if (child < 0) {
1044 Logger::Warning("Unable to fork delegate process: %s",
1045 folly::errnoStr(errno).c_str());
1046 close(pair[0]);
1047 close(pair[1]);
1048 return -1;
1051 if (child == 0) {
1052 // child
1053 fclose(stdin);
1054 fclose(stdout);
1055 fclose(stderr);
1057 mprotect_1g_pages(PROT_READ);
1058 if (s_trackProcessTimes) {
1059 HardwareCounter::RecordSubprocessTimes();
1061 Logger::ResetPid();
1062 pid_t sid = setsid();
1063 if (sid < 0) {
1064 Logger::Warning("Unable to setsid");
1065 _Exit(HPHP_EXIT_FAILURE);
1068 close(pair[0]);
1069 #ifdef __APPLE__
1071 int newfd = dup2(pair[1], 0);
1072 always_assert(newfd == 0);
1074 close(pair[1]);
1075 pair[1] = 0;
1076 #endif
1077 runShadow(pair[1]);
1080 always_assert(child > 0);
1081 close(pair[1]);
1082 return pair[0];
1085 ///////////////////////////////////////////////////////////////////////////////