ardour
system_exec.cc
Go to the documentation of this file.
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
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 #endif
174 }
175 
176 SystemExec::SystemExec (std::string c, std::string a)
177  : cmd(c)
178 {
179  init ();
180 
181  argp = NULL;
182  make_envp();
183  make_argp(a);
184 }
185 
186 SystemExec::SystemExec (std::string c, char **a)
187  : cmd(c) , argp(a)
188 {
189  init ();
190 
191 #ifdef PLATFORM_WINDOWS
192  make_wargs(a);
193 #endif
194  make_envp();
195 }
196 
197 SystemExec::SystemExec (std::string command, const std::map<char, std::string> subs)
198 {
199  init ();
200  make_argp_escaped(command, subs);
201 
202  if (find_file (Searchpath (Glib::getenv ("PATH")), argp[0], cmd)) {
203  // argp[0] exists in $PATH` - set it to the actual path where it was found
204  free (argp[0]);
205  argp[0] = strdup(cmd.c_str ());
206  }
207  // else argp[0] not found in path - leave it as-is, it might be an absolute path
208 
209  // Glib::find_program_in_path () is only available in Glib >= 2.28
210  // cmd = Glib::find_program_in_path (argp[0]);
211 
212  make_envp();
213 }
214 
215 void
216 SystemExec::make_argp_escaped(std::string command, const std::map<char, std::string> subs)
217 {
218 
219  int inquotes = 0;
220  int n = 0;
221  size_t i = 0;
222  std::string arg = "";
223 
224  argp = (char **) malloc(sizeof(char *));
225 
226  for (i = 0; i <= command.length(); i++) { // include terminating '\0'
227  char c = command.c_str()[i];
228  if (inquotes) {
229  if (c == '"') {
230  inquotes = 0;
231  } else {
232  // still in quotes - just copy
233  arg += c;
234  }
235  } else switch (c) {
236  case '%' :
237  c = command.c_str()[++i];
238  if (c == '%' || c == '\0') {
239  // "%%", "%" at end-of-string => "%"
240  arg += '%';
241  } else {
242  // search subs for string to substitute for char
243  std::map<char, std::string>::const_iterator s = subs.find(c);
244  if (s != subs.end()) {
245  // found substitution
246  arg += s->second;
247  } else {
248  // not a valid substitution, just copy
249  arg += '%';
250  arg += c;
251  }
252  }
253  break;
254  case '\\':
255  c = command.c_str()[++i];
256  switch (c) {
257  case ' ' :
258  case '"' : arg += c; break; // "\\", "\" at end-of-string => "\"
259  case '\0':
260  case '\\': arg += '\\'; break;
261  default : arg += '\\'; arg += c; break;
262  }
263  break;
264  case '"' :
265  inquotes = 1;
266  break;
267  case ' ' :
268  case '\t':
269  case '\0':
270  if (arg.length() > 0) {
271  // if there wasn't already a space or tab, start a new parameter
272  argp = (char **) realloc(argp, (n + 2) * sizeof(char *));
273  argp[n++] = strdup (arg.c_str());
274  arg = "";
275  }
276  break;
277  default :
278  arg += c;
279  break;
280  }
281  }
282  argp[n] = NULL;
283 }
284 
286 {
287  terminate ();
288  if (envp) {
289  for (int i=0;envp[i];++i) {
290  free(envp[i]);
291  }
292  free (envp);
293  }
294  if (argp) {
295  for (int i=0;argp[i];++i) {
296  free(argp[i]);
297  }
298  free (argp);
299  }
300 #ifdef PLATFORM_WINDOWS
301  if (w_args) free(w_args);
302 #endif
303  pthread_mutex_destroy(&write_lock);
304 }
305 
306 static void *
307 interposer_thread (void *arg) {
308  SystemExec *sex = static_cast<SystemExec *>(arg);
309  sex->output_interposer();
310  pthread_exit(0);
311  return 0;
312 }
313 
314 #ifdef PLATFORM_WINDOWS /* Windows Process */
315 
316 /* HELPER FUNCTIONS */
317 
318 static void create_pipe (HANDLE *pipe, bool in) {
319  SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
320  HANDLE tmpHandle;
321  if (in) {
322  if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024)) return;
323  if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) return;
324  } else {
325  if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024)) return;
326  if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) return;
327  }
328  CloseHandle(tmpHandle);
329 }
330 
331 static void destroy_pipe (HANDLE pipe[2]) {
332  if (pipe[0] != INVALID_HANDLE_VALUE) {
333  CloseHandle(pipe[0]);
334  pipe[0] = INVALID_HANDLE_VALUE;
335  }
336  if (pipe[1] != INVALID_HANDLE_VALUE) {
337  CloseHandle(pipe[1]);
338  pipe[1] = INVALID_HANDLE_VALUE;
339  }
340 }
341 
342 static BOOL CALLBACK my_terminateApp(HWND hwnd, LPARAM procId)
343 {
344  DWORD currentProcId = 0;
345  GetWindowThreadProcessId(hwnd, &currentProcId);
346  if (currentProcId == (DWORD)procId)
347  PostMessage(hwnd, WM_CLOSE, 0, 0);
348  return TRUE;
349 }
350 
351 /* PROCESS API */
352 
353 void
355  ;/* environemt is copied over with CreateProcess(...,env=0 ,..) */
356 }
357 
358 void
359 SystemExec::make_wargs(char **a) {
360  std::string wa = cmd;
361  if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; }
362  std::replace(cmd.begin(), cmd.end(), '/', '\\' );
363  char **tmp = ++a;
364  while (tmp && *tmp) {
365  wa.append(" \"");
366  std::string arg(*tmp);
367  size_t start_pos = 0;
368  while((start_pos = arg.find("\\", start_pos)) != std::string::npos) {
369  arg.replace(start_pos, 1, "\\\\");
370  start_pos += 2;
371  }
372  wa.append(arg);
373  wa.append("\"");
374  tmp++;
375  }
376  w_args = strdup(wa.c_str());
377 }
378 
379 void
380 SystemExec::make_argp(std::string args) {
381  std::string wa = cmd;
382  if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; }
383  std::replace(cmd.begin(), cmd.end(), '/', '\\' );
384  wa.append(" ");
385  wa.append(args);
386  w_args = strdup(wa.c_str());
387 }
388 
389 void
391 {
392  ::pthread_mutex_lock(&write_lock);
393 
394  close_stdin();
395 
396  if (pid) {
397  /* terminate */
398  EnumWindows(my_terminateApp, (LPARAM)pid->dwProcessId);
399  PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
400 
401  /* kill ! */
402  TerminateProcess(pid->hProcess, 0xf291);
403 
404  CloseHandle(pid->hThread);
405  CloseHandle(pid->hProcess);
406  destroy_pipe(stdinP);
407  destroy_pipe(stdoutP);
408  destroy_pipe(stderrP);
409  delete pid;
410  pid=0;
411  }
412  ::pthread_mutex_unlock(&write_lock);
413 }
414 
415 int
416 SystemExec::wait (int options)
417 {
418  while (is_running()) {
419  WaitForSingleObject(pid->hProcess, 40);
420  }
421  return 0;
422 }
423 
424 bool
426 {
427  if (!pid) return false;
428  DWORD exit_code;
429  if (GetExitCodeProcess(pid->hProcess, &exit_code)) {
430  if (exit_code == STILL_ACTIVE) return true;
431  }
432  return false;
433 }
434 
435 int
436 SystemExec::start (int stderr_mode, const char * /*vfork_exec_wrapper*/)
437 {
438  char* working_dir = 0;
439 
440  if (pid) { return 0; }
441 
442  pid = new PROCESS_INFORMATION;
443  memset(pid, 0, sizeof(PROCESS_INFORMATION));
444 
445  create_pipe(stdinP, true);
446  create_pipe(stdoutP, false);
447 
448  if (stderr_mode == 2) {
449  /* merge stout & stderr */
450  DuplicateHandle(GetCurrentProcess(), stdoutP[1], GetCurrentProcess(), &stderrP[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
451  } else if (stderr_mode == 1) {
452  //TODO read/flush this pipe or close it...
453  create_pipe(stderrP, false);
454  } else {
455  //TODO: keep stderr of this process mode.
456  }
457 
458  bool success = false;
459  STARTUPINFOA startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
460  (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT,
461  (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT,
462  0, 0, 0,
463  STARTF_USESTDHANDLES,
464  0, 0, 0,
465  stdinP[0], stdoutP[1], stderrP[1]
466  };
467 
468  success = CreateProcess(0, w_args,
469  0, 0, /* bInheritHandles = */ TRUE,
470  (CREATE_NO_WINDOW&0) | CREATE_UNICODE_ENVIRONMENT | (0&CREATE_NEW_CONSOLE),
471  /*env = */ 0,
472  working_dir,
473  &startupInfo, pid);
474 
475  if (stdinP[0] != INVALID_HANDLE_VALUE) {
476  CloseHandle(stdinP[0]);
477  stdinP[0] = INVALID_HANDLE_VALUE;
478  }
479  if (stdoutP[1] != INVALID_HANDLE_VALUE) {
480  CloseHandle(stdoutP[1]);
481  stdoutP[1] = INVALID_HANDLE_VALUE;
482  }
483  if (stderrP[1] != INVALID_HANDLE_VALUE) {
484  CloseHandle(stderrP[1]);
485  stderrP[1] = INVALID_HANDLE_VALUE;
486  }
487 
488  if (!success) {
489  CloseHandle(pid->hThread);
490  CloseHandle(pid->hProcess);
491  destroy_pipe(stdinP);
492  destroy_pipe(stdoutP);
493  destroy_pipe(stderrP);
494  delete pid;
495  pid=0;
496  return -1;
497  }
498 
499  int rv = pthread_create(&thread_id_tt, NULL, interposer_thread, this);
500  thread_active=true;
501  if (rv) {
502  thread_active=false;
503  terminate();
504  return -2;
505  }
506  Sleep(20);
507  return 0;
508 }
509 
510 void
512 {
513  DWORD bytesRead = 0;
514  char data[BUFSIZ];
515 #if 0 // untested code to set up nonblocking
516  unsigned long l = 1;
517  ioctlsocket(stdoutP[0], FIONBIO, &l);
518 #endif
519  while(1) {
520 #if 0 // for non-blocking pipes..
521  DWORD bytesAvail = 0;
522  PeekNamedPipe(stdoutP[0], 0, 0, 0, &bytesAvail, 0);
523  if (bytesAvail < 1) {Sleep(500); printf("N/A\n"); continue;}
524 #endif
525  if (stdoutP[0] == INVALID_HANDLE_VALUE) break;
526  if (!ReadFile(stdoutP[0], data, BUFSIZ, &bytesRead, 0)) {
527  DWORD err = GetLastError();
528  if (err == ERROR_IO_PENDING) continue;
529  break;
530  }
531  if (bytesRead < 1) continue; /* actually not needed; but this is safe. */
532  data[bytesRead] = 0;
533  ReadStdout(data, bytesRead);/* EMIT SIGNAL */
534  }
535  Terminated();/* EMIT SIGNAL */
536 }
537 
538 void
540 {
541  if (stdinP[0]!= INVALID_HANDLE_VALUE) FlushFileBuffers(stdinP[0]);
542  if (stdinP[1]!= INVALID_HANDLE_VALUE) FlushFileBuffers(stdinP[1]);
543  Sleep(200);
544  destroy_pipe(stdinP);
545 }
546 
547 int
548 SystemExec::write_to_stdin(std::string d, size_t len)
549 {
550  const char *data;
551  DWORD r,c;
552 
553  ::pthread_mutex_lock(&write_lock);
554 
555  data=d.c_str();
556  if (len == 0) {
557  len=(d.length());
558  }
559  c=0;
560  while (c < len) {
561  if (!WriteFile(stdinP[1], data+c, len-c, &r, NULL)) {
562  if (GetLastError() == 0xE8 /*NT_STATUS_INVALID_USER_BUFFER*/) {
563  Sleep(100);
564  continue;
565  } else {
566  fprintf(stderr, "SYSTEM-EXEC: stdin write error.\n");
567  break;
568  }
569  }
570  c += r;
571  }
572  ::pthread_mutex_unlock(&write_lock);
573  return c;
574 }
575 
576 
577 /* end windows process */
578 #else
579 /* UNIX/POSIX process */
580 
581 extern char **environ;
582 void
584  int i=0;
585  envp = (char **) calloc(1, sizeof(char*));
586  /* copy current environment */
587  for (i=0;environ[i];++i) {
588  envp[i] = strdup(environ[i]);
589  envp = (char **) realloc(envp, (i+2) * sizeof(char*));
590  }
591  envp[i] = 0;
592 }
593 
594 void
595 SystemExec::make_argp(std::string args) {
596  int argn = 1;
597  char *cp1;
598  char *cp2;
599 
600  char *carg = strdup(args.c_str());
601 
602  argp = (char **) malloc((argn + 1) * sizeof(char *));
603  if (argp == (char **) 0) {
604  free(carg);
605  return; // FATAL
606  }
607 
608  argp[0] = strdup(cmd.c_str());
609 
610  /* TODO: quotations and escapes
611  * http://stackoverflow.com/questions/1511797/convert-string-to-argv-in-c
612  *
613  * It's actually not needed. All relevant invocations specify 'argp' directly.
614  * Only 'xjadeo -L -R' uses this function and that uses neither quotations
615  * nor arguments with white-space.
616  */
617  for (cp1 = cp2 = carg; *cp2 != '\0'; ++cp2) {
618  if (*cp2 == ' ') {
619  *cp2 = '\0';
620  argp[argn++] = strdup(cp1);
621  cp1 = cp2 + 1;
622  argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
623  }
624  }
625  if (cp2 != cp1) {
626  argp[argn++] = strdup(cp1);
627  argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
628  }
629  argp[argn] = (char *) 0;
630  free(carg);
631 }
632 
633 
634 
635 void
637 {
638  ::pthread_mutex_lock(&write_lock);
639 
640  /* close stdin in an attempt to get the child to exit cleanly.
641  */
642 
643  close_stdin();
644 
645  if (pid) {
646  ::usleep(200000);
647  sched_yield();
648  wait(WNOHANG);
649  }
650 
651  /* if pid is non-zero, the child task is still executing (i.e. it did
652  * not exit in response to stdin being closed). try to kill it.
653  */
654 
655  if (pid) {
656  ::kill(pid, SIGTERM);
657  ::usleep(250000);
658  sched_yield();
659  wait(WNOHANG);
660  }
661 
662  /* if pid is non-zero, the child task is STILL executing after being
663  * sent SIGTERM. Act tough ... send SIGKILL
664  */
665 
666  if (pid) {
667  ::fprintf(stderr, "Process is still running! trying SIGKILL\n");
668  ::kill(pid, SIGKILL);
669  }
670 
671  wait();
672  if (thread_active) pthread_join(thread_id_tt, NULL);
673  thread_active = false;
674  assert(pid == 0);
675  ::pthread_mutex_unlock(&write_lock);
676 }
677 
678 int
679 SystemExec::wait (int options)
680 {
681  int status=0;
682  int ret;
683 
684  if (pid==0) return -1;
685 
686  ret = waitpid (pid, &status, options);
687 
688  if (ret == pid) {
689  if (WEXITSTATUS(status) || WIFSIGNALED(status)) {
690  pid=0;
691  }
692  } else {
693  if (ret != 0) {
694  if (errno == ECHILD) {
695  /* no currently running children, reset pid */
696  pid=0;
697  }
698  } /* else the process is still running */
699  }
700  return status;
701 }
702 
703 bool
705 {
706  int status=0;
707  if (pid==0) return false;
708  if (::waitpid(pid, &status, WNOHANG)==0) return true;
709  return false;
710 }
711 
712 int
713 SystemExec::start (int stderr_mode, const char *vfork_exec_wrapper)
714 {
715  if (is_running()) {
716  return 0; // mmh what to return here?
717  }
718  int r;
719 
720  if (::pipe(pin) < 0 || ::pipe(pout) < 0 || ::pipe(pok) < 0) {
721  /* Something unexpected went wrong creating a pipe. */
722  return -1;
723  }
724 
725 #ifndef NO_VFORK
726  r = ::vfork();
727 #else
728  r = ::fork();
729 #endif
730  if (r < 0) {
731  /* failed to fork */
732  return -2;
733  }
734 
735  if (r > 0) {
736  /* main */
737  pid=r;
738 
739  /* check if execve was successful. */
740  close_fd(pok[1]);
741  char buf;
742  for ( ;; ) {
743  ssize_t n = ::read(pok[0], &buf, 1 );
744  if ( n==1 ) {
745  /* child process returned from execve */
746  pid=0;
747  close_fd(pok[0]);
748  close_fd(pok[1]);
749  close_fd(pin[1]);
750  close_fd(pin[0]);
751  close_fd(pout[1]);
752  close_fd(pout[0]);
753  return -3;
754  } else if ( n==-1 ) {
755  if ( errno==EAGAIN || errno==EINTR )
756  continue;
757  }
758  break;
759  }
760  close_fd(pok[0]);
761  /* child started successfully */
762 
763  close_fd(pout[1]);
764  close_fd(pin[0]);
765  int rv = pthread_create(&thread_id_tt, NULL, interposer_thread, this);
766 
767  thread_active=true;
768  if (rv) {
769  thread_active=false;
770  terminate();
771  return -2;
772  }
773  return 0; /* all systems go - return to main */
774  }
775 
776 #ifdef NO_VFORK
777  /* child process - exec external process */
778  close_fd(pok[0]);
779  ::fcntl(pok[1], F_SETFD, FD_CLOEXEC);
780 
781  close_fd(pin[1]);
782  if (pin[0] != STDIN_FILENO) {
783  ::dup2(pin[0], STDIN_FILENO);
784  }
785  close_fd(pin[0]);
786  close_fd(pout[0]);
787  if (pout[1] != STDOUT_FILENO) {
788  ::dup2(pout[1], STDOUT_FILENO);
789  }
790 
791  if (stderr_mode == 2) {
792  /* merge STDERR into output */
793  if (pout[1] != STDERR_FILENO) {
794  ::dup2(pout[1], STDERR_FILENO);
795  }
796  } else if (stderr_mode == 1) {
797  /* ignore STDERR */
798  ::close(STDERR_FILENO);
799  } else {
800  /* keep STDERR */
801  }
802 
803  if (pout[1] != STDOUT_FILENO && pout[1] != STDERR_FILENO) {
804  close_fd(pout[1]);
805  }
806 
807  if (nicelevel !=0) {
808  ::nice(nicelevel);
809  }
810 
811 #ifdef HAVE_SIGSET
812  sigset(SIGPIPE, SIG_DFL);
813 #else
814  signal(SIGPIPE, SIG_DFL);
815 #endif
816  if (!vfork_exec_wrapper) {
817  error << _("Cannot start external process, no vfork wrapper") << endmsg;
818  return -1;
819  }
820 
821  int good_fds[2] = { pok[1], -1 };
822  close_allv(good_fds);
823 
824  ::execve(argp[0], argp, envp);
825  /* if we reach here something went wrong.. */
826  char buf = 0;
827  (void) ::write(pok[1], &buf, 1 );
828  close_fd(pok[1]);
829  exit(-1);
830  return -1;
831 #else
832 
833  /* XXX this should be done before vfork()
834  * calling malloc here only increases the time vfork() blocks
835  */
836  int argn = 0;
837  for (int i=0;argp[i];++i) { argn++; }
838  char **argx = (char **) malloc((argn + 10) * sizeof(char *));
839  argx[0] = strdup(vfork_exec_wrapper); // XXX
840 
841 #define FDARG(NUM, FDN) \
842  argx[NUM] = (char*) calloc(6, sizeof(char)); snprintf(argx[NUM], 6, "%d", FDN);
843 
844  FDARG(1, pok[0])
845  FDARG(2, pok[1])
846  FDARG(3, pin[0])
847  FDARG(4, pin[1])
848  FDARG(5, pout[0])
849  FDARG(6, pout[1])
850  FDARG(7, stderr_mode)
851  FDARG(8, nicelevel)
852 
853  for (int i=0;argp[i];++i) {
854  argx[9+i] = argp[i];
855  }
856  argx[argn+9] = NULL;
857 
858  ::execve(argx[0], argx, envp);
859 
860  /* if we reach here something went wrong.. */
861  char buf = 0;
862  (void) ::write(pok[1], &buf, 1 );
863  close_fd(pok[1]);
864  exit(-1);
865  return -1;
866 #endif
867 }
868 
869 void
871 {
872  int rfd=pout[0];
873  char buf[BUFSIZ];
874  ssize_t r;
875  unsigned long l = 1;
876 
877  ioctl(rfd, FIONBIO, &l); // set non-blocking I/O
878 
879  for (;fcntl(rfd, F_GETFL)!=-1;) {
880  r = read(rfd, buf, sizeof(buf));
881  if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
882  fd_set rfds;
883  struct timeval tv;
884  FD_ZERO(&rfds);
885  FD_SET(rfd, &rfds);
886  tv.tv_sec = 0;
887  tv.tv_usec = 10000;
888  int rv = select(1, &rfds, NULL, NULL, &tv);
889  if (rv == -1) {
890  break;
891  }
892  continue;
893  }
894  if (r <= 0) {
895  break;
896  }
897  buf[r]=0;
898  std::string rv = std::string(buf,r); // TODO: check allocation strategy
899  ReadStdout(rv, r);/* EMIT SIGNAL */
900  }
901  Terminated();/* EMIT SIGNAL */
902 }
903 
904 void
906 {
907  if (pin[1]<0) return;
908  close_fd(pin[0]);
909  close_fd(pin[1]);
910  close_fd(pout[0]);
911  close_fd(pout[1]);
912 }
913 
914 int
915 SystemExec::write_to_stdin(std::string d, size_t len)
916 {
917  const char *data;
918  ssize_t r;
919  size_t c;
920  ::pthread_mutex_lock(&write_lock);
921 
922  data=d.c_str();
923  if (len == 0) {
924  len=(d.length());
925  }
926  c=0;
927  while (c < len) {
928  for (;;) {
929  r=::write(pin[1], data+c, len-c);
930  if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
931  sleep(1);
932  continue;
933  }
934  if ((size_t) r != (len-c)) {
935  ::pthread_mutex_unlock(&write_lock);
936  return c;
937  }
938  break;
939  }
940  c += r;
941  }
942  fsync(pin[1]);
943  ::pthread_mutex_unlock(&write_lock);
944  return c;
945 }
946 
947 #endif // end UNIX process
SystemExec(std::string c, std::string a="")
Definition: system_exec.cc:176
PBD::Signal0< void > Terminated
Definition: system_exec.h:183
pthread_t thread_id_tt
Definition: system_exec.h:228
virtual ~SystemExec()
Definition: system_exec.cc:285
void make_argp(std::string)
Definition: system_exec.cc:595
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
std::string cmd
path to command - set when creating the class
Definition: system_exec.h:200
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
bool find_file(const Searchpath &search_path, const string &filename, std::string &result)
Definition: file_utils.cc:187
static void close_fd(int &fd)
Definition: system_exec.cc:60
#define _(Text)
Definition: i18n.h:11
pthread_mutex_t write_lock
Definition: system_exec.h:222
#define STDIN_FILENO
Definition: system_exec.h:24
#define FDARG(NUM, FDN)
void output_interposer()
Definition: system_exec.cc:870
int write_to_stdin(std::string d, size_t len=0)
Definition: system_exec.cc:915
char ** environ
int nicelevel
process nice level - defaults to 0
Definition: system_exec.h:201
execute an external command
Definition: system_exec.h:70
int wait(int options=0)
Definition: system_exec.cc:679
int start(int stderr_mode, const char *_vfork_exec_wrapper)
Definition: system_exec.cc:713
static void * interposer_thread(void *arg)
Definition: system_exec.cc:307
LIBARDOUR_API PBD::PropertyDescriptor< bool > select
Definition: route_group.cc:48
Definition: debug.h:30
PBD::Signal2< void, std::string, size_t > ReadStdout
Definition: system_exec.h:176
LIBARDOUR_API bool init(bool with_vst, bool try_optimization, const char *localedir)
Definition: globals.cc:376
int pthread_create(pthread_t *__restrict thread, __const pthread_attr_t *__restrict attr, void *(*start_routine)(void *), void *__restrict arg)
Definition: gprofhelper.c:83
#define STDOUT_FILENO
Definition: system_exec.h:27
#define STDERR_FILENO
Definition: system_exec.h:30
def signal
Definition: signals.py:53
void make_argp_escaped(std::string command, const std::map< char, std::string > subs)
Definition: system_exec.cc:216