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