X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Fsystem_exec.cc;h=351e4ee066ae118f2f69e0e64de6cf2b90715f42;hb=db981b6085d3557d82178d003368905b48608ff0;hp=ac0d37f0d0ef966e4bbbd43d0b7fd54a60f88c81;hpb=f6129ed0f4c4e485a22093e833d420b13a0af0cb;p=ardour.git diff --git a/libs/pbd/system_exec.cc b/libs/pbd/system_exec.cc index ac0d37f0d0..351e4ee066 100644 --- a/libs/pbd/system_exec.cc +++ b/libs/pbd/system_exec.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -42,16 +43,24 @@ #include #endif +#include +#define USE_VFORK + +#include "pbd/file_utils.h" +#include "pbd/search_path.h" #include "pbd/system_exec.h" using namespace std; using namespace PBD; static void * interposer_thread (void *arg); + +#ifndef PLATFORM_WINDOWS /* POSIX Process only */ static void close_fd (int& fd) { if (fd >= 0) ::close (fd); fd = -1; } +#endif -#ifndef PLATFORM_WINDOWS +#if (!defined PLATFORM_WINDOWS && defined NO_VFORK) /* * This function was part of libasyncns. * LGPL v2.1 @@ -146,11 +155,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 +166,20 @@ 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; + w_args = NULL; #endif +} + +SystemExec::SystemExec (std::string c, std::string a) + : cmd(c) +{ + init (); + + argp = NULL; make_envp(); make_argp(a); } @@ -171,33 +187,137 @@ 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); + +#ifdef PLATFORM_WINDOWS + if (argp[0] && strlen (argp[0]) > 0) { + std::string wa = argp[0]; + // only add quotes to command if required.. + if (argp[0][0] != '"' + && argp[0][strlen(argp[0])-1] != '"' + && strchr(argp[0], ' ')) { + wa = "\""; + wa += argp[0]; + wa += "\""; + } + // ...but always quote all args + for (int i = 1; argp[i]; ++i) { + std::string tmp (argp[i]); + while (tmp.find("\"") != std::string::npos) + tmp.replace(tmp.find("\""), 1, "\\\""); + wa += " \""; + wa += tmp; + wa += '"'; + } + w_args = strdup(wa.c_str()); + } +#else + if (find_file (Searchpath (Glib::getenv ("PATH")), argp[0], cmd)) { + // argp[0] exists in $PATH` - set it to the actual path where it was found + free (argp[0]); + argp[0] = strdup(cmd.c_str ()); + } + // else argp[0] not found in path - leave it as-is, it might be an absolute path + + // Glib::find_program_in_path () is only available in Glib >= 2.28 + // cmd = Glib::find_program_in_path (argp[0]); +#endif + 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; +} + SystemExec::~SystemExec () { terminate (); if (envp) { for (int i=0;envp[i];++i) { - free(envp[i]); + free(envp[i]); } free (envp); } if (argp) { for (int i=0;argp[i];++i) { - free(argp[i]); + free(argp[i]); } free (argp); } @@ -215,6 +335,22 @@ interposer_thread (void *arg) { return 0; } +string +SystemExec::to_s () const +{ +#ifdef PLATFORM_WINDOWS + return string (w_args ? w_args : ""); +#else + stringstream out; + if (argp) { + for (int i = 0; argp[i]; ++i) { + out << argp[i] << " "; + } + } + return out.str(); +#endif +} + #ifdef PLATFORM_WINDOWS /* Windows Process */ /* HELPER FUNCTIONS */ @@ -264,10 +400,13 @@ 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); + if (strlen(*tmp) > 0 && (*tmp)[strlen(*tmp) - 1] == '\\') { + wa.append("\\"); + } wa.append("\""); tmp++; } @@ -288,6 +427,9 @@ void SystemExec::terminate () { ::pthread_mutex_lock(&write_lock); + + close_stdin(); + if (pid) { /* terminate */ EnumWindows(my_terminateApp, (LPARAM)pid->dwProcessId); @@ -311,8 +453,7 @@ int SystemExec::wait (int options) { while (is_running()) { - WaitForSingleObject(pid->hProcess, INFINITE); - Sleep(20); + WaitForSingleObject(pid->hProcess, 40); } return 0; } @@ -320,11 +461,16 @@ SystemExec::wait (int options) bool SystemExec::is_running () { - return pid?true:false; + if (!pid) return false; + DWORD exit_code; + if (GetExitCodeProcess(pid->hProcess, &exit_code)) { + if (exit_code == STILL_ACTIVE) return true; + } + return false; } int -SystemExec::start (int stderr_mode) +SystemExec::start (int stderr_mode, const char * /*vfork_exec_wrapper*/) { char* working_dir = 0; @@ -414,12 +560,17 @@ 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 */ } Terminated();/* EMIT SIGNAL */ + pthread_exit(0); } void @@ -506,7 +657,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) { @@ -530,7 +681,7 @@ SystemExec::terminate () close_stdin(); if (pid) { - ::usleep(50000); + ::usleep(200000); sched_yield(); wait(WNOHANG); } @@ -538,10 +689,10 @@ SystemExec::terminate () /* if pid is non-zero, the child task is still executing (i.e. it did * not exit in response to stdin being closed). try to kill it. */ - + if (pid) { ::kill(pid, SIGTERM); - usleep(50000); + ::usleep(250000); sched_yield(); wait(WNOHANG); } @@ -558,6 +709,7 @@ SystemExec::terminate () wait(); if (thread_active) pthread_join(thread_id_tt, NULL); thread_active = false; + assert(pid == 0); ::pthread_mutex_unlock(&write_lock); } @@ -596,7 +748,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 +760,7 @@ SystemExec::start (int stderr_mode) return -1; } -#ifdef USE_VFORK +#ifndef NO_VFORK r = ::vfork(); #else r = ::fork(); @@ -631,11 +783,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 ) @@ -659,7 +811,7 @@ SystemExec::start (int stderr_mode) return 0; /* all systems go - return to main */ } -#ifndef USE_VFORK +#ifdef NO_VFORK /* child process - exec external process */ close_fd(pok[0]); ::fcntl(pok[1], F_SETFD, FD_CLOEXEC); @@ -699,6 +851,10 @@ SystemExec::start (int stderr_mode) #else signal(SIGPIPE, SIG_DFL); #endif + if (!vfork_exec_wrapper) { + error << _("Cannot start external process, no vfork wrapper") << endmsg; + return -1; + } int good_fds[2] = { pok[1], -1 }; close_allv(good_fds); @@ -713,12 +869,12 @@ SystemExec::start (int stderr_mode) #else /* XXX this should be done before vfork() - * calling malloc here only increases the time we vfork() blocks + * 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("/home/rgareus/src/git/ardourCairoCanvas/tools/exec_wrapper"); // XXX TODO + argx[0] = strdup(vfork_exec_wrapper); // XXX #define FDARG(NUM, FDN) \ argx[NUM] = (char*) calloc(6, sizeof(char)); snprintf(argx[NUM], 6, "%d", FDN); @@ -761,7 +917,16 @@ SystemExec::output_interposer() for (;fcntl(rfd, F_GETFL)!=-1;) { r = read(rfd, buf, sizeof(buf)); if (r < 0 && (errno == EINTR || errno == EAGAIN)) { - ::usleep(1000); + fd_set rfds; + struct timeval tv; + FD_ZERO(&rfds); + FD_SET(rfd, &rfds); + tv.tv_sec = 0; + tv.tv_usec = 10000; + int rv = select(1, &rfds, NULL, NULL, &tv); + if (rv == -1) { + break; + } continue; } if (r <= 0) { @@ -772,6 +937,7 @@ SystemExec::output_interposer() ReadStdout(rv, r);/* EMIT SIGNAL */ } Terminated();/* EMIT SIGNAL */ + pthread_exit(0); } void