1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "base/process.hpp"
4 #include "base/exception.hpp"
5 #include "base/convert.hpp"
6 #include "base/array.hpp"
7 #include "base/objectlock.hpp"
8 #include "base/utility.hpp"
9 #include "base/initialize.hpp"
10 #include "base/logger.hpp"
11 #include "base/utility.hpp"
12 #include "base/scriptglobal.hpp"
13 #include "base/json.hpp"
14 #include <boost/algorithm/string/join.hpp>
15 #include <boost/thread/once.hpp>
25 extern char **environ;
26 # else /* __APPLE__ */
27 # include <crt_externs.h>
28 # define environ (*_NSGetEnviron())
29 # endif /* __APPLE__ */
32 using namespace icinga;
36 static boost::mutex l_ProcessMutex[IOTHREADS];
37 static std::map<Process::ProcessHandle, Process::Ptr> l_Processes[IOTHREADS];
39 static HANDLE l_Events[IOTHREADS];
41 static int l_EventFDs[IOTHREADS][2];
42 static std::map<Process::ConsoleHandle, Process::ProcessHandle> l_FDs[IOTHREADS];
44 static boost::mutex l_ProcessControlMutex;
45 static int l_ProcessControlFD = -1;
46 static pid_t l_ProcessControlPID;
48 static boost::once_flag l_ProcessOnceFlag = BOOST_ONCE_INIT;
49 static boost::once_flag l_SpawnHelperOnceFlag = BOOST_ONCE_INIT;
51 Process::Process(Process::Arguments arguments, Dictionary::Ptr extraEnvironment)
52 : m_Arguments(std::move(arguments)), m_ExtraEnvironment(std::move(extraEnvironment)), m_Timeout(600), m_AdjustPriority(false)
54 , m_ReadPending(false), m_ReadFailed(false), m_Overlapped()
58 m_Overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
65 CloseHandle(m_Overlapped.hEvent);
70 static Value ProcessSpawnImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
72 struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
74 if (cmsg == nullptr || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_len != CMSG_LEN(sizeof(int) * 3)) {
75 std::cerr << "Invalid 'spawn' request: FDs missing" << std::endl;
79 auto *fds = (int *)CMSG_DATA(cmsg);
81 Array::Ptr arguments = request->Get("arguments");
82 Dictionary::Ptr extraEnvironment = request->Get("extraEnvironment");
83 bool adjustPriority = request->Get("adjustPriority");
86 auto **argv = new char *[arguments->GetLength() + 1];
88 for (unsigned int i = 0; i < arguments->GetLength(); i++) {
89 String arg = arguments->Get(i);
90 argv[i] = strdup(arg.CStr());
93 argv[arguments->GetLength()] = nullptr;
98 /* count existing environment variables */
102 auto **envp = new char *[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0) + 2];
104 for (int i = 0; i < envc; i++)
105 envp[i] = strdup(environ[i]);
107 if (extraEnvironment) {
108 ObjectLock olock(extraEnvironment);
111 for (const Dictionary::Pair& kv : extraEnvironment) {
112 String skv = kv.first + "=" + Convert::ToString(kv.second);
113 envp[index] = strdup(skv.CStr());
118 envp[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0)] = strdup("LC_NUMERIC=C");
119 envp[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0) + 1] = nullptr;
121 extraEnvironment.reset();
133 (void)close(l_ProcessControlFD);
136 perror("setsid() failed");
140 if (dup2(fds[0], STDIN_FILENO) < 0 || dup2(fds[1], STDOUT_FILENO) < 0 || dup2(fds[2], STDERR_FILENO) < 0) {
141 perror("dup2() failed");
150 if (adjustPriority) {
151 // Cheating the compiler on "warning: ignoring return value of 'int nice(int)', declared with attribute warn_unused_result [-Wunused-result]".
155 #endif /* HAVE_NICE */
159 sigprocmask(SIG_SETMASK, &mask, nullptr);
161 if (icinga2_execvpe(argv[0], argv, envp) < 0) {
163 strcpy(errmsg, "execvpe(");
164 strncat(errmsg, argv[0], sizeof(errmsg) - strlen(errmsg) - 1);
165 strncat(errmsg, ") failed", sizeof(errmsg) - strlen(errmsg) - 1);
166 errmsg[sizeof(errmsg) - 1] = '\0';
179 for (int i = 0; argv[i]; i++)
185 for (int i = 0; envp[i]; i++)
190 Dictionary::Ptr response = new Dictionary({
192 { "errno", errorCode }
198 static Value ProcessKillImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
200 pid_t pid = request->Get("pid");
201 int signum = request->Get("signum");
207 Dictionary::Ptr response = new Dictionary({
214 static Value ProcessWaitPIDImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
216 pid_t pid = request->Get("pid");
219 int rc = waitpid(pid, &status, 0);
221 Dictionary::Ptr response = new Dictionary({
222 { "status", status },
229 static void ProcessHandler()
233 sigprocmask(SIG_SETMASK, &mask, nullptr);
236 if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) {
237 rlim_t maxfds = rl.rlim_max;
239 if (maxfds == RLIM_INFINITY)
242 for (rlim_t i = 3; i < maxfds; i++)
243 if (i != static_cast<rlim_t>(l_ProcessControlFD))
251 memset(&msg, 0, sizeof(msg));
254 io.iov_base = &length;
255 io.iov_len = sizeof(length);
261 msg.msg_control = cbuf;
262 msg.msg_controllen = sizeof(cbuf);
264 int rc = recvmsg(l_ProcessControlFD, &msg, 0);
267 if (rc < 0 && (errno == EINTR || errno == EAGAIN))
273 auto *mbuf = new char[length];
276 while (count < length) {
277 rc = recv(l_ProcessControlFD, mbuf + count, length - count, 0);
280 if (rc < 0 && (errno == EINTR || errno == EAGAIN))
294 String jrequest = String(mbuf, mbuf + count);
298 Dictionary::Ptr request = JsonDecode(jrequest);
300 String command = request->Get("command");
304 if (command == "spawn")
305 response = ProcessSpawnImpl(&msg, request);
306 else if (command == "waitpid")
307 response = ProcessWaitPIDImpl(&msg, request);
308 else if (command == "kill")
309 response = ProcessKillImpl(&msg, request);
313 String jresponse = JsonEncode(response);
315 if (send(l_ProcessControlFD, jresponse.CStr(), jresponse.GetLength(), 0) < 0) {
316 BOOST_THROW_EXCEPTION(posix_error()
317 << boost::errinfo_api_function("send")
318 << boost::errinfo_errno(errno));
325 static void StartSpawnProcessHelper()
327 if (l_ProcessControlFD != -1) {
328 (void)close(l_ProcessControlFD);
331 (void)waitpid(l_ProcessControlPID, &status, 0);
335 if (socketpair(AF_UNIX, SOCK_STREAM, 0, controlFDs) < 0) {
336 BOOST_THROW_EXCEPTION(posix_error()
337 << boost::errinfo_api_function("socketpair")
338 << boost::errinfo_errno(errno));
344 BOOST_THROW_EXCEPTION(posix_error()
345 << boost::errinfo_api_function("fork")
346 << boost::errinfo_errno(errno));
350 (void)close(controlFDs[1]);
352 l_ProcessControlFD = controlFDs[0];
359 (void)close(controlFDs[0]);
361 l_ProcessControlFD = controlFDs[1];
362 l_ProcessControlPID = pid;
365 static pid_t ProcessSpawn(const std::vector<String>& arguments, const Dictionary::Ptr& extraEnvironment, bool adjustPriority, int fds[3])
367 Dictionary::Ptr request = new Dictionary({
368 { "command", "spawn" },
369 { "arguments", Array::FromVector(arguments) },
370 { "extraEnvironment", extraEnvironment },
371 { "adjustPriority", adjustPriority }
374 String jrequest = JsonEncode(request);
375 size_t length = jrequest.GetLength();
377 boost::mutex::scoped_lock lock(l_ProcessControlMutex);
380 memset(&msg, 0, sizeof(msg));
383 io.iov_base = &length;
384 io.iov_len = sizeof(length);
389 char cbuf[CMSG_SPACE(sizeof(int) * 3)];
390 msg.msg_control = cbuf;
391 msg.msg_controllen = sizeof(cbuf);
393 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
394 cmsg->cmsg_level = SOL_SOCKET;
395 cmsg->cmsg_type = SCM_RIGHTS;
396 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 3);
398 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * 3);
400 msg.msg_controllen = cmsg->cmsg_len;
403 while (sendmsg(l_ProcessControlFD, &msg, 0) < 0) {
404 StartSpawnProcessHelper();
406 } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0);
410 ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
415 String jresponse = String(buf, buf + rc);
417 Dictionary::Ptr response = JsonDecode(jresponse);
419 if (response->Get("rc") == -1)
420 errno = response->Get("errno");
422 return response->Get("rc");
425 static int ProcessKill(pid_t pid, int signum)
427 Dictionary::Ptr request = new Dictionary({
428 { "command", "kill" },
433 String jrequest = JsonEncode(request);
434 size_t length = jrequest.GetLength();
436 boost::mutex::scoped_lock lock(l_ProcessControlMutex);
439 while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0) {
440 StartSpawnProcessHelper();
442 } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0);
446 ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
451 String jresponse = String(buf, buf + rc);
453 Dictionary::Ptr response = JsonDecode(jresponse);
454 return response->Get("errno");
457 static int ProcessWaitPID(pid_t pid, int *status)
459 Dictionary::Ptr request = new Dictionary({
460 { "command", "waitpid" },
464 String jrequest = JsonEncode(request);
465 size_t length = jrequest.GetLength();
467 boost::mutex::scoped_lock lock(l_ProcessControlMutex);
470 while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0) {
471 StartSpawnProcessHelper();
473 } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0);
477 ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
482 String jresponse = String(buf, buf + rc);
484 Dictionary::Ptr response = JsonDecode(jresponse);
485 *status = response->Get("status");
486 return response->Get("rc");
489 void Process::InitializeSpawnHelper()
491 if (l_ProcessControlFD == -1)
492 StartSpawnProcessHelper();
496 static void InitializeProcess()
499 for (auto& event : l_Events) {
500 event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
503 for (auto& eventFD : l_EventFDs) {
505 if (pipe2(eventFD, O_CLOEXEC) < 0) {
506 if (errno == ENOSYS) {
507 # endif /* HAVE_PIPE2 */
508 if (pipe(eventFD) < 0) {
509 BOOST_THROW_EXCEPTION(posix_error()
510 << boost::errinfo_api_function("pipe")
511 << boost::errinfo_errno(errno));
514 Utility::SetCloExec(eventFD[0]);
515 Utility::SetCloExec(eventFD[1]);
518 BOOST_THROW_EXCEPTION(posix_error()
519 << boost::errinfo_api_function("pipe2")
520 << boost::errinfo_errno(errno));
523 # endif /* HAVE_PIPE2 */
528 INITIALIZE_ONCE(InitializeProcess);
530 void Process::ThreadInitialize()
532 /* Note to self: Make sure this runs _after_ we've daemonized. */
533 for (int tid = 0; tid < IOTHREADS; tid++) {
534 std::thread t(std::bind(&Process::IOThreadProc, tid));
539 Process::Arguments Process::PrepareCommand(const Value& command)
544 std::vector<String> args;
547 if (command.IsObjectType<Array>()) {
548 Array::Ptr arguments = command;
550 ObjectLock olock(arguments);
551 for (const Value& argument : arguments) {
556 args += Utility::EscapeCreateProcessArg(argument);
558 args.push_back(argument);
568 return { "sh", "-c", command };
572 void Process::SetTimeout(double timeout)
577 double Process::GetTimeout() const
582 void Process::SetAdjustPriority(bool adjust)
584 m_AdjustPriority = adjust;
587 bool Process::GetAdjustPriority() const
589 return m_AdjustPriority;
592 void Process::IOThreadProc(int tid)
595 HANDLE *handles = nullptr;
596 HANDLE *fhandles = nullptr;
598 pollfd *pfds = nullptr;
603 Utility::SetThreadName("ProcessIO");
608 now = Utility::GetTime();
611 boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
613 count = 1 + l_Processes[tid].size();
615 handles = reinterpret_cast<HANDLE *>(realloc(handles, sizeof(HANDLE) * count));
616 fhandles = reinterpret_cast<HANDLE *>(realloc(fhandles, sizeof(HANDLE) * count));
618 fhandles[0] = l_Events[tid];
621 pfds = reinterpret_cast<pollfd *>(realloc(pfds, sizeof(pollfd) * count));
623 pfds[0].fd = l_EventFDs[tid][0];
624 pfds[0].events = POLLIN;
629 typedef std::pair<ProcessHandle, Process::Ptr> kv_pair;
630 for (const kv_pair& kv : l_Processes[tid]) {
631 const Process::Ptr& process = kv.second;
633 handles[i] = kv.first;
635 if (!process->m_ReadPending) {
636 process->m_ReadPending = true;
638 BOOL res = ReadFile(process->m_FD, process->m_ReadBuffer, sizeof(process->m_ReadBuffer), 0, &process->m_Overlapped);
639 if (res || GetLastError() != ERROR_IO_PENDING) {
640 process->m_ReadFailed = !res;
641 SetEvent(process->m_Overlapped.hEvent);
645 fhandles[i] = process->m_Overlapped.hEvent;
647 pfds[i].fd = process->m_FD;
648 pfds[i].events = POLLIN;
652 if (process->m_Timeout != 0) {
653 double delta = process->m_Timeout - (now - process->m_Result.ExecutionStart);
655 if (timeout == -1 || delta < timeout)
669 DWORD rc = WaitForMultipleObjects(count, fhandles, FALSE, timeout == -1 ? INFINITE : static_cast<DWORD>(timeout));
671 int rc = poll(pfds, count, timeout);
677 now = Utility::GetTime();
680 boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
683 if (rc == WAIT_OBJECT_0)
684 ResetEvent(l_Events[tid]);
686 if (pfds[0].revents & (POLLIN | POLLHUP | POLLERR)) {
688 if (read(l_EventFDs[tid][0], buffer, sizeof(buffer)) < 0)
689 Log(LogCritical, "base", "Read from event FD failed.");
693 for (int i = 1; i < count; i++) {
695 auto it = l_Processes[tid].find(handles[i]);
697 auto it2 = l_FDs[tid].find(pfds[i].fd);
699 if (it2 == l_FDs[tid].end())
700 continue; /* This should never happen. */
702 auto it = l_Processes[tid].find(it2->second);
705 if (it == l_Processes[tid].end())
706 continue; /* This should never happen. */
708 bool is_timeout = false;
710 if (it->second->m_Timeout != 0) {
711 double timeout = it->second->m_Result.ExecutionStart + it->second->m_Timeout;
718 if (rc == WAIT_OBJECT_0 + i || is_timeout) {
720 if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR) || is_timeout) {
722 if (!it->second->DoEvents()) {
724 CloseHandle(it->first);
725 CloseHandle(it->second->m_FD);
727 l_FDs[tid].erase(it->second->m_FD);
728 (void)close(it->second->m_FD);
730 l_Processes[tid].erase(it);
738 String Process::PrettyPrintArguments(const Process::Arguments& arguments)
741 return "'" + arguments + "'";
743 return "'" + boost::algorithm::join(arguments, "' '") + "'";
748 static BOOL CreatePipeOverlapped(HANDLE *outReadPipe, HANDLE *outWritePipe,
749 SECURITY_ATTRIBUTES *securityAttributes, DWORD size, DWORD readMode, DWORD writeMode)
751 static LONG pipeIndex = 0;
756 LONG currentIndex = InterlockedIncrement(&pipeIndex);
759 sprintf(pipeName, "\\\\.\\Pipe\\OverlappedPipe.%d.%d", (int)GetCurrentProcessId(), (int)currentIndex);
761 *outReadPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_INBOUND | readMode,
762 PIPE_TYPE_BYTE | PIPE_WAIT, 1, size, size, 60 * 1000, securityAttributes);
764 if (*outReadPipe == INVALID_HANDLE_VALUE)
767 *outWritePipe = CreateFile(pipeName, GENERIC_WRITE, 0, securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | writeMode, nullptr);
769 if (*outWritePipe == INVALID_HANDLE_VALUE) {
770 DWORD error = GetLastError();
771 CloseHandle(*outReadPipe);
780 void Process::Run(const std::function<void(const ProcessResult&)>& callback)
783 boost::call_once(l_SpawnHelperOnceFlag, &Process::InitializeSpawnHelper);
785 boost::call_once(l_ProcessOnceFlag, &Process::ThreadInitialize);
787 m_Result.ExecutionStart = Utility::GetTime();
790 SECURITY_ATTRIBUTES sa = {};
791 sa.nLength = sizeof(sa);
792 sa.bInheritHandle = TRUE;
794 HANDLE outReadPipe, outWritePipe;
795 if (!CreatePipeOverlapped(&outReadPipe, &outWritePipe, &sa, 0, FILE_FLAG_OVERLAPPED, 0))
796 BOOST_THROW_EXCEPTION(win32_error()
797 << boost::errinfo_api_function("CreatePipe")
798 << errinfo_win32_error(GetLastError()));
800 if (!SetHandleInformation(outReadPipe, HANDLE_FLAG_INHERIT, 0))
801 BOOST_THROW_EXCEPTION(win32_error()
802 << boost::errinfo_api_function("SetHandleInformation")
803 << errinfo_win32_error(GetLastError()));
805 HANDLE outWritePipeDup;
806 if (!DuplicateHandle(GetCurrentProcess(), outWritePipe, GetCurrentProcess(),
807 &outWritePipeDup, 0, TRUE, DUPLICATE_SAME_ACCESS))
808 BOOST_THROW_EXCEPTION(win32_error()
809 << boost::errinfo_api_function("DuplicateHandle")
810 << errinfo_win32_error(GetLastError()));
812 /* LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
815 if (!InitializeProcThreadAttributeList(nullptr, 1, 0, &cbSize) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
816 BOOST_THROW_EXCEPTION(win32_error()
817 << boost::errinfo_api_function("InitializeProcThreadAttributeList")
818 << errinfo_win32_error(GetLastError()));
820 lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(new char[cbSize]);
822 if (!InitializeProcThreadAttributeList(lpAttributeList, 1, 0, &cbSize))
823 BOOST_THROW_EXCEPTION(win32_error()
824 << boost::errinfo_api_function("InitializeProcThreadAttributeList")
825 << errinfo_win32_error(GetLastError()));
828 rgHandles[0] = outWritePipe;
829 rgHandles[1] = outWritePipeDup;
830 rgHandles[2] = GetStdHandle(STD_INPUT_HANDLE);
832 if (!UpdateProcThreadAttribute(lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
833 rgHandles, sizeof(rgHandles), nullptr, nullptr))
834 BOOST_THROW_EXCEPTION(win32_error()
835 << boost::errinfo_api_function("UpdateProcThreadAttribute")
836 << errinfo_win32_error(GetLastError()));
839 STARTUPINFOEX si = {};
840 si.StartupInfo.cb = sizeof(si);
841 si.StartupInfo.hStdError = outWritePipe;
842 si.StartupInfo.hStdOutput = outWritePipeDup;
843 si.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
844 si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
845 // si.lpAttributeList = lpAttributeList;
847 PROCESS_INFORMATION pi;
849 char *args = new char[m_Arguments.GetLength() + 1];
850 strncpy(args, m_Arguments.CStr(), m_Arguments.GetLength() + 1);
851 args[m_Arguments.GetLength()] = '\0';
853 LPCH pEnvironment = GetEnvironmentStrings();
854 size_t ioffset = 0, offset = 0;
856 char *envp = nullptr;
859 size_t len = strlen(pEnvironment + ioffset);
864 char *eqp = strchr(pEnvironment + ioffset, '=');
865 if (eqp && m_ExtraEnvironment && m_ExtraEnvironment->Contains(String(pEnvironment + ioffset, eqp))) {
870 envp = static_cast<char *>(realloc(envp, offset + len + 1));
873 BOOST_THROW_EXCEPTION(std::bad_alloc());
875 strcpy(envp + offset, pEnvironment + ioffset);
880 FreeEnvironmentStrings(pEnvironment);
882 if (m_ExtraEnvironment) {
883 ObjectLock olock(m_ExtraEnvironment);
885 for (const Dictionary::Pair& kv : m_ExtraEnvironment) {
886 String skv = kv.first + "=" + Convert::ToString(kv.second);
888 envp = static_cast<char *>(realloc(envp, offset + skv.GetLength() + 1));
891 BOOST_THROW_EXCEPTION(std::bad_alloc());
893 strcpy(envp + offset, skv.CStr());
894 offset += skv.GetLength() + 1;
898 envp = static_cast<char *>(realloc(envp, offset + 1));
901 BOOST_THROW_EXCEPTION(std::bad_alloc());
905 if (!CreateProcess(nullptr, args, nullptr, nullptr, TRUE,
906 0 /*EXTENDED_STARTUPINFO_PRESENT*/, envp, nullptr, &si.StartupInfo, &pi)) {
907 DWORD error = GetLastError();
908 CloseHandle(outWritePipe);
909 CloseHandle(outWritePipeDup);
911 /* DeleteProcThreadAttributeList(lpAttributeList);
912 delete [] reinterpret_cast<char *>(lpAttributeList); */
915 m_Result.ExecutionEnd = Utility::GetTime();
916 m_Result.ExitStatus = 127;
917 m_Result.Output = "Command " + String(args) + " failed to execute: " + Utility::FormatErrorNumber(error);
922 Utility::QueueAsyncCallback(std::bind(callback, m_Result));
929 /* DeleteProcThreadAttributeList(lpAttributeList);
930 delete [] reinterpret_cast<char *>(lpAttributeList); */
932 CloseHandle(outWritePipe);
933 CloseHandle(outWritePipeDup);
934 CloseHandle(pi.hThread);
936 m_Process = pi.hProcess;
938 m_PID = pi.dwProcessId;
940 Log(LogNotice, "Process")
941 << "Running command " << PrettyPrintArguments(m_Arguments) << ": PID " << m_PID;
947 if (pipe2(outfds, O_CLOEXEC) < 0) {
948 if (errno == ENOSYS) {
949 #endif /* HAVE_PIPE2 */
950 if (pipe(outfds) < 0) {
951 BOOST_THROW_EXCEPTION(posix_error()
952 << boost::errinfo_api_function("pipe")
953 << boost::errinfo_errno(errno));
956 Utility::SetCloExec(outfds[0]);
957 Utility::SetCloExec(outfds[1]);
960 BOOST_THROW_EXCEPTION(posix_error()
961 << boost::errinfo_api_function("pipe2")
962 << boost::errinfo_errno(errno));
965 #endif /* HAVE_PIPE2 */
968 fds[0] = STDIN_FILENO;
972 m_Process = ProcessSpawn(m_Arguments, m_ExtraEnvironment, m_AdjustPriority, fds);
976 m_OutputStream << "Fork failed with error code " << errno << " (" << Utility::FormatErrorNumber(errno) << ")";
977 Log(LogCritical, "Process", m_OutputStream.str());
980 Log(LogNotice, "Process")
981 << "Running command " << PrettyPrintArguments(m_Arguments) << ": PID " << m_PID;
983 (void)close(outfds[1]);
985 Utility::SetNonBlocking(outfds[0]);
990 m_Callback = callback;
995 boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
996 l_Processes[tid][m_Process] = this;
998 l_FDs[tid][m_FD] = m_Process;
1003 SetEvent(l_Events[tid]);
1005 if (write(l_EventFDs[tid][1], "T", 1) < 0 && errno != EINTR && errno != EAGAIN)
1006 Log(LogCritical, "base", "Write to event FD failed.");
1010 bool Process::DoEvents()
1012 bool is_timeout = false;
1014 bool could_not_kill = false;
1017 if (m_Timeout != 0) {
1018 double timeout = m_Result.ExecutionStart + m_Timeout;
1020 if (timeout < Utility::GetTime()) {
1021 Log(LogWarning, "Process")
1022 << "Killing process group " << m_PID << " (" << PrettyPrintArguments(m_Arguments)
1023 << ") after timeout of " << m_Timeout << " seconds";
1025 m_OutputStream << "<Timeout exceeded.>";
1027 TerminateProcess(m_Process, 1);
1029 int error = ProcessKill(-m_Process, SIGKILL);
1031 Log(LogWarning, "Process")
1032 << "Couldn't kill the process group " << m_PID << " (" << PrettyPrintArguments(m_Arguments)
1033 << "): [errno " << error << "] " << strerror(error);
1034 could_not_kill = true;
1044 m_ReadPending = false;
1047 if (!m_ReadFailed && GetOverlappedResult(m_FD, &m_Overlapped, &rc, TRUE) && rc > 0) {
1048 m_OutputStream.write(m_ReadBuffer, rc);
1054 int rc = read(m_FD, buffer, sizeof(buffer));
1056 if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
1060 m_OutputStream.write(buffer, rc);
1069 String output = m_OutputStream.str();
1072 WaitForSingleObject(m_Process, INFINITE);
1075 GetExitCodeProcess(m_Process, &exitcode);
1077 Log(LogNotice, "Process")
1078 << "PID " << m_PID << " (" << PrettyPrintArguments(m_Arguments) << ") terminated with exit code " << exitcode;
1080 int status, exitcode;
1081 if (could_not_kill || m_PID == -1) {
1083 } else if (ProcessWaitPID(m_Process, &status) != m_Process) {
1086 Log(LogWarning, "Process")
1087 << "PID " << m_PID << " (" << PrettyPrintArguments(m_Arguments) << ") died mysteriously: waitpid failed";
1088 } else if (WIFEXITED(status)) {
1089 exitcode = WEXITSTATUS(status);
1091 Log(LogNotice, "Process")
1092 << "PID " << m_PID << " (" << PrettyPrintArguments(m_Arguments) << ") terminated with exit code " << exitcode;
1093 } else if (WIFSIGNALED(status)) {
1094 int signum = WTERMSIG(status);
1095 const char *zsigname = strsignal(signum);
1097 String signame = Convert::ToString(signum);
1101 signame += zsigname;
1105 Log(LogWarning, "Process")
1106 << "PID " << m_PID << " was terminated by signal " << signame;
1108 std::ostringstream outputbuf;
1109 outputbuf << "<Terminated by signal " << signame << ".>";
1110 output = output + outputbuf.str();
1117 m_Result.PID = m_PID;
1118 m_Result.ExecutionEnd = Utility::GetTime();
1119 m_Result.ExitStatus = exitcode;
1120 m_Result.Output = output;
1123 Utility::QueueAsyncCallback(std::bind(m_Callback, m_Result));
1128 pid_t Process::GetPID() const
1134 int Process::GetTID() const
1136 return (reinterpret_cast<uintptr_t>(this) / sizeof(void *)) % IOTHREADS;