29bc11bb3b04cb16ac4e2d4417364338a58fe3ea
[ardour.git] / libs / pbd / system_exec.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3     Copyright (C) 2010-2014 Robin Gareus <robin@gareus.org>
4     Copyright (C) 2005-2008 Lennart Poettering
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <algorithm>
27
28 #include <assert.h>
29
30 #ifndef COMPILER_MSVC
31 #include <dirent.h>
32 #endif
33
34 #ifdef PLATFORM_WINDOWS
35 #include <windows.h>
36 #else
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <sys/socket.h>
41 #include <sys/ioctl.h>
42 #include <sys/time.h>
43 #include <sys/resource.h>
44 #endif
45
46 #include <glibmm/miscutils.h>
47
48 #include "pbd/file_utils.h"
49 #include "pbd/search_path.h"
50 #include "pbd/system_exec.h"
51
52 using namespace std;
53 using namespace PBD;
54
55 static void * interposer_thread (void *arg);
56
57 #ifndef PLATFORM_WINDOWS /* POSIX Process only */
58 static void close_fd (int& fd) { if (fd >= 0) ::close (fd); fd = -1; }
59 #endif
60
61 #if (!defined PLATFORM_WINDOWS && defined NO_VFORK)
62 /*
63  * This function was part of libasyncns.
64  * LGPL v2.1
65  * Copyright 2005-2008 Lennart Poettering
66  */
67 static int close_allv(const int except_fds[]) {
68         struct rlimit rl;
69         int fd;
70
71 #ifdef __linux__
72
73         DIR *d;
74
75         assert(except_fds);
76
77         if ((d = opendir("/proc/self/fd"))) {
78                 struct dirent *de;
79
80                 while ((de = readdir(d))) {
81                         int found;
82                         long l;
83                         char *e = NULL;
84                         int i;
85
86                         if (de->d_name[0] == '.')
87                                         continue;
88
89                         errno = 0;
90                         l = strtol(de->d_name, &e, 10);
91                         if (errno != 0 || !e || *e) {
92                                 closedir(d);
93                                 errno = EINVAL;
94                                 return -1;
95                         }
96
97                         fd = (int) l;
98
99                         if ((long) fd != l) {
100                                 closedir(d);
101                                 errno = EINVAL;
102                                 return -1;
103                         }
104
105                         if (fd < 3)
106                                 continue;
107
108                         if (fd == dirfd(d))
109                                 continue;
110
111                         found = 0;
112                         for (i = 0; except_fds[i] >= 0; i++)
113                                 if (except_fds[i] == fd) {
114                                                 found = 1;
115                                                 break;
116                                 }
117
118                         if (found) continue;
119
120                         if (close(fd) < 0) {
121                                 int saved_errno;
122
123                                 saved_errno = errno;
124                                 closedir(d);
125                                 errno = saved_errno;
126
127                                 return -1;
128                         }
129                 }
130
131                 closedir(d);
132                 return 0;
133         }
134
135 #endif
136
137         if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
138                 return -1;
139
140         for (fd = 0; fd < (int) rl.rlim_max; fd++) {
141                 int i;
142
143                 if (fd <= 3)
144                                 continue;
145
146                 for (i = 0; except_fds[i] >= 0; i++)
147                         if (except_fds[i] == fd)
148                                 continue;
149
150                 if (close(fd) < 0 && errno != EBADF)
151                         return -1;
152         }
153
154         return 0;
155 }
156 #endif /* not on windows, nor vfork */
157
158 void
159 SystemExec::init ()
160 {
161         pthread_mutex_init (&write_lock, NULL);
162         thread_active = false;
163         pid = 0;
164         pin[1] = -1;
165         nicelevel = 0;
166         envp = NULL;
167 #ifdef PLATFORM_WINDOWS
168         stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
169         stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
170         stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
171         w_args = NULL;
172 #elif !defined NO_VFORK
173         argx = NULL;
174 #endif
175 }
176
177 SystemExec::SystemExec (std::string c, std::string a)
178         : cmd(c)
179 {
180         init ();
181
182         argp = NULL;
183         make_envp();
184         make_argp(a);
185 }
186
187 SystemExec::SystemExec (std::string c, char **a)
188         : cmd(c) , argp(a)
189 {
190         init ();
191
192 #ifdef PLATFORM_WINDOWS
193         make_wargs(a);
194 #endif
195         make_envp();
196 }
197
198 SystemExec::SystemExec (std::string command, const std::map<char, std::string> subs)
199 {
200         init ();
201         make_argp_escaped(command, subs);
202
203 #ifdef PLATFORM_WINDOWS
204         if (argp[0] && strlen (argp[0]) > 0) {
205                 std::string wa = argp[0];
206                 // only add quotes to command if required..
207                 if (argp[0][0] != '"'
208                                 && argp[0][strlen(argp[0])-1] != '"'
209                                 && strchr(argp[0], ' ')) {
210                         wa = "\"";
211                         wa += argp[0];
212                         wa += "\"";
213                 }
214                 // ...but always quote all args
215                 for (int i = 1; argp[i]; ++i) {
216                         std::string tmp (argp[i]);
217                         while (tmp.find("\"") != std::string::npos)
218                                 tmp.replace(tmp.find("\""), 1, "\\\"");
219                         wa += " \"";
220                         wa += tmp;
221                         wa += '"';
222                 }
223                 w_args = strdup(wa.c_str());
224         }
225 #else
226         if (find_file (Searchpath (Glib::getenv ("PATH")), argp[0], cmd)) {
227                 // argp[0] exists in $PATH` - set it to the actual path where it was found
228                 free (argp[0]);
229                 argp[0] = strdup(cmd.c_str ());
230         }
231         // else argp[0] not found in path - leave it as-is, it might be an absolute path
232
233         // Glib::find_program_in_path () is only available in Glib >= 2.28
234         // cmd = Glib::find_program_in_path (argp[0]);
235 #endif
236         make_envp();
237 }
238
239 char*
240 SystemExec::format_key_value_parameter (std::string key, std::string value)
241 {
242         size_t start_pos = 0;
243         std::string v1 = value;
244         while((start_pos = v1.find_first_not_of(
245                         "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789(),.\"'",
246                         start_pos)) != std::string::npos)
247         {
248                 v1.replace(start_pos, 1, "_");
249                 start_pos += 1;
250         }
251
252         start_pos = 0;
253         while((start_pos = v1.find("\"", start_pos)) != std::string::npos) {
254                 v1.replace(start_pos, 1, "\\\"");
255                 start_pos += 2;
256         }
257
258         size_t len = key.length() + v1.length() + 4;
259         char *mds = (char*) calloc(len, sizeof(char));
260 #ifdef PLATFORM_WINDOWS
261         /* SystemExec::make_wargs() adds quotes around the complete argument
262          * windows uses CreateProcess() with a parameter string
263          * (and not an array list of separate arguments)
264          */
265         snprintf(mds, len, "%s=%s", key.c_str(), v1.c_str());
266 #else
267         snprintf(mds, len, "%s=\"%s\"", key.c_str(), v1.c_str());
268 #endif
269         return mds;
270 }
271
272 void
273 SystemExec::make_argp_escaped (std::string command, const std::map<char, std::string> subs)
274 {
275
276         int inquotes = 0;
277         int n = 0;
278         size_t i = 0;
279         std::string arg = "";
280
281         argp = (char**) malloc (sizeof(char*));
282
283         for (i = 0; i <= command.length(); i++) { // include terminating '\0'
284                 char c = command.c_str()[i];
285                 if (inquotes) {
286                         if (c == '"') {
287                                 inquotes = 0;
288                         } else {
289                                 // still in quotes - just copy
290                                 arg += c;
291                         }
292                 } else switch (c) {
293                         case '%' :
294                                 c = command.c_str()[++i];
295                                 if (c == '%' || c == '\0') {
296                                         // "%%", "%" at end-of-string => "%"
297                                         arg += '%';
298                                 } else {
299                                         // search subs for string to substitute for char
300                                         std::map<char, std::string>::const_iterator s = subs.find(c);
301                                         if (s != subs.end()) {
302                                                 // found substitution
303                                                 arg += s->second;
304                                         } else {
305                                                 // not a valid substitution, just copy
306                                                 arg += '%';
307                                                 arg += c;
308                                         }
309                                 }
310                                 break;
311                         case '\\':
312                                 c = command.c_str()[++i];
313                                 switch (c) {
314                                         case ' ' :
315                                         case '"' : arg += c; break; // "\\", "\" at end-of-string => "\"
316                                         case '\0':
317                                         case '\\': arg += '\\'; break;
318                                         default  : arg += '\\'; arg += c; break;
319                                 }
320                                 break;
321                         case '"' :
322                                 inquotes = 1;
323                                 break;
324                         case ' ' :
325                         case '\t':
326                         case '\0':
327                                 if (arg.length() > 0) {
328                                         // if there wasn't already a space or tab, start a new parameter
329                                         argp = (char **) realloc(argp, (n + 2) * sizeof(char *));
330                                         argp[n++] = strdup (arg.c_str());
331                                         arg = "";
332                                 }
333                                 break;
334                         default :
335                                 arg += c;
336                                 break;
337                 }
338         }
339         argp[n] = NULL;
340 }
341
342 SystemExec::~SystemExec ()
343 {
344         terminate ();
345         if (envp) {
346                 for (int i = 0; envp[i]; ++i) {
347                         free (envp[i]);
348                 }
349                 free (envp);
350         }
351         if (argp) {
352                 for (int i = 0; argp[i]; ++i) {
353                         free (argp[i]);
354                 }
355                 free (argp);
356         }
357 #ifdef PLATFORM_WINDOWS
358         if (w_args) free(w_args);
359 #elif !defined NO_VFORK
360         if (argx) {
361                 /* argx[0 .. 8] are fixed parameters to vfork-exec-wrapper */
362                 for (int i = 0; i < 9; ++i) {
363                         free (argx[i]);
364                 }
365                 free (argx);
366         }
367 #endif
368         pthread_mutex_destroy(&write_lock);
369 }
370
371 static void*
372 interposer_thread (void *arg) {
373         SystemExec *sex = static_cast<SystemExec *>(arg);
374         sex->output_interposer();
375         pthread_exit(0);
376         return 0;
377 }
378
379 string
380 SystemExec::to_s () const
381 {
382 #ifdef PLATFORM_WINDOWS
383         return string (w_args ? w_args : "");
384 #else
385         stringstream out;
386         if (argp) {
387                 for (int i = 0; argp[i]; ++i) {
388                         out << argp[i] << " ";
389                 }
390         }
391         return out.str();
392 #endif
393 }
394
395 size_t
396 SystemExec::write_to_stdin (std::string const& d, size_t len)
397 {
398         const char *data = d.c_str();
399         if (len == 0) {
400                 len = d.length();
401         }
402         return write_to_stdin ((const void*)data, len);
403 }
404
405 #ifdef PLATFORM_WINDOWS /* Windows Process */
406
407 /* HELPER FUNCTIONS */
408
409 static void
410 create_pipe (HANDLE *pipe, bool in)
411 {
412         SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
413         HANDLE tmpHandle;
414         if (in) {
415                 if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024)) return;
416                 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) return;
417         } else {
418                 if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024)) return;
419                 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) return;
420         }
421         CloseHandle(tmpHandle);
422 }
423
424 static void
425 destroy_pipe (HANDLE pipe[2])
426 {
427         if (pipe[0] != INVALID_HANDLE_VALUE) {
428                 CloseHandle(pipe[0]);
429                 pipe[0] = INVALID_HANDLE_VALUE;
430         }
431         if (pipe[1] != INVALID_HANDLE_VALUE) {
432                 CloseHandle(pipe[1]);
433                 pipe[1] = INVALID_HANDLE_VALUE;
434         }
435 }
436
437 static BOOL
438 CALLBACK my_terminateApp(HWND hwnd, LPARAM procId)
439 {
440         DWORD currentProcId = 0;
441         GetWindowThreadProcessId(hwnd, &currentProcId);
442         if (currentProcId == (DWORD)procId)
443                 PostMessage(hwnd, WM_CLOSE, 0, 0);
444         return TRUE;
445 }
446
447 /* PROCESS API */
448
449 void
450 SystemExec::make_envp()
451 {
452         ; /* environemt is copied over with CreateProcess(...,env=0 ,..) */
453 }
454
455 void
456 SystemExec::make_wargs (char** a)
457 {
458         std::string wa = cmd;
459         if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; }
460         std::replace(cmd.begin(), cmd.end(), '/', '\\' );
461         char **tmp = ++a;
462         while (tmp && *tmp) {
463                 wa.append(" \"");
464                 wa.append(*tmp);
465                 if (strlen(*tmp) > 0 && (*tmp)[strlen(*tmp) - 1] == '\\') {
466                         wa.append("\\");
467                 }
468                 wa.append("\"");
469                 tmp++;
470         }
471         w_args = strdup(wa.c_str());
472 }
473
474 void
475 SystemExec::make_argp (std::string args)
476 {
477         std::string wa = cmd;
478         if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; }
479         std::replace(cmd.begin(), cmd.end(), '/', '\\' );
480         wa.append(" ");
481         wa.append(args);
482         w_args = strdup(wa.c_str());
483 }
484
485 void
486 SystemExec::terminate ()
487 {
488         ::pthread_mutex_lock(&write_lock);
489
490         close_stdin();
491
492         if (pid) {
493                 /* terminate */
494                 EnumWindows(my_terminateApp, (LPARAM)pid->dwProcessId);
495                 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
496
497                 /* kill ! */
498                 TerminateProcess(pid->hProcess, 0xf291);
499
500                 CloseHandle(pid->hThread);
501                 CloseHandle(pid->hProcess);
502                 destroy_pipe(stdinP);
503                 destroy_pipe(stdoutP);
504                 destroy_pipe(stderrP);
505                 delete pid;
506                 pid=0;
507         }
508         ::pthread_mutex_unlock(&write_lock);
509 }
510
511 int
512 SystemExec::wait (int options)
513 {
514         while (is_running()) {
515                 WaitForSingleObject(pid->hProcess, 40);
516         }
517         return 0;
518 }
519
520 bool
521 SystemExec::is_running ()
522 {
523         if (!pid) return false;
524         DWORD exit_code;
525         if (GetExitCodeProcess(pid->hProcess, &exit_code)) {
526                 if (exit_code == STILL_ACTIVE) return true;
527         }
528         return false;
529 }
530
531 int
532 SystemExec::start (StdErrMode stderr_mode, const char * /*vfork_exec_wrapper*/)
533 {
534         char* working_dir = 0;
535
536         if (pid) { return 0; }
537
538         pid = new PROCESS_INFORMATION;
539         memset(pid, 0, sizeof(PROCESS_INFORMATION));
540
541         create_pipe(stdinP, true);
542         create_pipe(stdoutP, false);
543
544         if (stderr_mode == MergeWithStdin) {
545         /* merge stout & stderr */
546                 DuplicateHandle(GetCurrentProcess(), stdoutP[1], GetCurrentProcess(), &stderrP[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
547         } else if (stderr_mode == IgnoreAndClose) {
548                 //TODO read/flush this pipe or close it...
549                 create_pipe(stderrP, false);
550         } else {
551                 //TODO: keep stderr of this process mode.
552         }
553
554         bool success = false;
555         STARTUPINFOA startupInfo = { sizeof(STARTUPINFO), 0, 0, 0,
556                 (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT,
557                 (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT,
558                 0, 0, 0,
559                 STARTF_USESTDHANDLES,
560                 0, 0, 0,
561                 stdinP[0], stdoutP[1], stderrP[1]
562         };
563
564         success = CreateProcess(0, w_args,
565                 0, 0, /* bInheritHandles = */ TRUE,
566                 (CREATE_NO_WINDOW&0) | CREATE_UNICODE_ENVIRONMENT | (0&CREATE_NEW_CONSOLE),
567                 /*env = */ 0,
568                 working_dir,
569                 &startupInfo, pid);
570
571         if (stdinP[0] != INVALID_HANDLE_VALUE) {
572                 CloseHandle(stdinP[0]);
573                 stdinP[0] = INVALID_HANDLE_VALUE;
574         }
575         if (stdoutP[1] != INVALID_HANDLE_VALUE) {
576                 CloseHandle(stdoutP[1]);
577                 stdoutP[1] = INVALID_HANDLE_VALUE;
578         }
579         if (stderrP[1] != INVALID_HANDLE_VALUE) {
580                 CloseHandle(stderrP[1]);
581                 stderrP[1] = INVALID_HANDLE_VALUE;
582         }
583
584         if (!success) {
585                 CloseHandle(pid->hThread);
586                 CloseHandle(pid->hProcess);
587                 destroy_pipe(stdinP);
588                 destroy_pipe(stdoutP);
589                 destroy_pipe(stderrP);
590                 delete pid;
591                 pid=0;
592                 return -1;
593         }
594
595         int rv = pthread_create (&thread_id_tt, NULL, interposer_thread, this);
596         thread_active=true;
597         if (rv) {
598                 thread_active=false;
599                 terminate();
600                 return -2;
601         }
602         Sleep(20);
603         return 0;
604 }
605
606 void
607 SystemExec::output_interposer()
608 {
609         DWORD bytesRead = 0;
610         char data[BUFSIZ];
611 #if 0 // untested code to set up nonblocking
612         unsigned long l = 1;
613         ioctlsocket(stdoutP[0], FIONBIO, &l);
614 #endif
615         while(1) {
616 #if 0 // for non-blocking pipes..
617                 DWORD bytesAvail = 0;
618                 PeekNamedPipe(stdoutP[0], 0, 0, 0, &bytesAvail, 0);
619                 if (bytesAvail < 1) {Sleep(500); printf("N/A\n"); continue;}
620 #endif
621                 if (stdoutP[0] == INVALID_HANDLE_VALUE) break;
622                 if (!ReadFile(stdoutP[0], data, BUFSIZ - 1, &bytesRead, 0)) {
623                         DWORD err =  GetLastError();
624                         if (err == ERROR_IO_PENDING) continue;
625                         break;
626                 }
627                 if (bytesRead < 1) continue; /* actually not needed; but this is safe. */
628                 data[bytesRead] = 0;
629                 ReadStdout(data, bytesRead); /* EMIT SIGNAL */
630         }
631         Terminated(); /* EMIT SIGNAL */
632         pthread_exit(0);
633 }
634
635 void
636 SystemExec::close_stdin()
637 {
638         if (stdinP[0] != INVALID_HANDLE_VALUE) FlushFileBuffers (stdinP[0]);
639         if (stdinP[1] != INVALID_HANDLE_VALUE) FlushFileBuffers (stdinP[1]);
640         Sleep(200);
641         destroy_pipe (stdinP);
642 }
643
644 size_t
645 SystemExec::write_to_stdin (const void* data, size_t bytes)
646 {
647         DWORD r, c;
648
649         ::pthread_mutex_lock (&write_lock);
650
651         c=0;
652         while (c < bytes) {
653                 if (!WriteFile (stdinP[1], &((const char*)data)[c], bytes - c, &r, NULL)) {
654                         if (GetLastError() == 0xE8 /*NT_STATUS_INVALID_USER_BUFFER*/) {
655                                 Sleep(100);
656                                 continue;
657                         } else {
658                                 fprintf(stderr, "SYSTEM-EXEC: stdin write error.\n");
659                                 break;
660                         }
661                 }
662                 c += r;
663         }
664         ::pthread_mutex_unlock(&write_lock);
665         return c;
666 }
667
668
669 /* end windows process */
670 #else
671 /* UNIX/POSIX process */
672
673 extern char **environ;
674
675 void
676 SystemExec::make_envp()
677 {
678         int i = 0;
679         envp = (char **) calloc(1, sizeof(char*));
680         /* copy current environment */
681         for (i = 0; environ[i]; ++i) {
682           envp[i] = strdup(environ[i]);
683           envp = (char **) realloc(envp, (i+2) * sizeof(char*));
684         }
685         envp[i] = 0;
686 }
687
688 void
689 SystemExec::make_argp(std::string args)
690 {
691         int argn = 1;
692         char *cp1;
693         char *cp2;
694
695         char *carg = strdup(args.c_str());
696
697         argp = (char **) malloc((argn + 1) * sizeof(char *));
698         if (argp == (char **) 0) {
699                 free(carg);
700                 return; // FATAL
701         }
702
703         argp[0] = strdup(cmd.c_str());
704
705         /* TODO: quotations and escapes
706          * http://stackoverflow.com/questions/1511797/convert-string-to-argv-in-c
707          *
708          * It's actually not needed. All relevant invocations specify 'argp' directly.
709          * Only 'xjadeo -L -R' uses this function and that uses neither quotations
710          * nor arguments with white-space.
711          */
712         for (cp1 = cp2 = carg; *cp2 != '\0'; ++cp2) {
713                 if (*cp2 == ' ') {
714                         *cp2 = '\0';
715                         argp[argn++] = strdup(cp1);
716                         cp1 = cp2 + 1;
717                         argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
718                 }
719         }
720         if (cp2 != cp1) {
721                 argp[argn++] = strdup(cp1);
722                 argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
723         }
724         argp[argn] = (char *) 0;
725         free(carg);
726 }
727
728 void
729 SystemExec::terminate ()
730 {
731         ::pthread_mutex_lock(&write_lock);
732
733         /* close stdin in an attempt to get the child to exit cleanly.
734          */
735
736         close_stdin();
737
738         if (pid) {
739                 ::usleep(200000);
740                 sched_yield();
741                 wait(WNOHANG);
742         }
743
744         /* if pid is non-zero, the child task is still executing (i.e. it did
745          * not exit in response to stdin being closed). try to kill it.
746          */
747
748         if (pid) {
749                 ::kill(pid, SIGTERM);
750                 ::usleep(250000);
751                 sched_yield();
752                 wait(WNOHANG);
753         }
754
755         /* if pid is non-zero, the child task is STILL executing after being
756          * sent SIGTERM. Act tough ... send SIGKILL
757          */
758
759         if (pid) {
760                 ::fprintf(stderr, "Process is still running! trying SIGKILL\n");
761                 ::kill(pid, SIGKILL);
762         }
763
764         wait();
765         if (thread_active) pthread_join(thread_id_tt, NULL);
766         thread_active = false;
767         assert(pid == 0);
768         ::pthread_mutex_unlock(&write_lock);
769 }
770
771 int
772 SystemExec::wait (int options)
773 {
774         int status = 0;
775         int ret;
776
777         if (pid == 0) return -1;
778
779         ret = waitpid (pid, &status, options);
780
781         if (ret == pid) {
782                 if (WEXITSTATUS(status) || WIFSIGNALED(status)) {
783                         pid=0;
784                 }
785         } else {
786                 if (ret != 0) {
787                         if (errno == ECHILD) {
788                                 /* no currently running children, reset pid */
789                                 pid=0;
790                         }
791                 } /* else the process is still running */
792         }
793         return status;
794 }
795
796 bool
797 SystemExec::is_running ()
798 {
799         int status = 0;
800         if (pid == 0) {
801                 return false;
802         }
803         if (::waitpid (pid, &status, WNOHANG)==0) {
804                 return true;
805         }
806         return false;
807 }
808
809 int
810 SystemExec::start (StdErrMode stderr_mode, const char *vfork_exec_wrapper)
811 {
812         if (is_running ()) {
813                 return 0;
814         }
815
816         int r;
817
818         if (::pipe(pin) < 0 || ::pipe(pout) < 0 || ::pipe(pok) < 0) {
819                 /* Something unexpected went wrong creating a pipe. */
820                 return -1;
821         }
822
823 #ifndef NO_VFORK
824         r = ::vfork();
825 #else
826         r = ::fork();
827 #endif
828         if (r < 0) {
829                 /* failed to fork */
830                 return -2;
831         }
832
833         if (r > 0) {
834                 /* main */
835                 pid = r;
836
837                 /* check if execve was successful. */
838                 close_fd (pok[1]);
839                 char buf;
840                 for (;;) {
841                         ssize_t n = ::read (pok[0], &buf, 1);
842                         if (n == 1) {
843                                 /* child process returned from execve */
844                                 pid=0;
845                                 close_fd (pok[0]);
846                                 close_fd (pok[1]);
847                                 close_fd (pin[1]);
848                                 close_fd (pin[0]);
849                                 close_fd (pout[1]);
850                                 close_fd (pout[0]);
851                                 return -3;
852                         } else if (n == -1) {
853                                 if (errno==EAGAIN || errno==EINTR) {
854                                         continue;
855                                 }
856                         }
857                         break;
858                 }
859
860                 close_fd (pok[0]);
861                 /* child started successfully */
862
863                 close_fd (pout[1]);
864                 close_fd (pin[0]);
865
866                 int rv = pthread_create (&thread_id_tt, NULL, interposer_thread, this);
867                 thread_active=true;
868
869                 if (rv) {
870                         thread_active=false;
871                         terminate();
872                         return -2;
873                 }
874                 return 0; /* all systems go - return to main */
875         }
876
877 #ifdef NO_VFORK
878         /* child process - exec external process */
879         close_fd (pok[0]);
880         ::fcntl (pok[1], F_SETFD, FD_CLOEXEC);
881
882         close_fd (pin[1]);
883         if (pin[0] != STDIN_FILENO) {
884           ::dup2 (pin[0], STDIN_FILENO);
885         }
886         close_fd (pin[0]);
887         close_fd (pout[0]);
888         if (pout[1] != STDOUT_FILENO) {
889                 ::dup2 (pout[1], STDOUT_FILENO);
890         }
891
892         if (stderr_mode == MergeWithStdin) {
893                 /* merge STDERR into output */
894                 if (pout[1] != STDERR_FILENO) {
895                         ::dup2(pout[1], STDERR_FILENO);
896                 }
897         } else if (stderr_mode == IgnoreAndClose) {
898                 /* ignore STDERR */
899                 ::close(STDERR_FILENO);
900         } else { /* stderr_mode == ShareWithParent */
901                 /* keep STDERR */
902 #if defined __APPLE__&& defined ASL_LOG_DESCRIPTOR_WRITE
903                 ::close(STDERR_FILENO);
904                 stderr_mode = IgnoreAndClose; // for vfork
905 #endif
906         }
907
908         if (pout[1] != STDOUT_FILENO && pout[1] != STDERR_FILENO) {
909                 close_fd(pout[1]);
910         }
911
912         if (nicelevel !=0) {
913                 ::nice(nicelevel);
914         }
915
916 #ifdef HAVE_SIGSET
917         sigset (SIGPIPE, SIG_DFL);
918 #else
919         signal (SIGPIPE, SIG_DFL);
920 #endif
921         if (!vfork_exec_wrapper) {
922                 error << _("Cannot start external process, no vfork wrapper") << endmsg;
923                 return -1;
924         }
925
926         int good_fds[2] = { pok[1],  -1 };
927         close_allv(good_fds);
928
929         ::execve(argp[0], argp, envp);
930
931 #else /* ! NO_VFORK */
932
933         /* XXX this should be done before vfork()
934          * calling malloc here only increases the time vfork() blocks
935          */
936         int argn = 0;
937         for (int i = 0; argp[i]; ++i) { argn++; }
938
939         argx = (char **) malloc ((argn + 10) * sizeof(char*));
940         argx[0] = strdup (vfork_exec_wrapper);
941
942 #define FDARG(NUM, FDN) \
943         argx[NUM] = (char*) calloc(6, sizeof(char)); snprintf(argx[NUM], 6, "%d", FDN);
944
945         FDARG (1, pok[0])
946         FDARG (2, pok[1])
947         FDARG (3, pin[0])
948         FDARG (4, pin[1])
949         FDARG (5, pout[0])
950         FDARG (6, pout[1])
951         FDARG (7, stderr_mode)
952         FDARG (8, nicelevel)
953
954         for (int i = 0; argp[i]; ++i) {
955                 argx[9+i] = argp[i];
956         }
957         argx[argn+9] = NULL;
958
959         ::execve (argx[0], argx, envp);
960 #endif
961
962         /* if we reach here something went wrong.. */
963         char buf = 0;
964         (void) ::write (pok[1], &buf, 1);
965         close_fd (pok[1]);
966         exit (-1);
967         return -1;
968 }
969
970 void
971 SystemExec::output_interposer ()
972 {
973         int rfd = pout[0];
974         char buf[BUFSIZ];
975         ssize_t r;
976         unsigned long l = 1;
977
978         ioctl (rfd, FIONBIO, &l); // set non-blocking I/O
979
980         for (;fcntl (rfd, F_GETFL) != -1;) {
981                 r = read (rfd, buf, BUFSIZ - 1);
982                 if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
983                         fd_set rfds;
984                         struct timeval tv;
985                         FD_ZERO (&rfds);
986                         FD_SET (rfd, &rfds);
987                         tv.tv_sec = 0;
988                         tv.tv_usec = 10000;
989                         int rv = select (1, &rfds, NULL, NULL, &tv);
990                         if (rv == -1) {
991                                 break;
992                         }
993                         continue;
994                 }
995                 if (r <= 0) {
996                         break;
997                 }
998                 buf[r]=0;
999                 std::string rv = std::string (buf, r);
1000                 ReadStdout (rv, r); /* EMIT SIGNAL */
1001         }
1002         Terminated (); /* EMIT SIGNAL */
1003         pthread_exit (0);
1004 }
1005
1006 void
1007 SystemExec::close_stdin()
1008 {
1009         if (pin[1] < 0) {
1010                 return;
1011         }
1012         close_fd (pin[0]);
1013         close_fd (pin[1]);
1014         close_fd (pout[0]);
1015         close_fd (pout[1]);
1016 }
1017
1018 size_t
1019 SystemExec::write_to_stdin (const void* data, size_t bytes)
1020 {
1021         ssize_t r;
1022         size_t c;
1023         ::pthread_mutex_lock (&write_lock);
1024
1025         c = 0;
1026         while (c < bytes) {
1027                 for (;;) {
1028                         r = ::write (pin[1], &((const char*)data)[c], bytes - c);
1029                         if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
1030                                 sleep(1);
1031                                 continue;
1032                         }
1033                         if ((size_t) r != (bytes-c)) {
1034                                 ::pthread_mutex_unlock(&write_lock);
1035                                 return c;
1036                         }
1037                         break;
1038                 }
1039                 c += r;
1040         }
1041         fsync (pin[1]);
1042         ::pthread_mutex_unlock(&write_lock);
1043         return c;
1044 }
1045
1046 #endif // end UNIX process