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