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