Optimize buffer zero-filling
[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
412 create_pipe (HANDLE *pipe, bool in)
413 {
414         SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
415         HANDLE tmpHandle;
416         if (in) {
417                 if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024)) return;
418                 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) return;
419         } else {
420                 if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024)) return;
421                 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) return;
422         }
423         CloseHandle(tmpHandle);
424 }
425
426 static void
427 destroy_pipe (HANDLE pipe[2])
428 {
429         if (pipe[0] != INVALID_HANDLE_VALUE) {
430                 CloseHandle(pipe[0]);
431                 pipe[0] = INVALID_HANDLE_VALUE;
432         }
433         if (pipe[1] != INVALID_HANDLE_VALUE) {
434                 CloseHandle(pipe[1]);
435                 pipe[1] = INVALID_HANDLE_VALUE;
436         }
437 }
438
439 static BOOL
440 CALLBACK my_terminateApp(HWND hwnd, LPARAM procId)
441 {
442         DWORD currentProcId = 0;
443         GetWindowThreadProcessId(hwnd, &currentProcId);
444         if (currentProcId == (DWORD)procId)
445                 PostMessage(hwnd, WM_CLOSE, 0, 0);
446         return TRUE;
447 }
448
449 /* PROCESS API */
450
451 void
452 SystemExec::make_envp()
453 {
454         ; /* environemt is copied over with CreateProcess(...,env=0 ,..) */
455 }
456
457 void
458 SystemExec::make_wargs (char** a)
459 {
460         std::string wa = cmd;
461         if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; }
462         std::replace(cmd.begin(), cmd.end(), '/', '\\' );
463         char **tmp = ++a;
464         while (tmp && *tmp) {
465                 wa.append(" \"");
466                 wa.append(*tmp);
467                 if (strlen(*tmp) > 0 && (*tmp)[strlen(*tmp) - 1] == '\\') {
468                         wa.append("\\");
469                 }
470                 wa.append("\"");
471                 tmp++;
472         }
473         w_args = strdup(wa.c_str());
474 }
475
476 void
477 SystemExec::make_argp (std::string args)
478 {
479         std::string wa = cmd;
480         if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; }
481         std::replace(cmd.begin(), cmd.end(), '/', '\\' );
482         wa.append(" ");
483         wa.append(args);
484         w_args = strdup(wa.c_str());
485 }
486
487 void
488 SystemExec::terminate ()
489 {
490         ::pthread_mutex_lock(&write_lock);
491
492         close_stdin();
493
494         if (pid) {
495                 /* terminate */
496                 EnumWindows(my_terminateApp, (LPARAM)pid->dwProcessId);
497                 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
498
499                 /* kill ! */
500                 TerminateProcess(pid->hProcess, 0xf291);
501
502                 CloseHandle(pid->hThread);
503                 CloseHandle(pid->hProcess);
504                 destroy_pipe(stdinP);
505                 destroy_pipe(stdoutP);
506                 destroy_pipe(stderrP);
507                 delete pid;
508                 pid=0;
509         }
510         ::pthread_mutex_unlock(&write_lock);
511 }
512
513 int
514 SystemExec::wait (int options)
515 {
516         while (is_running()) {
517                 WaitForSingleObject(pid->hProcess, 40);
518         }
519         return 0;
520 }
521
522 bool
523 SystemExec::is_running ()
524 {
525         if (!pid) return false;
526         DWORD exit_code;
527         if (GetExitCodeProcess(pid->hProcess, &exit_code)) {
528                 if (exit_code == STILL_ACTIVE) return true;
529         }
530         return false;
531 }
532
533 int
534 SystemExec::start (int stderr_mode, const char * /*vfork_exec_wrapper*/)
535 {
536         char* working_dir = 0;
537
538         if (pid) { return 0; }
539
540         pid = new PROCESS_INFORMATION;
541         memset(pid, 0, sizeof(PROCESS_INFORMATION));
542
543         create_pipe(stdinP, true);
544         create_pipe(stdoutP, false);
545
546         if (stderr_mode == 2) {
547         /* merge stout & stderr */
548                 DuplicateHandle(GetCurrentProcess(), stdoutP[1], GetCurrentProcess(), &stderrP[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
549         } else if (stderr_mode == 1) {
550                 //TODO read/flush this pipe or close it...
551                 create_pipe(stderrP, false);
552         } else {
553                 //TODO: keep stderr of this process mode.
554         }
555
556         bool success = false;
557         STARTUPINFOA startupInfo = { sizeof(STARTUPINFO), 0, 0, 0,
558                 (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT,
559                 (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT,
560                 0, 0, 0,
561                 STARTF_USESTDHANDLES,
562                 0, 0, 0,
563                 stdinP[0], stdoutP[1], stderrP[1]
564         };
565
566         success = CreateProcess(0, w_args,
567                 0, 0, /* bInheritHandles = */ TRUE,
568                 (CREATE_NO_WINDOW&0) | CREATE_UNICODE_ENVIRONMENT | (0&CREATE_NEW_CONSOLE),
569                 /*env = */ 0,
570                 working_dir,
571                 &startupInfo, pid);
572
573         if (stdinP[0] != INVALID_HANDLE_VALUE) {
574                 CloseHandle(stdinP[0]);
575                 stdinP[0] = INVALID_HANDLE_VALUE;
576         }
577         if (stdoutP[1] != INVALID_HANDLE_VALUE) {
578                 CloseHandle(stdoutP[1]);
579                 stdoutP[1] = INVALID_HANDLE_VALUE;
580         }
581         if (stderrP[1] != INVALID_HANDLE_VALUE) {
582                 CloseHandle(stderrP[1]);
583                 stderrP[1] = INVALID_HANDLE_VALUE;
584         }
585
586         if (!success) {
587                 CloseHandle(pid->hThread);
588                 CloseHandle(pid->hProcess);
589                 destroy_pipe(stdinP);
590                 destroy_pipe(stdoutP);
591                 destroy_pipe(stderrP);
592                 delete pid;
593                 pid=0;
594                 return -1;
595         }
596
597         int rv = pthread_create (&thread_id_tt, NULL, interposer_thread, this);
598         thread_active=true;
599         if (rv) {
600                 thread_active=false;
601                 terminate();
602                 return -2;
603         }
604         Sleep(20);
605         return 0;
606 }
607
608 void
609 SystemExec::output_interposer()
610 {
611         DWORD bytesRead = 0;
612         char data[BUFSIZ];
613 #if 0 // untested code to set up nonblocking
614         unsigned long l = 1;
615         ioctlsocket(stdoutP[0], FIONBIO, &l);
616 #endif
617         while(1) {
618 #if 0 // for non-blocking pipes..
619                 DWORD bytesAvail = 0;
620                 PeekNamedPipe(stdoutP[0], 0, 0, 0, &bytesAvail, 0);
621                 if (bytesAvail < 1) {Sleep(500); printf("N/A\n"); continue;}
622 #endif
623                 if (stdoutP[0] == INVALID_HANDLE_VALUE) break;
624                 if (!ReadFile(stdoutP[0], data, BUFSIZ - 1, &bytesRead, 0)) {
625                         DWORD err =  GetLastError();
626                         if (err == ERROR_IO_PENDING) continue;
627                         break;
628                 }
629                 if (bytesRead < 1) continue; /* actually not needed; but this is safe. */
630                 data[bytesRead] = 0;
631                 ReadStdout(data, bytesRead); /* EMIT SIGNAL */
632         }
633         Terminated(); /* EMIT SIGNAL */
634         pthread_exit(0);
635 }
636
637 void
638 SystemExec::close_stdin()
639 {
640         if (stdinP[0] != INVALID_HANDLE_VALUE) FlushFileBuffers (stdinP[0]);
641         if (stdinP[1] != INVALID_HANDLE_VALUE) FlushFileBuffers (stdinP[1]);
642         Sleep(200);
643         destroy_pipe (stdinP);
644 }
645
646 size_t
647 SystemExec::write_to_stdin (const void* data, size_t bytes)
648 {
649         DWORD r, c;
650
651         ::pthread_mutex_lock (&write_lock);
652
653         c=0;
654         while (c < bytes) {
655                 if (!WriteFile (stdinP[1], &((const char*)data)[c], bytes - c, &r, NULL)) {
656                         if (GetLastError() == 0xE8 /*NT_STATUS_INVALID_USER_BUFFER*/) {
657                                 Sleep(100);
658                                 continue;
659                         } else {
660                                 fprintf(stderr, "SYSTEM-EXEC: stdin write error.\n");
661                                 break;
662                         }
663                 }
664                 c += r;
665         }
666         ::pthread_mutex_unlock(&write_lock);
667         return c;
668 }
669
670
671 /* end windows process */
672 #else
673 /* UNIX/POSIX process */
674
675 extern char **environ;
676
677 void
678 SystemExec::make_envp()
679 {
680         int i = 0;
681         envp = (char **) calloc(1, sizeof(char*));
682         /* copy current environment */
683         for (i = 0; environ[i]; ++i) {
684           envp[i] = strdup(environ[i]);
685           envp = (char **) realloc(envp, (i+2) * sizeof(char*));
686         }
687         envp[i] = 0;
688 }
689
690 void
691 SystemExec::make_argp(std::string args)
692 {
693         int argn = 1;
694         char *cp1;
695         char *cp2;
696
697         char *carg = strdup(args.c_str());
698
699         argp = (char **) malloc((argn + 1) * sizeof(char *));
700         if (argp == (char **) 0) {
701                 free(carg);
702                 return; // FATAL
703         }
704
705         argp[0] = strdup(cmd.c_str());
706
707         /* TODO: quotations and escapes
708          * http://stackoverflow.com/questions/1511797/convert-string-to-argv-in-c
709          *
710          * It's actually not needed. All relevant invocations specify 'argp' directly.
711          * Only 'xjadeo -L -R' uses this function and that uses neither quotations
712          * nor arguments with white-space.
713          */
714         for (cp1 = cp2 = carg; *cp2 != '\0'; ++cp2) {
715                 if (*cp2 == ' ') {
716                         *cp2 = '\0';
717                         argp[argn++] = strdup(cp1);
718                         cp1 = cp2 + 1;
719                         argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
720                 }
721         }
722         if (cp2 != cp1) {
723                 argp[argn++] = strdup(cp1);
724                 argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
725         }
726         argp[argn] = (char *) 0;
727         free(carg);
728 }
729
730 void
731 SystemExec::terminate ()
732 {
733         ::pthread_mutex_lock(&write_lock);
734
735         /* close stdin in an attempt to get the child to exit cleanly.
736          */
737
738         close_stdin();
739
740         if (pid) {
741                 ::usleep(200000);
742                 sched_yield();
743                 wait(WNOHANG);
744         }
745
746         /* if pid is non-zero, the child task is still executing (i.e. it did
747          * not exit in response to stdin being closed). try to kill it.
748          */
749
750         if (pid) {
751                 ::kill(pid, SIGTERM);
752                 ::usleep(250000);
753                 sched_yield();
754                 wait(WNOHANG);
755         }
756
757         /* if pid is non-zero, the child task is STILL executing after being
758          * sent SIGTERM. Act tough ... send SIGKILL
759          */
760
761         if (pid) {
762                 ::fprintf(stderr, "Process is still running! trying SIGKILL\n");
763                 ::kill(pid, SIGKILL);
764         }
765
766         wait();
767         if (thread_active) pthread_join(thread_id_tt, NULL);
768         thread_active = false;
769         assert(pid == 0);
770         ::pthread_mutex_unlock(&write_lock);
771 }
772
773 int
774 SystemExec::wait (int options)
775 {
776         int status = 0;
777         int ret;
778
779         if (pid == 0) return -1;
780
781         ret = waitpid (pid, &status, options);
782
783         if (ret == pid) {
784                 if (WEXITSTATUS(status) || WIFSIGNALED(status)) {
785                         pid=0;
786                 }
787         } else {
788                 if (ret != 0) {
789                         if (errno == ECHILD) {
790                                 /* no currently running children, reset pid */
791                                 pid=0;
792                         }
793                 } /* else the process is still running */
794         }
795         return status;
796 }
797
798 bool
799 SystemExec::is_running ()
800 {
801         int status = 0;
802         if (pid == 0) {
803                 return false;
804         }
805         if (::waitpid (pid, &status, WNOHANG)==0) {
806                 return true;
807         }
808         return false;
809 }
810
811 int
812 SystemExec::start (int stderr_mode, const char *vfork_exec_wrapper)
813 {
814         if (is_running ()) {
815                 return 0;
816         }
817
818         int r;
819
820         if (::pipe(pin) < 0 || ::pipe(pout) < 0 || ::pipe(pok) < 0) {
821                 /* Something unexpected went wrong creating a pipe. */
822                 return -1;
823         }
824
825 #ifndef NO_VFORK
826         r = ::vfork();
827 #else
828         r = ::fork();
829 #endif
830         if (r < 0) {
831                 /* failed to fork */
832                 return -2;
833         }
834
835         if (r > 0) {
836                 /* main */
837                 pid = r;
838
839                 /* check if execve was successful. */
840                 close_fd (pok[1]);
841                 char buf;
842                 for (;;) {
843                         ssize_t n = ::read (pok[0], &buf, 1);
844                         if (n == 1) {
845                                 /* child process returned from execve */
846                                 pid=0;
847                                 close_fd (pok[0]);
848                                 close_fd (pok[1]);
849                                 close_fd (pin[1]);
850                                 close_fd (pin[0]);
851                                 close_fd (pout[1]);
852                                 close_fd (pout[0]);
853                                 return -3;
854                         } else if (n == -1) {
855                                 if (errno==EAGAIN || errno==EINTR) {
856                                         continue;
857                                 }
858                         }
859                         break;
860                 }
861
862                 close_fd (pok[0]);
863                 /* child started successfully */
864
865                 close_fd (pout[1]);
866                 close_fd (pin[0]);
867
868                 int rv = pthread_create (&thread_id_tt, NULL, interposer_thread, this);
869                 thread_active=true;
870
871                 if (rv) {
872                         thread_active=false;
873                         terminate();
874                         return -2;
875                 }
876                 return 0; /* all systems go - return to main */
877         }
878
879 #ifdef NO_VFORK
880         /* child process - exec external process */
881         close_fd (pok[0]);
882         ::fcntl (pok[1], F_SETFD, FD_CLOEXEC);
883
884         close_fd (pin[1]);
885         if (pin[0] != STDIN_FILENO) {
886           ::dup2 (pin[0], STDIN_FILENO);
887         }
888         close_fd (pin[0]);
889         close_fd (pout[0]);
890         if (pout[1] != STDOUT_FILENO) {
891                 ::dup2 (pout[1], STDOUT_FILENO);
892         }
893
894         if (stderr_mode == 2) {
895                 /* merge STDERR into output */
896                 if (pout[1] != STDERR_FILENO) {
897                         ::dup2(pout[1], STDERR_FILENO);
898                 }
899         } else if (stderr_mode == 1) {
900                 /* ignore STDERR */
901                 ::close(STDERR_FILENO);
902         } else {
903                 /* keep STDERR */
904         }
905
906         if (pout[1] != STDOUT_FILENO && pout[1] != STDERR_FILENO) {
907                 close_fd(pout[1]);
908         }
909
910         if (nicelevel !=0) {
911                 ::nice(nicelevel);
912         }
913
914 #ifdef HAVE_SIGSET
915         sigset (SIGPIPE, SIG_DFL);
916 #else
917         signal (SIGPIPE, SIG_DFL);
918 #endif
919         if (!vfork_exec_wrapper) {
920                 error << _("Cannot start external process, no vfork wrapper") << endmsg;
921                 return -1;
922         }
923
924         int good_fds[2] = { pok[1],  -1 };
925         close_allv(good_fds);
926
927         ::execve(argp[0], argp, envp);
928
929 #else /* ! NO_VFORK */
930
931         /* XXX this should be done before vfork()
932          * calling malloc here only increases the time vfork() blocks
933          */
934         int argn = 0;
935         for (int i = 0; argp[i]; ++i) { argn++; }
936
937         argx = (char **) malloc ((argn + 10) * sizeof(char*));
938         argx[0] = strdup (vfork_exec_wrapper);
939
940 #define FDARG(NUM, FDN) \
941         argx[NUM] = (char*) calloc(6, sizeof(char)); snprintf(argx[NUM], 6, "%d", FDN);
942
943         FDARG (1, pok[0])
944         FDARG (2, pok[1])
945         FDARG (3, pin[0])
946         FDARG (4, pin[1])
947         FDARG (5, pout[0])
948         FDARG (6, pout[1])
949         FDARG (7, stderr_mode)
950         FDARG (8, nicelevel)
951
952         for (int i = 0; argp[i]; ++i) {
953                 argx[9+i] = argp[i];
954         }
955         argx[argn+9] = NULL;
956
957         ::execve (argx[0], argx, envp);
958 #endif
959
960         /* if we reach here something went wrong.. */
961         char buf = 0;
962         (void) ::write (pok[1], &buf, 1);
963         close_fd (pok[1]);
964         exit (-1);
965         return -1;
966 }
967
968 void
969 SystemExec::output_interposer ()
970 {
971         int rfd = pout[0];
972         char buf[BUFSIZ];
973         ssize_t r;
974         unsigned long l = 1;
975
976         ioctl (rfd, FIONBIO, &l); // set non-blocking I/O
977
978         for (;fcntl (rfd, F_GETFL) != -1;) {
979                 r = read (rfd, buf, BUFSIZ - 1);
980                 if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
981                         fd_set rfds;
982                         struct timeval tv;
983                         FD_ZERO (&rfds);
984                         FD_SET (rfd, &rfds);
985                         tv.tv_sec = 0;
986                         tv.tv_usec = 10000;
987                         int rv = select (1, &rfds, NULL, NULL, &tv);
988                         if (rv == -1) {
989                                 break;
990                         }
991                         continue;
992                 }
993                 if (r <= 0) {
994                         break;
995                 }
996                 buf[r]=0;
997                 std::string rv = std::string (buf, r);
998                 ReadStdout (rv, r); /* EMIT SIGNAL */
999         }
1000         Terminated (); /* EMIT SIGNAL */
1001         pthread_exit (0);
1002 }
1003
1004 void
1005 SystemExec::close_stdin()
1006 {
1007         if (pin[1] < 0) {
1008                 return;
1009         }
1010         close_fd (pin[0]);
1011         close_fd (pin[1]);
1012         close_fd (pout[0]);
1013         close_fd (pout[1]);
1014 }
1015
1016 size_t
1017 SystemExec::write_to_stdin (const void* data, size_t bytes)
1018 {
1019         ssize_t r;
1020         size_t c;
1021         ::pthread_mutex_lock (&write_lock);
1022
1023         c = 0;
1024         while (c < bytes) {
1025                 for (;;) {
1026                         r = ::write (pin[1], &((const char*)data)[c], bytes - c);
1027                         if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
1028                                 sleep(1);
1029                                 continue;
1030                         }
1031                         if ((size_t) r != (bytes-c)) {
1032                                 ::pthread_mutex_unlock(&write_lock);
1033                                 return c;
1034                         }
1035                         break;
1036                 }
1037                 c += r;
1038         }
1039         fsync (pin[1]);
1040         ::pthread_mutex_unlock(&write_lock);
1041         return c;
1042 }
1043
1044 #endif // end UNIX process