]> granicus.if.org Git - icinga2/blob - lib/base/process.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / process.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
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>
16 #include <thread>
17 #include <iostream>
18
19 #ifndef _WIN32
20 #       include <execvpe.h>
21 #       include <poll.h>
22 #       include <string.h>
23
24 #       ifndef __APPLE__
25 extern char **environ;
26 #       else /* __APPLE__ */
27 #               include <crt_externs.h>
28 #               define environ (*_NSGetEnviron())
29 #       endif /* __APPLE__ */
30 #endif /* _WIN32 */
31
32 using namespace icinga;
33
34 #define IOTHREADS 4
35
36 static boost::mutex l_ProcessMutex[IOTHREADS];
37 static std::map<Process::ProcessHandle, Process::Ptr> l_Processes[IOTHREADS];
38 #ifdef _WIN32
39 static HANDLE l_Events[IOTHREADS];
40 #else /* _WIN32 */
41 static int l_EventFDs[IOTHREADS][2];
42 static std::map<Process::ConsoleHandle, Process::ProcessHandle> l_FDs[IOTHREADS];
43
44 static boost::mutex l_ProcessControlMutex;
45 static int l_ProcessControlFD = -1;
46 static pid_t l_ProcessControlPID;
47 #endif /* _WIN32 */
48 static boost::once_flag l_ProcessOnceFlag = BOOST_ONCE_INIT;
49 static boost::once_flag l_SpawnHelperOnceFlag = BOOST_ONCE_INIT;
50
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)
53 #ifdef _WIN32
54         , m_ReadPending(false), m_ReadFailed(false), m_Overlapped()
55 #endif /* _WIN32 */
56 {
57 #ifdef _WIN32
58         m_Overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
59 #endif /* _WIN32 */
60 }
61
62 Process::~Process()
63 {
64 #ifdef _WIN32
65         CloseHandle(m_Overlapped.hEvent);
66 #endif /* _WIN32 */
67 }
68
69 #ifndef _WIN32
70 static Value ProcessSpawnImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
71 {
72         struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
73
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;
76                 return Empty;
77         }
78
79         auto *fds = (int *)CMSG_DATA(cmsg);
80
81         Array::Ptr arguments = request->Get("arguments");
82         Dictionary::Ptr extraEnvironment = request->Get("extraEnvironment");
83         bool adjustPriority = request->Get("adjustPriority");
84
85         // build argv
86         auto **argv = new char *[arguments->GetLength() + 1];
87
88         for (unsigned int i = 0; i < arguments->GetLength(); i++) {
89                 String arg = arguments->Get(i);
90                 argv[i] = strdup(arg.CStr());
91         }
92
93         argv[arguments->GetLength()] = nullptr;
94
95         // build envp
96         int envc = 0;
97
98         /* count existing environment variables */
99         while (environ[envc])
100                 envc++;
101
102         auto **envp = new char *[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0) + 2];
103
104         for (int i = 0; i < envc; i++)
105                 envp[i] = strdup(environ[i]);
106
107         if (extraEnvironment) {
108                 ObjectLock olock(extraEnvironment);
109
110                 int index = envc;
111                 for (const Dictionary::Pair& kv : extraEnvironment) {
112                         String skv = kv.first + "=" + Convert::ToString(kv.second);
113                         envp[index] = strdup(skv.CStr());
114                         index++;
115                 }
116         }
117
118         envp[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0)] = strdup("LC_NUMERIC=C");
119         envp[envc + (extraEnvironment ? extraEnvironment->GetLength() : 0) + 1] = nullptr;
120
121         extraEnvironment.reset();
122
123         pid_t pid = fork();
124
125         int errorCode = 0;
126
127         if (pid < 0)
128                 errorCode = errno;
129
130         if (pid == 0) {
131                 // child process
132
133                 (void)close(l_ProcessControlFD);
134
135                 if (setsid() < 0) {
136                         perror("setsid() failed");
137                         _exit(128);
138                 }
139
140                 if (dup2(fds[0], STDIN_FILENO) < 0 || dup2(fds[1], STDOUT_FILENO) < 0 || dup2(fds[2], STDERR_FILENO) < 0) {
141                         perror("dup2() failed");
142                         _exit(128);
143                 }
144
145                 (void)close(fds[0]);
146                 (void)close(fds[1]);
147                 (void)close(fds[2]);
148
149 #ifdef HAVE_NICE
150                 if (adjustPriority) {
151                         // Cheating the compiler on "warning: ignoring return value of 'int nice(int)', declared with attribute warn_unused_result [-Wunused-result]".
152                         auto x (nice(5));
153                         (void)x;
154                 }
155 #endif /* HAVE_NICE */
156
157                 sigset_t mask;
158                 sigemptyset(&mask);
159                 sigprocmask(SIG_SETMASK, &mask, nullptr);
160
161                 if (icinga2_execvpe(argv[0], argv, envp) < 0) {
162                         char errmsg[512];
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';
167                         perror(errmsg);
168                         _exit(128);
169                 }
170
171                 _exit(128);
172         }
173
174         (void)close(fds[0]);
175         (void)close(fds[1]);
176         (void)close(fds[2]);
177
178         // free arguments
179         for (int i = 0; argv[i]; i++)
180                 free(argv[i]);
181
182         delete[] argv;
183
184         // free environment
185         for (int i = 0; envp[i]; i++)
186                 free(envp[i]);
187
188         delete[] envp;
189
190         Dictionary::Ptr response = new Dictionary({
191                 { "rc", pid },
192                 { "errno", errorCode }
193         });
194
195         return response;
196 }
197
198 static Value ProcessKillImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
199 {
200         pid_t pid = request->Get("pid");
201         int signum = request->Get("signum");
202
203         errno = 0;
204         kill(pid, signum);
205         int error = errno;
206
207         Dictionary::Ptr response = new Dictionary({
208                 { "errno", error }
209         });
210
211         return response;
212 }
213
214 static Value ProcessWaitPIDImpl(struct msghdr *msgh, const Dictionary::Ptr& request)
215 {
216         pid_t pid = request->Get("pid");
217
218         int status;
219         int rc = waitpid(pid, &status, 0);
220
221         Dictionary::Ptr response = new Dictionary({
222                 { "status", status },
223                 { "rc", rc }
224         });
225
226         return response;
227 }
228
229 static void ProcessHandler()
230 {
231         sigset_t mask;
232         sigfillset(&mask);
233         sigprocmask(SIG_SETMASK, &mask, nullptr);
234
235         rlimit rl;
236         if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) {
237                 rlim_t maxfds = rl.rlim_max;
238
239                 if (maxfds == RLIM_INFINITY)
240                         maxfds = 65536;
241
242                 for (rlim_t i = 3; i < maxfds; i++)
243                         if (i != static_cast<rlim_t>(l_ProcessControlFD))
244                                 (void)close(i);
245         }
246
247         for (;;) {
248                 size_t length;
249
250                 struct msghdr msg;
251                 memset(&msg, 0, sizeof(msg));
252
253                 struct iovec io;
254                 io.iov_base = &length;
255                 io.iov_len = sizeof(length);
256
257                 msg.msg_iov = &io;
258                 msg.msg_iovlen = 1;
259
260                 char cbuf[4096];
261                 msg.msg_control = cbuf;
262                 msg.msg_controllen = sizeof(cbuf);
263
264                 int rc = recvmsg(l_ProcessControlFD, &msg, 0);
265
266                 if (rc <= 0) {
267                         if (rc < 0 && (errno == EINTR || errno == EAGAIN))
268                                 continue;
269
270                         break;
271                 }
272
273                 auto *mbuf = new char[length];
274
275                 size_t count = 0;
276                 while (count < length) {
277                         rc = recv(l_ProcessControlFD, mbuf + count, length - count, 0);
278
279                         if (rc <= 0) {
280                                 if (rc < 0 && (errno == EINTR || errno == EAGAIN))
281                                         continue;
282
283                                 delete [] mbuf;
284
285                                 _exit(0);
286                         }
287
288                         count += rc;
289
290                         if (rc == 0)
291                                 break;
292                 }
293
294                 String jrequest = String(mbuf, mbuf + count);
295
296                 delete [] mbuf;
297
298                 Dictionary::Ptr request = JsonDecode(jrequest);
299
300                 String command = request->Get("command");
301
302                 Value response;
303
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);
310                 else
311                         response = Empty;
312
313                 String jresponse = JsonEncode(response);
314
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));
319                 }
320         }
321
322         _exit(0);
323 }
324
325 static void StartSpawnProcessHelper()
326 {
327         if (l_ProcessControlFD != -1) {
328                 (void)close(l_ProcessControlFD);
329
330                 int status;
331                 (void)waitpid(l_ProcessControlPID, &status, 0);
332         }
333
334         int controlFDs[2];
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));
339         }
340
341         pid_t pid = fork();
342
343         if (pid < 0) {
344                 BOOST_THROW_EXCEPTION(posix_error()
345                         << boost::errinfo_api_function("fork")
346                         << boost::errinfo_errno(errno));
347         }
348
349         if (pid == 0) {
350                 (void)close(controlFDs[1]);
351
352                 l_ProcessControlFD = controlFDs[0];
353
354                 ProcessHandler();
355
356                 _exit(1);
357         }
358
359         (void)close(controlFDs[0]);
360
361         l_ProcessControlFD = controlFDs[1];
362         l_ProcessControlPID = pid;
363 }
364
365 static pid_t ProcessSpawn(const std::vector<String>& arguments, const Dictionary::Ptr& extraEnvironment, bool adjustPriority, int fds[3])
366 {
367         Dictionary::Ptr request = new Dictionary({
368                 { "command", "spawn" },
369                 { "arguments", Array::FromVector(arguments) },
370                 { "extraEnvironment", extraEnvironment },
371                 { "adjustPriority", adjustPriority }
372         });
373
374         String jrequest = JsonEncode(request);
375         size_t length = jrequest.GetLength();
376
377         boost::mutex::scoped_lock lock(l_ProcessControlMutex);
378
379         struct msghdr msg;
380         memset(&msg, 0, sizeof(msg));
381
382         struct iovec io;
383         io.iov_base = &length;
384         io.iov_len = sizeof(length);
385
386         msg.msg_iov = &io;
387         msg.msg_iovlen = 1;
388
389         char cbuf[CMSG_SPACE(sizeof(int) * 3)];
390         msg.msg_control = cbuf;
391         msg.msg_controllen = sizeof(cbuf);
392
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);
397
398         memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * 3);
399
400         msg.msg_controllen = cmsg->cmsg_len;
401
402         do {
403                 while (sendmsg(l_ProcessControlFD, &msg, 0) < 0) {
404                         StartSpawnProcessHelper();
405                 }
406         } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0);
407
408         char buf[4096];
409
410         ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
411
412         if (rc <= 0)
413                 return -1;
414
415         String jresponse = String(buf, buf + rc);
416
417         Dictionary::Ptr response = JsonDecode(jresponse);
418
419         if (response->Get("rc") == -1)
420                 errno = response->Get("errno");
421
422         return response->Get("rc");
423 }
424
425 static int ProcessKill(pid_t pid, int signum)
426 {
427         Dictionary::Ptr request = new Dictionary({
428                 { "command", "kill" },
429                 { "pid", pid },
430                 { "signum", signum }
431         });
432
433         String jrequest = JsonEncode(request);
434         size_t length = jrequest.GetLength();
435
436         boost::mutex::scoped_lock lock(l_ProcessControlMutex);
437
438         do {
439                 while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0) {
440                         StartSpawnProcessHelper();
441                 }
442         } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0);
443
444         char buf[4096];
445
446         ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
447
448         if (rc <= 0)
449                 return -1;
450
451         String jresponse = String(buf, buf + rc);
452
453         Dictionary::Ptr response = JsonDecode(jresponse);
454         return response->Get("errno");
455 }
456
457 static int ProcessWaitPID(pid_t pid, int *status)
458 {
459         Dictionary::Ptr request = new Dictionary({
460                 { "command", "waitpid" },
461                 { "pid", pid }
462         });
463
464         String jrequest = JsonEncode(request);
465         size_t length = jrequest.GetLength();
466
467         boost::mutex::scoped_lock lock(l_ProcessControlMutex);
468
469         do {
470                 while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0) {
471                         StartSpawnProcessHelper();
472                 }
473         } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0);
474
475         char buf[4096];
476
477         ssize_t rc = recv(l_ProcessControlFD, buf, sizeof(buf), 0);
478
479         if (rc <= 0)
480                 return -1;
481
482         String jresponse = String(buf, buf + rc);
483
484         Dictionary::Ptr response = JsonDecode(jresponse);
485         *status = response->Get("status");
486         return response->Get("rc");
487 }
488
489 void Process::InitializeSpawnHelper()
490 {
491         if (l_ProcessControlFD == -1)
492                 StartSpawnProcessHelper();
493 }
494 #endif /* _WIN32 */
495
496 static void InitializeProcess()
497 {
498 #ifdef _WIN32
499         for (auto& event : l_Events) {
500                 event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
501         }
502 #else /* _WIN32 */
503         for (auto& eventFD : l_EventFDs) {
504 #       ifdef HAVE_PIPE2
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));
512                                 }
513
514                                 Utility::SetCloExec(eventFD[0]);
515                                 Utility::SetCloExec(eventFD[1]);
516 #       ifdef HAVE_PIPE2
517                         } else {
518                                 BOOST_THROW_EXCEPTION(posix_error()
519                                         << boost::errinfo_api_function("pipe2")
520                                         << boost::errinfo_errno(errno));
521                         }
522                 }
523 #       endif /* HAVE_PIPE2 */
524         }
525 #endif /* _WIN32 */
526 }
527
528 INITIALIZE_ONCE(InitializeProcess);
529
530 void Process::ThreadInitialize()
531 {
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));
535                 t.detach();
536         }
537 }
538
539 Process::Arguments Process::PrepareCommand(const Value& command)
540 {
541 #ifdef _WIN32
542         String args;
543 #else /* _WIN32 */
544         std::vector<String> args;
545 #endif /* _WIN32 */
546
547         if (command.IsObjectType<Array>()) {
548                 Array::Ptr arguments = command;
549
550                 ObjectLock olock(arguments);
551                 for (const Value& argument : arguments) {
552 #ifdef _WIN32
553                         if (args != "")
554                                 args += " ";
555
556                         args += Utility::EscapeCreateProcessArg(argument);
557 #else /* _WIN32 */
558                         args.push_back(argument);
559 #endif /* _WIN32 */
560                 }
561
562                 return args;
563         }
564
565 #ifdef _WIN32
566         return command;
567 #else /* _WIN32 */
568         return { "sh", "-c", command };
569 #endif
570 }
571
572 void Process::SetTimeout(double timeout)
573 {
574         m_Timeout = timeout;
575 }
576
577 double Process::GetTimeout() const
578 {
579         return m_Timeout;
580 }
581
582 void Process::SetAdjustPriority(bool adjust)
583 {
584         m_AdjustPriority = adjust;
585 }
586
587 bool Process::GetAdjustPriority() const
588 {
589         return m_AdjustPriority;
590 }
591
592 void Process::IOThreadProc(int tid)
593 {
594 #ifdef _WIN32
595         HANDLE *handles = nullptr;
596         HANDLE *fhandles = nullptr;
597 #else /* _WIN32 */
598         pollfd *pfds = nullptr;
599 #endif /* _WIN32 */
600         int count = 0;
601         double now;
602
603         Utility::SetThreadName("ProcessIO");
604
605         for (;;) {
606                 double timeout = -1;
607
608                 now = Utility::GetTime();
609
610                 {
611                         boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
612
613                         count = 1 + l_Processes[tid].size();
614 #ifdef _WIN32
615                         handles = reinterpret_cast<HANDLE *>(realloc(handles, sizeof(HANDLE) * count));
616                         fhandles = reinterpret_cast<HANDLE *>(realloc(fhandles, sizeof(HANDLE) * count));
617
618                         fhandles[0] = l_Events[tid];
619
620 #else /* _WIN32 */
621                         pfds = reinterpret_cast<pollfd *>(realloc(pfds, sizeof(pollfd) * count));
622
623                         pfds[0].fd = l_EventFDs[tid][0];
624                         pfds[0].events = POLLIN;
625                         pfds[0].revents = 0;
626 #endif /* _WIN32 */
627
628                         int i = 1;
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;
632 #ifdef _WIN32
633                                 handles[i] = kv.first;
634
635                                 if (!process->m_ReadPending) {
636                                         process->m_ReadPending = true;
637
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);
642                                         }
643                                 }
644
645                                 fhandles[i] = process->m_Overlapped.hEvent;
646 #else /* _WIN32 */
647                                 pfds[i].fd = process->m_FD;
648                                 pfds[i].events = POLLIN;
649                                 pfds[i].revents = 0;
650 #endif /* _WIN32 */
651
652                                 if (process->m_Timeout != 0) {
653                                         double delta = process->m_Timeout - (now - process->m_Result.ExecutionStart);
654
655                                         if (timeout == -1 || delta < timeout)
656                                                 timeout = delta;
657                                 }
658
659                                 i++;
660                         }
661                 }
662
663                 if (timeout < 0.01)
664                         timeout = 0.5;
665
666                 timeout *= 1000;
667
668 #ifdef _WIN32
669                 DWORD rc = WaitForMultipleObjects(count, fhandles, FALSE, timeout == -1 ? INFINITE : static_cast<DWORD>(timeout));
670 #else /* _WIN32 */
671                 int rc = poll(pfds, count, timeout);
672
673                 if (rc < 0)
674                         continue;
675 #endif /* _WIN32 */
676
677                 now = Utility::GetTime();
678
679                 {
680                         boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
681
682 #ifdef _WIN32
683                         if (rc == WAIT_OBJECT_0)
684                                 ResetEvent(l_Events[tid]);
685 #else /* _WIN32 */
686                         if (pfds[0].revents & (POLLIN | POLLHUP | POLLERR)) {
687                                 char buffer[512];
688                                 if (read(l_EventFDs[tid][0], buffer, sizeof(buffer)) < 0)
689                                         Log(LogCritical, "base", "Read from event FD failed.");
690                         }
691 #endif /* _WIN32 */
692
693                         for (int i = 1; i < count; i++) {
694 #ifdef _WIN32
695                                 auto it = l_Processes[tid].find(handles[i]);
696 #else /* _WIN32 */
697                                 auto it2 = l_FDs[tid].find(pfds[i].fd);
698
699                                 if (it2 == l_FDs[tid].end())
700                                         continue; /* This should never happen. */
701
702                                 auto it = l_Processes[tid].find(it2->second);
703 #endif /* _WIN32 */
704
705                                 if (it == l_Processes[tid].end())
706                                         continue; /* This should never happen. */
707
708                                 bool is_timeout = false;
709
710                                 if (it->second->m_Timeout != 0) {
711                                         double timeout = it->second->m_Result.ExecutionStart + it->second->m_Timeout;
712
713                                         if (timeout < now)
714                                                 is_timeout = true;
715                                 }
716
717 #ifdef _WIN32
718                                 if (rc == WAIT_OBJECT_0 + i || is_timeout) {
719 #else /* _WIN32 */
720                                 if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR) || is_timeout) {
721 #endif /* _WIN32 */
722                                         if (!it->second->DoEvents()) {
723 #ifdef _WIN32
724                                                 CloseHandle(it->first);
725                                                 CloseHandle(it->second->m_FD);
726 #else /* _WIN32 */
727                                                 l_FDs[tid].erase(it->second->m_FD);
728                                                 (void)close(it->second->m_FD);
729 #endif /* _WIN32 */
730                                                 l_Processes[tid].erase(it);
731                                         }
732                                 }
733                         }
734                 }
735         }
736 }
737
738 String Process::PrettyPrintArguments(const Process::Arguments& arguments)
739 {
740 #ifdef _WIN32
741         return "'" + arguments + "'";
742 #else /* _WIN32 */
743         return "'" + boost::algorithm::join(arguments, "' '") + "'";
744 #endif /* _WIN32 */
745 }
746
747 #ifdef _WIN32
748 static BOOL CreatePipeOverlapped(HANDLE *outReadPipe, HANDLE *outWritePipe,
749         SECURITY_ATTRIBUTES *securityAttributes, DWORD size, DWORD readMode, DWORD writeMode)
750 {
751         static LONG pipeIndex = 0;
752
753         if (size == 0)
754                 size = 8192;
755
756         LONG currentIndex = InterlockedIncrement(&pipeIndex);
757
758         char pipeName[128];
759         sprintf(pipeName, "\\\\.\\Pipe\\OverlappedPipe.%d.%d", (int)GetCurrentProcessId(), (int)currentIndex);
760
761         *outReadPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_INBOUND | readMode,
762                 PIPE_TYPE_BYTE | PIPE_WAIT, 1, size, size, 60 * 1000, securityAttributes);
763
764         if (*outReadPipe == INVALID_HANDLE_VALUE)
765                 return FALSE;
766
767         *outWritePipe = CreateFile(pipeName, GENERIC_WRITE, 0, securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | writeMode, nullptr);
768
769         if (*outWritePipe == INVALID_HANDLE_VALUE) {
770                 DWORD error = GetLastError();
771                 CloseHandle(*outReadPipe);
772                 SetLastError(error);
773                 return FALSE;
774         }
775
776         return TRUE;
777 }
778 #endif /* _WIN32 */
779
780 void Process::Run(const std::function<void(const ProcessResult&)>& callback)
781 {
782 #ifndef _WIN32
783         boost::call_once(l_SpawnHelperOnceFlag, &Process::InitializeSpawnHelper);
784 #endif /* _WIN32 */
785         boost::call_once(l_ProcessOnceFlag, &Process::ThreadInitialize);
786
787         m_Result.ExecutionStart = Utility::GetTime();
788
789 #ifdef _WIN32
790         SECURITY_ATTRIBUTES sa = {};
791         sa.nLength = sizeof(sa);
792         sa.bInheritHandle = TRUE;
793
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()));
799
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()));
804
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()));
811
812 /*      LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
813         SIZE_T cbSize;
814
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()));
819
820         lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(new char[cbSize]);
821
822         if (!InitializeProcThreadAttributeList(lpAttributeList, 1, 0, &cbSize))
823                 BOOST_THROW_EXCEPTION(win32_error()
824                 << boost::errinfo_api_function("InitializeProcThreadAttributeList")
825                 << errinfo_win32_error(GetLastError()));
826
827         HANDLE rgHandles[3];
828         rgHandles[0] = outWritePipe;
829         rgHandles[1] = outWritePipeDup;
830         rgHandles[2] = GetStdHandle(STD_INPUT_HANDLE);
831
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()));
837 */
838
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;
846
847         PROCESS_INFORMATION pi;
848
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';
852
853         LPCH pEnvironment = GetEnvironmentStrings();
854         size_t ioffset = 0, offset = 0;
855
856         char *envp = nullptr;
857
858         for (;;) {
859                 size_t len = strlen(pEnvironment + ioffset);
860
861                 if (len == 0)
862                         break;
863
864                 char *eqp = strchr(pEnvironment + ioffset, '=');
865                 if (eqp && m_ExtraEnvironment && m_ExtraEnvironment->Contains(String(pEnvironment + ioffset, eqp))) {
866                         ioffset += len + 1;
867                         continue;
868                 }
869
870                 envp = static_cast<char *>(realloc(envp, offset + len + 1));
871
872                 if (!envp)
873                         BOOST_THROW_EXCEPTION(std::bad_alloc());
874
875                 strcpy(envp + offset, pEnvironment + ioffset);
876                 offset += len + 1;
877                 ioffset += len + 1;
878         }
879
880         FreeEnvironmentStrings(pEnvironment);
881
882         if (m_ExtraEnvironment) {
883                 ObjectLock olock(m_ExtraEnvironment);
884
885                 for (const Dictionary::Pair& kv : m_ExtraEnvironment) {
886                         String skv = kv.first + "=" + Convert::ToString(kv.second);
887
888                         envp = static_cast<char *>(realloc(envp, offset + skv.GetLength() + 1));
889
890                         if (!envp)
891                                 BOOST_THROW_EXCEPTION(std::bad_alloc());
892
893                         strcpy(envp + offset, skv.CStr());
894                         offset += skv.GetLength() + 1;
895                 }
896         }
897
898         envp = static_cast<char *>(realloc(envp, offset + 1));
899
900         if (!envp)
901                 BOOST_THROW_EXCEPTION(std::bad_alloc());
902
903         envp[offset] = '\0';
904
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);
910                 free(envp);
911 /*              DeleteProcThreadAttributeList(lpAttributeList);
912                 delete [] reinterpret_cast<char *>(lpAttributeList); */
913
914                 m_Result.PID = 0;
915                 m_Result.ExecutionEnd = Utility::GetTime();
916                 m_Result.ExitStatus = 127;
917                 m_Result.Output = "Command " + String(args) + " failed to execute: " + Utility::FormatErrorNumber(error);
918
919                 delete [] args;
920
921                 if (callback)
922                         Utility::QueueAsyncCallback(std::bind(callback, m_Result));
923
924                 return;
925         }
926
927         delete [] args;
928         free(envp);
929 /*      DeleteProcThreadAttributeList(lpAttributeList);
930         delete [] reinterpret_cast<char *>(lpAttributeList); */
931
932         CloseHandle(outWritePipe);
933         CloseHandle(outWritePipeDup);
934         CloseHandle(pi.hThread);
935
936         m_Process = pi.hProcess;
937         m_FD = outReadPipe;
938         m_PID = pi.dwProcessId;
939
940         Log(LogNotice, "Process")
941                 << "Running command " << PrettyPrintArguments(m_Arguments) << ": PID " << m_PID;
942
943 #else /* _WIN32 */
944         int outfds[2];
945
946 #ifdef HAVE_PIPE2
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));
954                         }
955
956                         Utility::SetCloExec(outfds[0]);
957                         Utility::SetCloExec(outfds[1]);
958 #ifdef HAVE_PIPE2
959                 } else {
960                         BOOST_THROW_EXCEPTION(posix_error()
961                                 << boost::errinfo_api_function("pipe2")
962                                 << boost::errinfo_errno(errno));
963                 }
964         }
965 #endif /* HAVE_PIPE2 */
966
967         int fds[3];
968         fds[0] = STDIN_FILENO;
969         fds[1] = outfds[1];
970         fds[2] = outfds[1];
971
972         m_Process = ProcessSpawn(m_Arguments, m_ExtraEnvironment, m_AdjustPriority, fds);
973         m_PID = m_Process;
974
975         if (m_PID == -1) {
976                 m_OutputStream << "Fork failed with error code " << errno << " (" << Utility::FormatErrorNumber(errno) << ")";
977                 Log(LogCritical, "Process", m_OutputStream.str());
978         }
979
980         Log(LogNotice, "Process")
981                 << "Running command " << PrettyPrintArguments(m_Arguments) << ": PID " << m_PID;
982
983         (void)close(outfds[1]);
984
985         Utility::SetNonBlocking(outfds[0]);
986
987         m_FD = outfds[0];
988 #endif /* _WIN32 */
989
990         m_Callback = callback;
991
992         int tid = GetTID();
993
994         {
995                 boost::mutex::scoped_lock lock(l_ProcessMutex[tid]);
996                 l_Processes[tid][m_Process] = this;
997 #ifndef _WIN32
998                 l_FDs[tid][m_FD] = m_Process;
999 #endif /* _WIN32 */
1000         }
1001
1002 #ifdef _WIN32
1003         SetEvent(l_Events[tid]);
1004 #else /* _WIN32 */
1005         if (write(l_EventFDs[tid][1], "T", 1) < 0 && errno != EINTR && errno != EAGAIN)
1006                 Log(LogCritical, "base", "Write to event FD failed.");
1007 #endif /* _WIN32 */
1008 }
1009
1010 bool Process::DoEvents()
1011 {
1012         bool is_timeout = false;
1013 #ifndef _WIN32
1014         bool could_not_kill = false;
1015 #endif /* _WIN32 */
1016
1017         if (m_Timeout != 0) {
1018                 double timeout = m_Result.ExecutionStart + m_Timeout;
1019
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";
1024
1025                         m_OutputStream << "<Timeout exceeded.>";
1026 #ifdef _WIN32
1027                         TerminateProcess(m_Process, 1);
1028 #else /* _WIN32 */
1029                         int error = ProcessKill(-m_Process, SIGKILL);
1030                         if (error) {
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;
1035                         }
1036 #endif /* _WIN32 */
1037
1038                         is_timeout = true;
1039                 }
1040         }
1041
1042         if (!is_timeout) {
1043 #ifdef _WIN32
1044                 m_ReadPending = false;
1045
1046                 DWORD rc;
1047                 if (!m_ReadFailed && GetOverlappedResult(m_FD, &m_Overlapped, &rc, TRUE) && rc > 0) {
1048                         m_OutputStream.write(m_ReadBuffer, rc);
1049                         return true;
1050                 }
1051 #else /* _WIN32 */
1052                 char buffer[512];
1053                 for (;;) {
1054                         int rc = read(m_FD, buffer, sizeof(buffer));
1055
1056                         if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
1057                                 return true;
1058
1059                         if (rc > 0) {
1060                                 m_OutputStream.write(buffer, rc);
1061                                 continue;
1062                         }
1063
1064                         break;
1065                 }
1066 #endif /* _WIN32 */
1067         }
1068
1069         String output = m_OutputStream.str();
1070
1071 #ifdef _WIN32
1072         WaitForSingleObject(m_Process, INFINITE);
1073
1074         DWORD exitcode;
1075         GetExitCodeProcess(m_Process, &exitcode);
1076
1077         Log(LogNotice, "Process")
1078                 << "PID " << m_PID << " (" << PrettyPrintArguments(m_Arguments) << ") terminated with exit code " << exitcode;
1079 #else /* _WIN32 */
1080         int status, exitcode;
1081         if (could_not_kill || m_PID == -1) {
1082                 exitcode = 128;
1083         } else if (ProcessWaitPID(m_Process, &status) != m_Process) {
1084                 exitcode = 128;
1085
1086                 Log(LogWarning, "Process")
1087                         << "PID " << m_PID << " (" << PrettyPrintArguments(m_Arguments) << ") died mysteriously: waitpid failed";
1088         } else if (WIFEXITED(status)) {
1089                 exitcode = WEXITSTATUS(status);
1090
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);
1096
1097                 String signame = Convert::ToString(signum);
1098
1099                 if (zsigname) {
1100                         signame += " (";
1101                         signame += zsigname;
1102                         signame += ")";
1103                 }
1104
1105                 Log(LogWarning, "Process")
1106                         << "PID " << m_PID << " was terminated by signal " << signame;
1107
1108                 std::ostringstream outputbuf;
1109                 outputbuf << "<Terminated by signal " << signame << ".>";
1110                 output = output + outputbuf.str();
1111                 exitcode = 128;
1112         } else {
1113                 exitcode = 128;
1114         }
1115 #endif /* _WIN32 */
1116
1117         m_Result.PID = m_PID;
1118         m_Result.ExecutionEnd = Utility::GetTime();
1119         m_Result.ExitStatus = exitcode;
1120         m_Result.Output = output;
1121
1122         if (m_Callback)
1123                 Utility::QueueAsyncCallback(std::bind(m_Callback, m_Result));
1124
1125         return false;
1126 }
1127
1128 pid_t Process::GetPID() const
1129 {
1130         return m_PID;
1131 }
1132
1133
1134 int Process::GetTID() const
1135 {
1136         return (reinterpret_cast<uintptr_t>(this) / sizeof(void *)) % IOTHREADS;
1137 }
1138