2 +----------------------------------------------------------------------+
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"
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>
34 #include <folly/portability/Unistd.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"
55 ///////////////////////////////////////////////////////////////////////////////
58 bool LightProcess::g_strictUser
= false;
62 __thread LightProcess
* tl_proc
;
63 bool s_trackProcessTimes
= false;
69 char **build_cstrarr(const std::vector
<std::string
> &vec
) {
70 char **cstrarr
= nullptr;
71 int size
= vec
.size();
73 cstrarr
= (char **)malloc((size
+ 1) * sizeof(char *));
75 for (unsigned int i
= 0; i
< vec
.size(); i
++, j
++) {
76 *(cstrarr
+ j
) = (char *)vec
[i
].c_str();
78 *(cstrarr
+ j
) = nullptr;
83 void close_fds(const std::vector
<int> &fds
) {
89 template<class Head
, class... Tail
>
90 void lwp_write(int afdt_fd
, const Head
& h
, Tail
&&... args
) {
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
) {
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
109 int popen_impl(const char* cmd
, const char* mode
, pid_t
* out_pid
) {
111 auto const read
= *mode
== 'r';
112 if (!read
&& *mode
!= 'w') return -1;
114 if (pipe2(p
, O_CLOEXEC
) < 0) {
124 int child_pipe
= read
? 1 : 0;
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
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
);
144 sigprocmask(SIG_SETMASK
, &eset
, nullptr);
145 execl("/bin/sh", "sh", "-c", cmd
, nullptr);
146 Logger::Warning("Failed to exec: `%s'", cmd
);
151 // close the pipe we're not using
152 close(p
[child_pipe
]);
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};
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.
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,
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") {
212 } else if (event
== "loads") {
214 } else if (event
== "stores") {
223 void hardwareCounterWrapperHelper(pid_t (*func
)(int), int afdt_fd
) {
224 if (!s_trackProcessTimes
) {
229 auto arg
= std::make_unique
<HardwareCounterWrapperArg
>();
230 arg
->afdt_fd
= afdt_fd
;
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.
241 // successfully forked, so don't join until waitpid.
242 auto& map_entry
= s_pidToHCWMap
[arg
->pid
];
243 map_entry
= std::move(arg
);
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
);
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());
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
281 Logger::Error("Light process failed popen: %d (%s).", errno
,
282 folly::errnoStr(errno
).c_str());
283 lwp_write(afdt_fd
, "error");
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
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
;
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
) {
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
);
314 lwp_write(afdt_fd
, "success", pid
);
316 lwp_write(afdt_fd
, "error", (int) pid
, (int) errno
);
319 for (auto [_target
, fd
] : fds
) {
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
);
339 lwp_write(afdt_fd
, "error", (int32_t)EPROTO
);
346 // indicate error if an empty command was received
347 if (cmd
.length() == 0) {
348 lwp_write(afdt_fd
, "error", (int32_t)ENOENT
);
352 // now ready to start the child process
353 pid_t child
= fork();
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
368 char **envp
= build_cstrarr(env
);
369 execle("/bin/sh", "sh", "-c", cmd
.c_str(), nullptr, envp
);
372 execl("/bin/sh", "sh", "-c", cmd
.c_str(), nullptr);
374 _Exit(HPHP_EXIT_FAILURE
);
378 // successfully created the child process
379 lwp_write(afdt_fd
, "success", child
);
381 // failed creating the child process
382 lwp_write(afdt_fd
, "error", errno
);
389 void do_proc_open(int afdt_fd
) {
390 hardwareCounterWrapperHelper(do_proc_open_helper
, afdt_fd
);
395 void kill_handler(int sig
) {
396 if (sig
== SIGALRM
&& waited
) {
397 kill(waited
, SIGKILL
);
401 void do_waitpid(int afdt_fd
) {
405 lwp_read(afdt_fd
, pid
, options
, timeout
);
410 signal(SIGALRM
, kill_handler
);
416 const auto ret
= ::wait4(pid
, &stat
, options
, &ru
);
417 alarm(0); // cancel the previous alarm if not triggered yet
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
;
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
) {
441 lwp_read(afdt_fd
, uname
);
442 if (!uname
.length()) return;
444 StructuredLogEntry
* log
= nullptr;
448 log
->setInt("errno", err
);
449 log
->setStr("new_user", uname
);
450 StructuredLog::log("hhvm_lightprocess_error", *log
);
454 auto buf
= PasswdBuffer
{};
456 if (getpwnam_r(uname
.c_str(), &buf
.ent
, buf
.data
.get(), buf
.size
, &pw
)) {
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
)};
466 log
= new StructuredLogEntry();
467 log
->setStr("function", "getpwnam_r");
468 if (LightProcess::g_strictUser
) {
469 throw std::runtime_error
{"getpwnam_r(): not found"};
474 if (initgroups(pw
->pw_name
, pw
->pw_gid
)) {
476 log
= new StructuredLogEntry();
477 log
->setStr("function", "initgroups");
479 if (setgid(pw
->pw_gid
)) {
482 log
= new StructuredLogEntry();
483 log
->setStr("function", "setgid");
484 if (LightProcess::g_strictUser
) {
485 throw std::runtime_error
{"setgid():" + folly::errnoStr(err
)};
491 if (setuid(pw
->pw_uid
)) {
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
;
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
) {
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
);
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) {
548 // already initialized
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
);
563 Logger::Error("Unable to afdt_listen to %s: %d %s",
564 afdt_filename
.c_str(),
565 errno
, folly::errnoStr(errno
).c_str());
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();
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");
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();
605 mprotect_1g_pages(PROT_READ
);
606 if (s_trackProcessTimes
) {
607 HardwareCounter::RecordSubprocessTimes();
610 pid_t sid
= setsid();
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
);
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();
630 close_fds(inherited_fds
);
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);
636 } else if (child
< 0) {
638 Logger::Warning("Unable to fork lightly: %d %s", errno
,
639 folly::errnoStr(errno
).c_str());
643 m_shadowProcess
= child
;
646 socklen_t addrlen
= sizeof(addr
);
647 m_afdt_fd
= accept(afdt_lid
, &addr
, &addrlen
);
649 Logger::Warning("Unable to establish afdt connection: %d %s",
650 errno
, folly::errnoStr(errno
).c_str());
658 void LightProcess::runShadow(int afdt_fd
) {
663 pfd
[0].events
= POLLIN
;
666 int ret
= poll(pfd
, 1, -1);
667 if (ret
< 0 && errno
== EINTR
) {
670 if (pfd
[0].revents
& POLLHUP
) {
671 // no more command can come in
672 Logger::Error("Lost parent, LightProcess exiting");
675 if (pfd
[0].revents
& POLLIN
) {
676 lwp_read(afdt_fd
, buf
);
678 Logger::Verbose("LightProcess exiting upon request");
680 } else if (buf
== "popen") {
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") {
688 } else if (buf
== "change_user") {
689 do_change_user(afdt_fd
);
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());
698 Logger::Error("LightProcess exiting due to unknown exception");
708 return hash_int64((long)pthread_self()) % g_procsCount
;
712 void handleException(const char* call
) {
715 } catch (const std::exception
& e
) {
716 Logger::Error("LightProcess::%s failed due to exception: %s",
719 Logger::Error("LightProcess::%s failed due to unknown exception",
724 template <class R
, class F1
>
725 R
runLight(const char* call
, F1 body
, R failureResult
) {
727 auto proc
= tl_proc
? tl_proc
: &g_procs
[GetId()];
728 Lock
lock(proc
->mutex());
732 handleException(call
);
734 return failureResult
;
739 void LightProcess::Close() {
740 std::unique_ptr
<LightProcess
[]> procs
;
742 int count
= g_procsCount
;
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
) {
755 lwp_write(m_afdt_fd
, "exit");
757 handleException("closeShadow");
759 // removes the "zombie" process, so not to interfere with later waits
761 auto r
= ::waitpid(m_shadowProcess
, nullptr, 0);
763 if (r
!= -1 || errno
!= EINTR
) {
773 void LightProcess::closeFiles() {
774 if (m_afdt_fd
>= 0) {
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 */) {
788 // fallback to normal popen
789 Logger::Verbose("Light-weight fork not available; "
790 "use the heavy one instead.");
792 FILE *f
= LightPopenImpl(cmd
, type
, cwd
);
797 Logger::Verbose("Light-weight fork failed in remote CLI mode.");
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
,
811 auto old_cwd
= Process::GetCurrentDirectory();
812 if (old_cwd
!= 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;
824 fd
= popen_impl(cmd
, type
, &pid
);
826 if (fd
< 0) return nullptr;
827 auto f
= fdopen(fd
, type
);
834 FILE *LightProcess::LightPopenImpl(const char *cmd
, const char *type
,
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
: "");
841 lwp_read(afdt_fd
, buf
);
842 if (buf
== "error") {
847 lwp_read(afdt_fd
, pid
);
849 int fd
= recv_fd(afdt_fd
);
851 Logger::Error("Light process failed to send the file descriptor.");
854 FILE *f
= fdopen(fd
, type
);
856 proc
->m_popenMap
[f
] = pid
;
859 }, static_cast<FILE*>(nullptr));
862 int LightProcess::pclose(FILE *f
) {
866 auto it
= s_popenMap
.find(f
);
867 if (it
== s_popenMap
.end()) return -1;
869 s_popenMap
.erase(it
);
872 if (tl_proc
) return tl_proc
;
873 return &g_procs
[GetId()];
875 Lock
lock(proc
->m_procMutex
);
877 auto it
= proc
->m_popenMap
.find(f
);
878 if (it
== proc
->m_popenMap
.end()) return -1;
880 proc
->m_popenMap
.erase(it
);
885 if (LightProcess::waitpid(pid
, &status
, 0, 0) < 0) return -1;
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
,
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
);
908 if (buf
== "success") {
913 assert(buf
== "error");
917 lwp_read(fin
, ret
, saved_errno
);
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
,
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
: "",
935 bool error_send
= false;
937 for (auto cfd
: created
) {
938 if (!send_fd(proc
->m_afdt_fd
, cfd
)) {
946 auto fin
= proc
->m_afdt_fd
;
948 if (buf
== "error") {
949 lwp_read(fin
, errno
);
951 // On this error, the receiver side returns dummy errno,
952 // use the sender side errno here.
957 always_assert_flog(buf
== "success",
958 "Unexpected message from light process: `{}'", buf
);
963 }, static_cast<pid_t
>(-1));
966 pid_t
LightProcess::waitpid(pid_t pid
, int *stat_loc
, int options
,
969 // light process is not really there
971 const auto ret
= wait4(pid
, stat_loc
, options
, &ru
);
972 if (ret
> 0 && s_trackProcessTimes
) {
973 s_extra_request_nanoseconds
+= ru2microseconds(ru
) * 1000;
978 return runLight("waitpid", [&] (LightProcess
* proc
) -> pid_t
{
979 lwp_write(proc
->m_afdt_fd
, "waitpid", pid
, options
, timeout
);
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]);
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]);
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();
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
;
1033 int LightProcess::createDelegate() {
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());
1041 pid_t child
= fork();
1044 Logger::Warning("Unable to fork delegate process: %s",
1045 folly::errnoStr(errno
).c_str());
1057 mprotect_1g_pages(PROT_READ
);
1058 if (s_trackProcessTimes
) {
1059 HardwareCounter::RecordSubprocessTimes();
1062 pid_t sid
= setsid();
1064 Logger::Warning("Unable to setsid");
1065 _Exit(HPHP_EXIT_FAILURE
);
1071 int newfd
= dup2(pair
[1], 0);
1072 always_assert(newfd
== 0);
1080 always_assert(child
> 0);
1085 ///////////////////////////////////////////////////////////////////////////////