1 /******************************************************************************
3 * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
20 #include "base/process.hpp"
21 #include "base/exception.hpp"
22 #include "base/convert.hpp"
23 #include "base/array.hpp"
24 #include "base/objectlock.hpp"
25 #include "base/utility.hpp"
26 #include "base/initialize.hpp"
27 #include "base/logger.hpp"
28 #include "base/utility.hpp"
29 #include "base/scriptglobal.hpp"
30 #include "base/json.hpp"
31 #include <boost/algorithm/string/join.hpp>
32 #include <boost/thread/once.hpp>
41 extern char **environ;
42 # else /* __APPLE__ */
43 # include <crt_externs.h>
44 # define environ (*_NSGetEnviron())
45 # endif /* __APPLE__ */
48 using namespace icinga;
52 static boost::mutex l_ProcessMutex[IOTHREADS];
53 static std::map<Process::ProcessHandle, Process::Ptr> l_Processes[IOTHREADS];
55 static HANDLE l_Events[IOTHREADS];
57 static int l_EventFDs[IOTHREADS][2];
58 static std::map<Process::ConsoleHandle, Process::ProcessHandle> l_FDs[IOTHREADS];
60 static boost::mutex l_ProcessControlMutex;
61 static int l_ProcessControlFD = -1;
62 static pid_t l_ProcessControlPID;
64 static boost::once_flag l_ProcessOnceFlag = BOOST_ONCE_INIT;
65 static boost::once_flag l_SpawnHelperOnceFlag = BOOST_ONCE_INIT;
67 Process::Process(const Process::Arguments& arguments, const Dictionary::Ptr& extraEnvironment)
68 : m_Arguments(arguments), m_ExtraEnvironment(extraEnvironment), m_Timeout(600), m_AdjustPriority(false)
70 , m_ReadPending(false), m_ReadFailed(false), m_Overlapped()
74 m_Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
78 Process::~Process(void)
81 CloseHandle(m_Overlapped.hEvent);
86 static Value ProcessSpawnImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
88 struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
90 if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_len != CMSG_LEN(sizeof(int) * 3)) {
91 std::cerr << "Invalid 'spawn' request: FDs missing" << std::endl;
95 int *fds = (int *)CMSG_DATA(cmsg);
97 Array::Ptr arguments = request->Get("arguments");
98 Dictionary::Ptr extraEnvironment = request->Get("extraEnvironment");
99 bool adjustPriority = request->Get("adjustPriority");
102 char **argv = new char *[arguments->GetLength() + 1];
104 for (unsigned int i = 0; i < arguments->GetLength(); i++) {
105 String arg = arguments->Get(i);
106 argv[i] = strdup(arg.CStr());
109 argv[arguments->GetLength()] = NULL;
114 /* count existing environment variables */
115 while (environ[envc] != NULL)
118 char **envp = new char *[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0) + 2];
120 for (int i = 0; i < envc; i++)
121 envp[i] = strdup(environ[i]);
123 if (extraEnvironment) {
124 ObjectLock olock(extraEnvironment);
127 for (const Dictionary::Pair& kv : extraEnvironment) {
128 String skv = kv.first + "=" + Convert::ToString(kv.second);
129 envp[index] = strdup(skv.CStr());
134 envp[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0)] = strdup("LC_NUMERIC=C");
135 envp[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0) + 1] = NULL;
137 extraEnvironment.reset();
149 (void)close(l_ProcessControlFD);
152 perror("setsid() failed");
156 if (dup2(fds[0], STDIN_FILENO) < 0 || dup2(fds[1], STDOUT_FILENO) < 0 || dup2(fds[2], STDERR_FILENO) < 0) {
157 perror("dup2() failed");
168 #endif /* HAVE_NICE */
172 sigprocmask(SIG_SETMASK, &mask, NULL);
174 if (icinga2_execvpe(argv[0], argv, envp) < 0) {
176 strcpy(errmsg, "execvpe(");
177 strncat(errmsg, argv[0], sizeof(errmsg) - strlen(errmsg) - 1);
178 strncat(errmsg, ") failed", sizeof(errmsg) - strlen(errmsg) - 1);
179 errmsg[sizeof(errmsg) - 1] = '\0';
192 for (int i = 0; argv[i] != NULL; i++)
198 for (int i = 0; envp[i] != NULL; i++)
203 Dictionary::Ptr response = new Dictionary();
204 response->Set("rc", pid);
206 response->Set("errno", errorCode);
210 static Value ProcessKillImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
212 pid_t pid = request->Get("pid");
213 int signum = request->Get("signum");
219 Dictionary::Ptr response = new Dictionary();
220 response->Set("errno", error);
225 static Value ProcessWaitPIDImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
227 pid_t pid = request->Get("pid");
230 int rc = waitpid(pid, &status, 0);
232 Dictionary::Ptr response = new Dictionary();
233 response->Set("status", status);
234 response->Set("rc", rc);
239 static void ProcessHandler(void)
243 sigprocmask(SIG_SETMASK, &mask, NULL);
246 if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) {
247 rlim_t maxfds = rl.rlim_max;
249 if (maxfds == RLIM_INFINITY)
252 for (rlim_t i = 3; i < maxfds; i++)
253 if (i != static_cast<rlim_t>(l_ProcessControlFD))
261 memset(&msg, 0, sizeof(msg));
264 io.iov_base = &length;
265 io.iov_len = sizeof(length);
271 msg.msg_control = cbuf;
272 msg.msg_controllen = sizeof(cbuf);
274 int rc = recvmsg(l_ProcessControlFD, &msg, 0);
277 if (rc < 0 && (errno == EINTR || errno == EAGAIN))
283 char *mbuf = new char[length];
286 while (count < length) {
287 rc = recv(l_ProcessControlFD, mbuf + count, length - count, 0);
290 if (rc < 0 && (errno == EINTR || errno == EAGAIN))
304 String jrequest = String(mbuf, mbuf + count);
308 Dictionary::Ptr request = JsonDecode(jrequest);
310 String command = request->Get("command");
314 if (command == "spawn")
315 response = ProcessSpawnImpl(&msg, request);
316 else if (command == "waitpid")
317 response = ProcessWaitPIDImpl(&msg, request);
318 else if (command == "kill")
319 response = ProcessKillImpl(&msg, request);
323 String jresponse = JsonEncode(response);
325 if (send(l_ProcessControlFD, jresponse.CStr(), jresponse.GetLength(), 0) < 0) {
326 BOOST_THROW_EXCEPTION(posix_error()
327 << boost::errinfo_api_function("send")
328 << boost::errinfo_errno(errno));
335 static void StartSpawnProcessHelper(void)
337 if (l_ProcessControlFD != -1) {
338 (void)close(l_ProcessControlFD);
341 (void)waitpid(l_ProcessControlPID, &status, 0);
345 if (socketpair(AF_UNIX, SOCK_STREAM, 0, controlFDs) < 0) {
346 BOOST_THROW_EXCEPTION(posix_error()
347 << boost::errinfo_api_function("socketpair")
348 << boost::errinfo_errno(errno));
354 BOOST_THROW_EXCEPTION(posix_error()
355 << boost::errinfo_api_function("fork")
356 << boost::errinfo_errno(errno));
360 (void)close(controlFDs[1]);
362 l_ProcessControlFD = controlFDs[0];
369 (void)close(controlFDs[0]);
371 l_ProcessControlFD = controlFDs[1];
372 l_ProcessControlPID = pid;
375 static pid_t ProcessSpawn(const std::vector<String>& arguments, const Dictionary::Ptr& extraEnvironment, bool adjustPriority, int fds[3])
377 Dictionary::Ptr request = new Dictionary();
378 request->Set("command", "spawn");
379 request->Set("arguments", Array::FromVector(arguments));
380 request->Set("extraEnvironment", extraEnvironment);
381 request->Set("adjustPriority", adjustPriority);
383 String jrequest = JsonEncode(request);
384 size_t length = jrequest.GetLength();
386 boost::mutex::scoped_lock lock(l_ProcessControlMutex);
389 memset(&msg, 0, sizeof(msg));
392 io.iov_base = &length;
393 io.iov_len = sizeof(length);
398 char cbuf[CMSG_SPACE(sizeof(int) * 3)];
399 msg.msg_control = cbuf;
400 msg.msg_controllen = sizeof(cbuf);
402 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
403 cmsg->cmsg_level = SOL_SOCKET;
404 cmsg->cmsg_type = SCM_RIGHTS;
405 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 3);
407 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * 3);
409 msg.msg_controllen = cmsg->cmsg_len;
412 while (sendmsg(l_ProcessControlFD, &msg, 0) < 0)
413 StartSpawnProcessHelper();
415 if (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0)
420 ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
425 String jresponse = String(buf, buf + rc);
427 Dictionary::Ptr response = JsonDecode(jresponse);
429 if (response->Get("rc") == -1)
430 errno = response->Get("errno");
432 return response->Get("rc");
435 static int ProcessKill(pid_t pid, int signum)
437 Dictionary::Ptr request = new Dictionary();
438 request->Set("command", "kill");
439 request->Set("pid", pid);
440 request->Set("signum", signum);
442 String jrequest = JsonEncode(request);
443 size_t length = jrequest.GetLength();
445 boost::mutex::scoped_lock lock(l_ProcessControlMutex);
448 while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0)
449 StartSpawnProcessHelper();
451 if (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0)
456 ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
461 String jresponse = String(buf, buf + rc);
463 Dictionary::Ptr response = JsonDecode(jresponse);
464 return response->Get("errno");
467 static int ProcessWaitPID(pid_t pid, int *status)
469 Dictionary::Ptr request = new Dictionary();
470 request->Set("command", "waitpid");
471 request->Set("pid", pid);
473 String jrequest = JsonEncode(request);
474 size_t length = jrequest.GetLength();
476 boost::mutex::scoped_lock lock(l_ProcessControlMutex);
479 while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0)
480 StartSpawnProcessHelper();
482 if (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0)
487 ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
492 String jresponse = String(buf, buf + rc);
494 Dictionary::Ptr response = JsonDecode(jresponse);
495 *status = response->Get("status");
496 return response->Get("rc");
499 void Process::InitializeSpawnHelper(void)
501 if (l_ProcessControlFD == -1)
502 StartSpawnProcessHelper();
506 static void InitializeProcess(void)
508 for (int tid = 0; tid < IOTHREADS; tid++) {
510 l_Events[tid] = CreateEvent(NULL, TRUE, FALSE, NULL);
513 if (pipe2(l_EventFDs[tid], O_CLOEXEC) < 0) {
514 if (errno == ENOSYS) {
515 # endif /* HAVE_PIPE2 */
516 if (pipe(l_EventFDs[tid]) < 0) {
517 BOOST_THROW_EXCEPTION(posix_error()
518 << boost::errinfo_api_function("pipe")
519 << boost::errinfo_errno(errno));
522 Utility::SetCloExec(l_EventFDs[tid][0]);
523 Utility::SetCloExec(l_EventFDs[tid][1]);
526 BOOST_THROW_EXCEPTION(posix_error()
527 << boost::errinfo_api_function("pipe2")
528 << boost::errinfo_errno(errno));
531 # endif /* HAVE_PIPE2 */
536 INITIALIZE_ONCE(InitializeProcess);
538 void Process::ThreadInitialize(void)
540 /* Note to self: Make sure this runs _after_ we've daemonized. */
541 for (int tid = 0; tid < IOTHREADS; tid++) {
542 boost::thread t(boost::bind(&Process::IOThreadProc, tid));
547 Process::Arguments Process::PrepareCommand(const Value& command)
552 std::vector<String> args;
555 if (command.IsObjectType<Array>()) {
556 Array::Ptr arguments = command;
558 ObjectLock olock(arguments);
559 for (const Value& argument : arguments) {
564 args += Utility::EscapeCreateProcessArg(argument);
566 args.push_back(argument);
576 args.push_back("sh");
577 args.push_back("-c");
578 args.push_back(command);
583 void Process::SetTimeout(double timeout)
588 double Process::GetTimeout(void) const
593 void Process::SetAdjustPriority(bool adjust)
595 m_AdjustPriority = adjust;
598 bool Process::GetAdjustPriority(void) const
600 return m_AdjustPriority;
603 void Process::IOThreadProc(int tid)
606 HANDLE *handles = NULL;
607 HANDLE *fhandles = NULL;
614 Utility::SetThreadName("ProcessIO");
619 now = Utility::GetTime();
622 boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
624 count = 1 + l_Processes[tid].size();
626 handles = reinterpret_cast<HANDLE *>(realloc(handles, sizeof(HANDLE) * count));
627 fhandles = reinterpret_cast<HANDLE *>(realloc(fhandles, sizeof(HANDLE) * count));
629 fhandles[0] = l_Events[tid];
632 pfds = reinterpret_cast<pollfd *>(realloc(pfds, sizeof(pollfd) * count));
634 pfds[0].fd = l_EventFDs[tid][0];
635 pfds[0].events = POLLIN;
640 typedef std::pair<ProcessHandle, Process::Ptr> kv_pair;
641 for (const kv_pair& kv : l_Processes[tid]) {
642 const Process::Ptr& process = kv.second;
644 handles[i] = kv.first;
646 if (!process->m_ReadPending) {
647 process->m_ReadPending = true;
649 BOOL res = ReadFile(process->m_FD, process->m_ReadBuffer, sizeof(process->m_ReadBuffer), 0, &process->m_Overlapped);
650 if (res || GetLastError() != ERROR_IO_PENDING) {
651 process->m_ReadFailed = !res;
652 SetEvent(process->m_Overlapped.hEvent);
656 fhandles[i] = process->m_Overlapped.hEvent;
658 pfds[i].fd = process->m_FD;
659 pfds[i].events = POLLIN;
663 if (process->m_Timeout != 0) {
664 double delta = process->m_Timeout - (now - process->m_Result.ExecutionStart);
666 if (timeout == -1 || delta < timeout)
680 DWORD rc = WaitForMultipleObjects(count, fhandles, FALSE, timeout == -1 ? INFINITE : static_cast<DWORD>(timeout));
682 int rc = poll(pfds, count, timeout);
688 now = Utility::GetTime();
691 boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
694 if (rc == WAIT_OBJECT_0)
695 ResetEvent(l_Events[tid]);
697 if (pfds[0].revents & (POLLIN | POLLHUP | POLLERR)) {
699 if (read(l_EventFDs[tid][0], buffer, sizeof(buffer)) < 0)
700 Log(LogCritical, "base", "Read from event FD failed.");
704 for (int i = 1; i < count; i++) {
706 auto it = l_Processes[tid].find(handles[i]);
708 auto it2 = l_FDs[tid].find(pfds[i].fd);
710 if (it2 == l_FDs[tid].end())
711 continue; /* This should never happen. */
713 auto it = l_Processes[tid].find(it2->second);
716 if (it == l_Processes[tid].end())
717 continue; /* This should never happen. */
719 bool is_timeout = false;
721 if (it->second->m_Timeout != 0) {
722 double timeout = it->second->m_Result.ExecutionStart + it->second->m_Timeout;
729 if (rc == WAIT_OBJECT_0 + i || is_timeout) {
731 if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR) || is_timeout) {
733 if (!it->second->DoEvents()) {
735 CloseHandle(it->first);
736 CloseHandle(it->second->m_FD);
738 l_FDs[tid].erase(it->second->m_FD);
739 (void)close(it->second->m_FD);
741 l_Processes[tid].erase(it);
749 String Process::PrettyPrintArguments(const Process::Arguments& arguments)
752 return "'" + arguments + "'";
754 return "'" + boost::algorithm::join(arguments, "' '") + "'";
759 static BOOL CreatePipeOverlapped(HANDLE *outReadPipe, HANDLE *outWritePipe,
760 SECURITY_ATTRIBUTES *securityAttributes, DWORD size, DWORD readMode, DWORD writeMode)
762 static LONG pipeIndex = 0;
767 LONG currentIndex = InterlockedIncrement(&pipeIndex);
770 sprintf(pipeName, "\\\\.\\Pipe\\OverlappedPipe.%d.%d", (int)GetCurrentProcessId(), (int)currentIndex);
772 *outReadPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_INBOUND | readMode,
773 PIPE_TYPE_BYTE | PIPE_WAIT, 1, size, size, 60 * 1000, securityAttributes);
775 if (*outReadPipe == INVALID_HANDLE_VALUE)
778 *outWritePipe = CreateFile(pipeName, GENERIC_WRITE, 0, securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | writeMode, NULL);
780 if (*outWritePipe == INVALID_HANDLE_VALUE) {
781 DWORD error = GetLastError();
782 CloseHandle(*outReadPipe);
791 void Process::Run(const boost::function<void(const ProcessResult&)>& callback)
794 boost::call_once(l_SpawnHelperOnceFlag, &Process::InitializeSpawnHelper);
796 boost::call_once(l_ProcessOnceFlag, &Process::ThreadInitialize);
798 m_Result.ExecutionStart = Utility::GetTime();
801 SECURITY_ATTRIBUTES sa = {};
802 sa.nLength = sizeof(sa);
803 sa.bInheritHandle = TRUE;
805 HANDLE outReadPipe, outWritePipe;
806 if (!CreatePipeOverlapped(&outReadPipe, &outWritePipe, &sa, 0, FILE_FLAG_OVERLAPPED, 0))
807 BOOST_THROW_EXCEPTION(win32_error()
808 << boost::errinfo_api_function("CreatePipe")
809 << errinfo_win32_error(GetLastError()));
811 if (!SetHandleInformation(outReadPipe, HANDLE_FLAG_INHERIT, 0))
812 BOOST_THROW_EXCEPTION(win32_error()
813 << boost::errinfo_api_function("SetHandleInformation")
814 << errinfo_win32_error(GetLastError()));
816 HANDLE outWritePipeDup;
817 if (!DuplicateHandle(GetCurrentProcess(), outWritePipe, GetCurrentProcess(),
818 &outWritePipeDup, 0, TRUE, DUPLICATE_SAME_ACCESS))
819 BOOST_THROW_EXCEPTION(win32_error()
820 << boost::errinfo_api_function("DuplicateHandle")
821 << errinfo_win32_error(GetLastError()));
823 /* LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
826 if (!InitializeProcThreadAttributeList(NULL, 1, 0, &cbSize) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
827 BOOST_THROW_EXCEPTION(win32_error()
828 << boost::errinfo_api_function("InitializeProcThreadAttributeList")
829 << errinfo_win32_error(GetLastError()));
831 lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(new char[cbSize]);
833 if (!InitializeProcThreadAttributeList(lpAttributeList, 1, 0, &cbSize))
834 BOOST_THROW_EXCEPTION(win32_error()
835 << boost::errinfo_api_function("InitializeProcThreadAttributeList")
836 << errinfo_win32_error(GetLastError()));
839 rgHandles[0] = outWritePipe;
840 rgHandles[1] = outWritePipeDup;
841 rgHandles[2] = GetStdHandle(STD_INPUT_HANDLE);
843 if (!UpdateProcThreadAttribute(lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
844 rgHandles, sizeof(rgHandles), NULL, NULL))
845 BOOST_THROW_EXCEPTION(win32_error()
846 << boost::errinfo_api_function("UpdateProcThreadAttribute")
847 << errinfo_win32_error(GetLastError()));
850 STARTUPINFOEX si = {};
851 si.StartupInfo.cb = sizeof(si);
852 si.StartupInfo.hStdError = outWritePipe;
853 si.StartupInfo.hStdOutput = outWritePipeDup;
854 si.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
855 si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
856 // si.lpAttributeList = lpAttributeList;
858 PROCESS_INFORMATION pi;
860 char *args = new char[m_Arguments.GetLength() + 1];
861 strncpy(args, m_Arguments.CStr(), m_Arguments.GetLength() + 1);
862 args[m_Arguments.GetLength()] = '\0';
864 LPCH pEnvironment = GetEnvironmentStrings();
865 size_t ioffset = 0, offset = 0;
870 size_t len = strlen(pEnvironment + ioffset);
875 char *eqp = strchr(pEnvironment + ioffset, '=');
876 if (eqp && m_ExtraEnvironment && m_ExtraEnvironment->Contains(String(pEnvironment + ioffset, eqp))) {
881 envp = static_cast<char *>(realloc(envp, offset + len + 1));
884 BOOST_THROW_EXCEPTION(std::bad_alloc());
886 strcpy(envp + offset, pEnvironment + ioffset);
891 FreeEnvironmentStrings(pEnvironment);
893 if (m_ExtraEnvironment) {
894 ObjectLock olock(m_ExtraEnvironment);
896 for (const Dictionary::Pair& kv : m_ExtraEnvironment) {
897 String skv = kv.first + "=" + Convert::ToString(kv.second);
899 envp = static_cast<char *>(realloc(envp, offset + skv.GetLength() + 1));
902 BOOST_THROW_EXCEPTION(std::bad_alloc());
904 strcpy(envp + offset, skv.CStr());
905 offset += skv.GetLength() + 1;
909 envp = static_cast<char *>(realloc(envp, offset + 1));
912 BOOST_THROW_EXCEPTION(std::bad_alloc());
916 if (!CreateProcess(NULL, args, NULL, NULL, TRUE,
917 0 /*EXTENDED_STARTUPINFO_PRESENT*/, envp, NULL, &si.StartupInfo, &pi)) {
918 DWORD error = GetLastError();
919 CloseHandle(outWritePipe);
920 CloseHandle(outWritePipeDup);
922 /* DeleteProcThreadAttributeList(lpAttributeList);
923 delete [] reinterpret_cast<char *>(lpAttributeList); */
926 m_Result.ExecutionEnd = Utility::GetTime();
927 m_Result.ExitStatus = 127;
928 m_Result.Output = "Command " + String(args) + " failed to execute: " + Utility::FormatErrorNumber(error);
933 Utility::QueueAsyncCallback(boost::bind(callback, m_Result));
940 /* DeleteProcThreadAttributeList(lpAttributeList);
941 delete [] reinterpret_cast<char *>(lpAttributeList); */
943 CloseHandle(outWritePipe);
944 CloseHandle(outWritePipeDup);
945 CloseHandle(pi.hThread);
947 m_Process = pi.hProcess;
949 m_PID = pi.dwProcessId;
951 Log(LogNotice, "Process")
952 << "Running command " << PrettyPrintArguments(m_Arguments) << ": PID " << m_PID;
958 if (pipe2(outfds, O_CLOEXEC) < 0) {
959 if (errno == ENOSYS) {
960 #endif /* HAVE_PIPE2 */
961 if (pipe(outfds) < 0) {
962 BOOST_THROW_EXCEPTION(posix_error()
963 << boost::errinfo_api_function("pipe")
964 << boost::errinfo_errno(errno));
967 Utility::SetCloExec(outfds[0]);
968 Utility::SetCloExec(outfds[1]);
971 BOOST_THROW_EXCEPTION(posix_error()
972 << boost::errinfo_api_function("pipe2")
973 << boost::errinfo_errno(errno));
976 #endif /* HAVE_PIPE2 */
979 fds[0] = STDIN_FILENO;
983 m_Process = ProcessSpawn(m_Arguments, m_ExtraEnvironment, m_AdjustPriority, fds);
987 m_OutputStream << "Fork failed with error code " << errno << " (" << Utility::FormatErrorNumber(errno) << ")";
988 Log(LogCritical, "Process", m_OutputStream.str());
991 Log(LogNotice, "Process")
992 << "Running command " << PrettyPrintArguments(m_Arguments) << ": PID " << m_PID;
994 (void)close(outfds[1]);
996 Utility::SetNonBlocking(outfds[0]);
1001 m_Callback = callback;
1006 boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
1007 l_Processes[tid][m_Process] = this;
1009 l_FDs[tid][m_FD] = m_Process;
1014 SetEvent(l_Events[tid]);
1016 if (write(l_EventFDs[tid][1], "T", 1) < 0 && errno != EINTR && errno != EAGAIN)
1017 Log(LogCritical, "base", "Write to event FD failed.");
1021 bool Process::DoEvents(void)
1023 bool is_timeout = false;
1025 bool could_not_kill = false;
1028 if (m_Timeout != 0) {
1029 double timeout = m_Result.ExecutionStart + m_Timeout;
1031 if (timeout < Utility::GetTime()) {
1032 Log(LogWarning, "Process")
1033 << "Killing process group " << m_PID << " (" << PrettyPrintArguments(m_Arguments)
1034 << ") after timeout of " << m_Timeout << " seconds";
1036 m_OutputStream << "<Timeout exceeded.>";
1038 TerminateProcess(m_Process, 1);
1040 int error = ProcessKill(-m_Process, SIGKILL);
1042 Log(LogWarning, "Process")
1043 << "Couldn't kill the process group " << m_PID << " (" << PrettyPrintArguments(m_Arguments)
1044 << "): [errno " << error << "] " << strerror(error);
1045 could_not_kill = true;
1055 m_ReadPending = false;
1058 if (!m_ReadFailed && GetOverlappedResult(m_FD, &m_Overlapped, &rc, TRUE) && rc > 0) {
1059 m_OutputStream.write(m_ReadBuffer, rc);
1065 int rc = read(m_FD, buffer, sizeof(buffer));
1067 if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
1071 m_OutputStream.write(buffer, rc);
1080 String output = m_OutputStream.str();
1083 WaitForSingleObject(m_Process, INFINITE);
1086 GetExitCodeProcess(m_Process, &exitcode);
1088 Log(LogNotice, "Process")
1089 << "PID " << m_PID << " (" << PrettyPrintArguments(m_Arguments) << ") terminated with exit code " << exitcode;
1091 int status, exitcode;
1092 if (could_not_kill || m_PID == -1) {
1094 } else if (ProcessWaitPID(m_Process, &status) != m_Process) {
1097 Log(LogWarning, "Process")
1098 << "PID " << m_PID << " (" << PrettyPrintArguments(m_Arguments) << ") died mysteriously: waitpid failed";
1099 } else if (WIFEXITED(status)) {
1100 exitcode = WEXITSTATUS(status);
1102 Log(LogNotice, "Process")
1103 << "PID " << m_PID << " (" << PrettyPrintArguments(m_Arguments) << ") terminated with exit code " << exitcode;
1104 } else if (WIFSIGNALED(status)) {
1105 int signum = WTERMSIG(status);
1106 const char *zsigname = strsignal(signum);
1108 String signame = Convert::ToString(signum);
1112 signame += zsigname;
1116 Log(LogWarning, "Process")
1117 << "PID " << m_PID << " was terminated by signal " << signame;
1119 std::ostringstream outputbuf;
1120 outputbuf << "<Terminated by signal " << signame << ".>";
1121 output = output + outputbuf.str();
1128 m_Result.PID = m_PID;
1129 m_Result.ExecutionEnd = Utility::GetTime();
1130 m_Result.ExitStatus = exitcode;
1131 m_Result.Output = output;
1134 Utility::QueueAsyncCallback(boost::bind(m_Callback, m_Result));
1139 pid_t Process::GetPID(void) const
1145 int Process::GetTID(void) const
1147 return (reinterpret_cast<uintptr_t>(this) / sizeof(void *)) % IOTHREADS;