X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Fsystem_exec.cc;h=82398af0c84c7854fbd640901dab2a441ce89d73;hb=95ccbc452f513a9d6f70de45bc413067e568364c;hp=38f86a04ad678895f576d7673e19d3d59c97937d;hpb=1d39cf754355805d2281232905808670e32f7d6e;p=ardour.git diff --git a/libs/pbd/system_exec.cc b/libs/pbd/system_exec.cc index 38f86a04ad..82398af0c8 100644 --- a/libs/pbd/system_exec.cc +++ b/libs/pbd/system_exec.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -43,6 +44,8 @@ #endif +#define USE_VFORK + #include "pbd/system_exec.h" using namespace std; @@ -51,7 +54,7 @@ using namespace PBD; static void * interposer_thread (void *arg); static void close_fd (int& fd) { if (fd >= 0) ::close (fd); fd = -1; } -#ifndef PLATFORM_WINDOWS +#if (!defined PLATFORM_WINDOWS && defined NO_VFORK) /* * This function was part of libasyncns. * LGPL v2.1 @@ -146,11 +149,10 @@ static int close_allv(const int except_fds[]) { return 0; } -#endif /* not on windows */ - +#endif /* not on windows, nor vfork */ -SystemExec::SystemExec (std::string c, std::string a) - : cmd(c) +void +SystemExec::init () { pthread_mutex_init(&write_lock, NULL); thread_active=false; @@ -158,12 +160,19 @@ SystemExec::SystemExec (std::string c, std::string a) pin[1] = -1; nicelevel = 0; envp = NULL; - argp = NULL; #ifdef PLATFORM_WINDOWS stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE; stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE; stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE; #endif +} + +SystemExec::SystemExec (std::string c, std::string a) + : cmd(c) +{ + init (); + + argp = NULL; make_envp(); make_argp(a); } @@ -171,21 +180,101 @@ SystemExec::SystemExec (std::string c, std::string a) SystemExec::SystemExec (std::string c, char **a) : cmd(c) , argp(a) { - pthread_mutex_init(&write_lock, NULL); - thread_active=false; - pid = 0; - pin[1] = -1; - nicelevel = 0; - envp = NULL; + init (); + #ifdef PLATFORM_WINDOWS - stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE; - stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE; - stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE; make_wargs(a); #endif make_envp(); } +SystemExec::SystemExec (std::string command, const std::map subs) +{ + init (); + make_argp_escaped(command, subs); + cmd = argp[0]; + // cmd = strdup(argp[0]); + make_envp(); +} + +void +SystemExec::make_argp_escaped(std::string command, const std::map subs) +{ + + int inquotes = 0; + int n = 0; + size_t i = 0; + std::string arg = ""; + + argp = (char **) malloc(sizeof(char *)); + + for (i = 0; i <= command.length(); i++) { // include terminating '\0' + char c = command.c_str()[i]; + if (inquotes) { + if (c == '"') { + inquotes = 0; + } else { + // still in quotes - just copy + arg += c; + } + } else switch (c) { + case '%' : + c = command.c_str()[++i]; + if (c == '%' || c == '\0') { + // "%%", "%" at end-of-string => "%" + arg += '%'; + } else { + // search subs for string to substitute for char + std::map::const_iterator s = subs.find(c); + if (s != subs.end()) { + // found substitution + arg += s->second; + } else { + // not a valid substitution, just copy + arg += '%'; + arg += c; + } + } + break; + case '\\': + c = command.c_str()[++i]; + switch (c) { + case ' ' : + case '"' : arg += c; break; // "\\", "\" at end-of-string => "\" + case '\0': + case '\\': arg += '\\'; break; + default : arg += '\\'; arg += c; break; + } + break; + case '"' : + inquotes = 1; + break; + case ' ' : + case '\t': + case '\0': + if (arg.length() > 0) { + // if there wasn't already a space or tab, start a new parameter + argp = (char **) realloc(argp, (n + 2) * sizeof(char *)); + argp[n++] = strdup (arg.c_str()); + arg = ""; + } + break; + default : + arg += c; + break; + } + } + argp[n] = NULL; + + char *p = argp[0]; + n = 0; + do { + std::cerr << "argv[" << n << "] == \"" << p << "\"" << std::endl; + p = argp[n++]; + } while (p); + +} + SystemExec::~SystemExec () { terminate (); @@ -264,10 +353,16 @@ SystemExec::make_wargs(char **a) { std::string wa = cmd; if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; } std::replace(cmd.begin(), cmd.end(), '/', '\\' ); - char **tmp = a; + char **tmp = ++a; while (tmp && *tmp) { wa.append(" \""); - wa.append(*tmp); + std::string arg(*tmp); + size_t start_pos = 0; + while((start_pos = arg.find("\\", start_pos)) != std::string::npos) { + arg.replace(start_pos, 1, "\\\\"); + start_pos += 2; + } + wa.append(arg); wa.append("\""); tmp++; } @@ -288,6 +383,9 @@ void SystemExec::terminate () { ::pthread_mutex_lock(&write_lock); + + close_stdin(); + if (pid) { /* terminate */ EnumWindows(my_terminateApp, (LPARAM)pid->dwProcessId); @@ -324,7 +422,7 @@ SystemExec::is_running () } int -SystemExec::start (int stderr_mode) +SystemExec::start (int stderr_mode, const char * /*vfork_exec_wrapper*/) { char* working_dir = 0; @@ -414,7 +512,11 @@ SystemExec::output_interposer() if (bytesAvail < 1) {Sleep(500); printf("N/A\n"); continue;} #endif if (stdoutP[0] == INVALID_HANDLE_VALUE) break; - if (!ReadFile(stdoutP[0], data, BUFSIZ, &bytesRead, 0)) break; + if (!ReadFile(stdoutP[0], data, BUFSIZ, &bytesRead, 0)) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) continue; + break; + } if (bytesRead < 1) continue; /* actually not needed; but this is safe. */ data[bytesRead] = 0; ReadStdout(data, bytesRead);/* EMIT SIGNAL */ @@ -506,7 +608,7 @@ SystemExec::make_argp(std::string args) { *cp2 = '\0'; argp[argn++] = strdup(cp1); cp1 = cp2 + 1; - argp = (char **) realloc(argp, (argn + 1) * sizeof(char *)); + argp = (char **) realloc(argp, (argn + 1) * sizeof(char *)); } } if (cp2 != cp1) { @@ -596,7 +698,7 @@ SystemExec::is_running () } int -SystemExec::start (int stderr_mode) +SystemExec::start (int stderr_mode, const char *vfork_exec_wrapper) { if (is_running()) { return 0; // mmh what to return here? @@ -608,7 +710,11 @@ SystemExec::start (int stderr_mode) return -1; } +#ifndef NO_VFORK + r = ::vfork(); +#else r = ::fork(); +#endif if (r < 0) { /* failed to fork */ return -2; @@ -627,11 +733,11 @@ SystemExec::start (int stderr_mode) /* child process returned from execve */ pid=0; close_fd(pok[0]); + close_fd(pok[1]); close_fd(pin[1]); close_fd(pin[0]); close_fd(pout[1]); close_fd(pout[0]); - pin[1] = -1; return -3; } else if ( n==-1 ) { if ( errno==EAGAIN || errno==EINTR ) @@ -642,28 +748,6 @@ SystemExec::start (int stderr_mode) close_fd(pok[0]); /* child started successfully */ -#if 0 -/* use fork for output-interposer - * it will run in a separated process - */ - /* catch stdout thread */ - r = ::fork(); - if (r < 0) { - // failed to fork - terminate(); - return -2; - } - if (r == 0) { - /* 2nd child process - catch stdout */ - close_fd(pin[1]); - close_fd(pout[1]); - output_interposer(); - exit(0); - } - close_fd(pout[1]); - close_fd(pin[0]); - close_fd(pout[0]); -#else /* use pthread */ close_fd(pout[1]); close_fd(pin[0]); int rv = pthread_create(&thread_id_tt, NULL, interposer_thread, this); @@ -674,10 +758,10 @@ SystemExec::start (int stderr_mode) terminate(); return -2; } -#endif return 0; /* all systems go - return to main */ } +#ifdef NO_VFORK /* child process - exec external process */ close_fd(pok[0]); ::fcntl(pok[1], F_SETFD, FD_CLOEXEC); @@ -712,23 +796,13 @@ SystemExec::start (int stderr_mode) ::nice(nicelevel); } -#if 0 - /* chdir to executable dir */ - char *directory; - directory = strdup(cmd.c_str()); - if (strrchr(directory, '/') != (char *) 0) { - ::chdir(directory); - } - free(directory); -#endif - #ifdef HAVE_SIGSET sigset(SIGPIPE, SIG_DFL); #else signal(SIGPIPE, SIG_DFL); #endif - int good_fds[1] = { -1 }; + int good_fds[2] = { pok[1], -1 }; close_allv(good_fds); ::execve(argp[0], argp, envp); @@ -738,6 +812,42 @@ SystemExec::start (int stderr_mode) close_fd(pok[1]); exit(-1); return -1; +#else + + /* XXX this should be done before vfork() + * calling malloc here only increases the time vfork() blocks + */ + int argn = 0; + for (int i=0;argp[i];++i) { argn++; } + char **argx = (char **) malloc((argn + 10) * sizeof(char *)); + argx[0] = strdup(vfork_exec_wrapper); // XXX + +#define FDARG(NUM, FDN) \ + argx[NUM] = (char*) calloc(6, sizeof(char)); snprintf(argx[NUM], 6, "%d", FDN); + + FDARG(1, pok[0]) + FDARG(2, pok[1]) + FDARG(3, pin[0]) + FDARG(4, pin[1]) + FDARG(5, pout[0]) + FDARG(6, pout[1]) + FDARG(7, stderr_mode) + FDARG(8, nicelevel) + + for (int i=0;argp[i];++i) { + argx[9+i] = argp[i]; + } + argx[argn+9] = NULL; + + ::execve(argx[0], argx, envp); + + /* if we reach here something went wrong.. */ + char buf = 0; + (void) ::write(pok[1], &buf, 1 ); + close_fd(pok[1]); + exit(-1); + return -1; +#endif } void