diff options
Diffstat (limited to 'gtk2_ardour/system_exec.cc')
-rw-r--r-- | gtk2_ardour/system_exec.cc | 811 |
1 files changed, 0 insertions, 811 deletions
diff --git a/gtk2_ardour/system_exec.cc b/gtk2_ardour/system_exec.cc deleted file mode 100644 index 034ba90e40..0000000000 --- a/gtk2_ardour/system_exec.cc +++ /dev/null @@ -1,811 +0,0 @@ -/* - Copyright (C) 2010 Paul Davis - Copyright 2005-2008 Lennart Poettering - Author: Robin Gareus <robin@gareus.org> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> - -#include <assert.h> - -#ifndef COMPILER_MSVC -#include <dirent.h> -#endif - -#ifdef PLATFORM_WINDOWS -#include <windows.h> -#else -#include <fcntl.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <sys/resource.h> -#endif - - -#include "system_exec.h" - -using namespace std; -void * interposer_thread (void *arg); - -static void close_fd (int& fd) { if (fd >= 0) ::close (fd); fd = -1; } - -#ifndef PLATFORM_WINDOWS -/* - * This function was part of libasyncns. - * LGPL v2.1 - * Copyright 2005-2008 Lennart Poettering - */ -static int close_allv(const int except_fds[]) { - struct rlimit rl; - int fd; - -#ifdef __linux__ - - DIR *d; - - assert(except_fds); - - if ((d = opendir("/proc/self/fd"))) { - struct dirent *de; - - while ((de = readdir(d))) { - int found; - long l; - char *e = NULL; - int i; - - if (de->d_name[0] == '.') - continue; - - errno = 0; - l = strtol(de->d_name, &e, 10); - if (errno != 0 || !e || *e) { - closedir(d); - errno = EINVAL; - return -1; - } - - fd = (int) l; - - if ((long) fd != l) { - closedir(d); - errno = EINVAL; - return -1; - } - - if (fd < 3) - continue; - - if (fd == dirfd(d)) - continue; - - found = 0; - for (i = 0; except_fds[i] >= 0; i++) - if (except_fds[i] == fd) { - found = 1; - break; - } - - if (found) continue; - - if (close(fd) < 0) { - int saved_errno; - - saved_errno = errno; - closedir(d); - errno = saved_errno; - - return -1; - } - } - - closedir(d); - return 0; - } - -#endif - - if (getrlimit(RLIMIT_NOFILE, &rl) < 0) - return -1; - - for (fd = 0; fd < (int) rl.rlim_max; fd++) { - int i; - - if (fd <= 3) - continue; - - for (i = 0; except_fds[i] >= 0; i++) - if (except_fds[i] == fd) - continue; - - if (close(fd) < 0 && errno != EBADF) - return -1; - } - - return 0; -} -#endif /* not on windows */ - - -SystemExec::SystemExec (std::string c, std::string a) - : cmd(c) -{ - pthread_mutex_init(&write_lock, NULL); - thread_active=false; - pid = 0; - 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 - make_envp(); - make_argp(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; -#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 () -{ - terminate (); - if (envp) { - for (int i=0;envp[i];++i) { - free(envp[i]); - } - free (envp); - } - if (argp) { - for (int i=0;argp[i];++i) { - free(argp[i]); - } - free (argp); - } -#ifdef PLATFORM_WINDOWS - if (w_args) free(w_args); -#endif - pthread_mutex_destroy(&write_lock); -} - -void * -interposer_thread (void *arg) { - SystemExec *sex = static_cast<SystemExec *>(arg); - sex->output_interposer(); - pthread_exit(0); - return 0; -} - -#ifdef PLATFORM_WINDOWS /* Windows Process */ - -/* HELPER FUNCTIONS */ - -static void create_pipe (HANDLE *pipe, bool in) { - SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE }; - HANDLE tmpHandle; - if (in) { - if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024)) return; - if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) return; - } else { - if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024)) return; - if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) return; - } - CloseHandle(tmpHandle); -} - -static void destroy_pipe (HANDLE pipe[2]) { - if (pipe[0] != INVALID_HANDLE_VALUE) { - CloseHandle(pipe[0]); - pipe[0] = INVALID_HANDLE_VALUE; - } - if (pipe[1] != INVALID_HANDLE_VALUE) { - CloseHandle(pipe[1]); - pipe[1] = INVALID_HANDLE_VALUE; - } -} - -static BOOL CALLBACK my_terminateApp(HWND hwnd, LPARAM procId) -{ - DWORD currentProcId = 0; - GetWindowThreadProcessId(hwnd, ¤tProcId); - if (currentProcId == (DWORD)procId) - PostMessage(hwnd, WM_CLOSE, 0, 0); - return TRUE; -} - -/* PROCESS API */ - -void -SystemExec::make_envp() { - ;/* environemt is copied over with CreateProcess(...,env=0 ,..) */ -} - -void -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; - while (tmp && *tmp) { - wa.append(" \""); - wa.append(*tmp); - wa.append("\""); - tmp++; - } - w_args = strdup(wa.c_str()); -} - -void -SystemExec::make_argp(std::string args) { - std::string wa = cmd; - if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; } - std::replace(cmd.begin(), cmd.end(), '/', '\\' ); - wa.append(" "); - wa.append(args); - w_args = strdup(wa.c_str()); -} - -void -SystemExec::terminate () -{ - ::pthread_mutex_lock(&write_lock); - if (pid) { - /* terminate */ - EnumWindows(my_terminateApp, (LPARAM)pid->dwProcessId); - PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0); - - /* kill ! */ - TerminateProcess(pid->hProcess, 0xf291); - - CloseHandle(pid->hThread); - CloseHandle(pid->hProcess); - destroy_pipe(stdinP); - destroy_pipe(stdoutP); - destroy_pipe(stderrP); - delete pid; - pid=0; - } - ::pthread_mutex_unlock(&write_lock); -} - -int -SystemExec::wait (int options) -{ - while (is_running()) { - WaitForSingleObject(pid->hProcess, INFINITE); - Sleep(20); - } - return 0; -} - -bool -SystemExec::is_running () -{ - return pid?true:false; -} - -int -SystemExec::start (int stderr_mode) -{ - char* working_dir = 0; - - if (pid) { return 0; } - - pid = new PROCESS_INFORMATION; - memset(pid, 0, sizeof(PROCESS_INFORMATION)); - - create_pipe(stdinP, true); - create_pipe(stdoutP, false); - - if (stderr_mode == 2) { - /* merge stout & stderr */ - DuplicateHandle(GetCurrentProcess(), stdoutP[1], GetCurrentProcess(), &stderrP[1], 0, TRUE, DUPLICATE_SAME_ACCESS); - } else if (stderr_mode == 1) { - //TODO read/flush this pipe or close it... - create_pipe(stderrP, false); - } else { - //TODO: keep stderr of this process mode. - } - - bool success = false; - STARTUPINFOA startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0, - (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT, - (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT, - 0, 0, 0, - STARTF_USESTDHANDLES, - 0, 0, 0, - stdinP[0], stdoutP[1], stderrP[1] - }; - - success = CreateProcess(0, w_args, - 0, 0, /* bInheritHandles = */ TRUE, - (CREATE_NO_WINDOW&0) | CREATE_UNICODE_ENVIRONMENT | (0&CREATE_NEW_CONSOLE), - /*env = */ 0, - working_dir, - &startupInfo, pid); - - if (stdinP[0] != INVALID_HANDLE_VALUE) { - CloseHandle(stdinP[0]); - stdinP[0] = INVALID_HANDLE_VALUE; - } - if (stdoutP[1] != INVALID_HANDLE_VALUE) { - CloseHandle(stdoutP[1]); - stdoutP[1] = INVALID_HANDLE_VALUE; - } - if (stderrP[1] != INVALID_HANDLE_VALUE) { - CloseHandle(stderrP[1]); - stderrP[1] = INVALID_HANDLE_VALUE; - } - - if (!success) { - CloseHandle(pid->hThread); - CloseHandle(pid->hProcess); - destroy_pipe(stdinP); - destroy_pipe(stdoutP); - destroy_pipe(stderrP); - delete pid; - pid=0; - return -1; - } - - int rv = pthread_create(&thread_id_tt, NULL, interposer_thread, this); - thread_active=true; - if (rv) { - thread_active=false; - terminate(); - return -2; - } - Sleep(20); - return 0; -} - -void -SystemExec::output_interposer() -{ - DWORD bytesRead = 0; - char data[BUFSIZ]; -#if 0 // untested code to set up nonblocking - unsigned long l = 1; - ioctlsocket(stdoutP[0], FIONBIO, &l); -#endif - while(1) { -#if 0 // for non-blocking pipes.. - DWORD bytesAvail = 0; - PeekNamedPipe(stdoutP[0], 0, 0, 0, &bytesAvail, 0); - 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 (bytesRead < 1) continue; /* actually not needed; but this is safe. */ - data[bytesRead] = 0; - ReadStdout(data, bytesRead);/* EMIT SIGNAL */ - } - Terminated();/* EMIT SIGNAL */ -} - -void -SystemExec::close_stdin() -{ - if (stdinP[0]!= INVALID_HANDLE_VALUE) FlushFileBuffers(stdinP[0]); - if (stdinP[1]!= INVALID_HANDLE_VALUE) FlushFileBuffers(stdinP[1]); - Sleep(200); - destroy_pipe(stdinP); -} - -int -SystemExec::write_to_stdin(std::string d, size_t len) -{ - const char *data; - DWORD r,c; - - ::pthread_mutex_lock(&write_lock); - - data=d.c_str(); - if (len == 0) { - len=(d.length()); - } - c=0; - while (c < len) { - if (!WriteFile(stdinP[1], data+c, len-c, &r, NULL)) { - if (GetLastError() == 0xE8 /*NT_STATUS_INVALID_USER_BUFFER*/) { - Sleep(100); - continue; - } else { - fprintf(stderr, "SYSTEM-EXEC: stdin write error.\n"); - break; - } - } - c += r; - } - ::pthread_mutex_unlock(&write_lock); - return c; -} - - -/* end windows process */ -#else -/* UNIX/POSIX process */ - -extern char **environ; -void -SystemExec::make_envp() { - int i=0; - envp = (char **) calloc(1, sizeof(char*)); - /* copy current environment */ - for (i=0;environ[i];++i) { - envp[i] = strdup(environ[i]); - envp = (char **) realloc(envp, (i+2) * sizeof(char*)); - } - envp[i] = 0; -} - -void -SystemExec::make_argp(std::string args) { - int argn = 1; - char *cp1; - char *cp2; - - char *carg = strdup(args.c_str()); - - argp = (char **) malloc((argn + 1) * sizeof(char *)); - if (argp == (char **) 0) { - free(carg); - return; // FATAL - } - - argp[0] = strdup(cmd.c_str()); - - /* TODO: quotations and escapes - * http://stackoverflow.com/questions/1511797/convert-string-to-argv-in-c - * - * It's actually not needed. All relevant invocations specify 'argp' directly. - * Only 'xjadeo -L -R' uses this function and that uses neither quotations - * nor arguments with white-space. - */ - for (cp1 = cp2 = carg; *cp2 != '\0'; ++cp2) { - if (*cp2 == ' ') { - *cp2 = '\0'; - argp[argn++] = strdup(cp1); - cp1 = cp2 + 1; - argp = (char **) realloc(argp, (argn + 1) * sizeof(char *)); - } - } - if (cp2 != cp1) { - argp[argn++] = strdup(cp1); - argp = (char **) realloc(argp, (argn + 1) * sizeof(char *)); - } - argp[argn] = (char *) 0; - free(carg); -} - - - -void -SystemExec::terminate () -{ - ::pthread_mutex_lock(&write_lock); - - /* close stdin in an attempt to get the child to exit cleanly. - */ - - close_stdin(); - - if (pid) { - ::usleep(50000); - sched_yield(); - wait(WNOHANG); - } - - /* 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); - sched_yield(); - wait(WNOHANG); - } - - /* if pid is non-zero, the child task is STILL executing after being - * sent SIGTERM. Act tough ... send SIGKILL - */ - - if (pid) { - ::fprintf(stderr, "Process is still running! trying SIGKILL\n"); - ::kill(pid, SIGKILL); - } - - wait(); - if (thread_active) pthread_join(thread_id_tt, NULL); - thread_active = false; - ::pthread_mutex_unlock(&write_lock); -} - -int -SystemExec::wait (int options) -{ - int status=0; - int ret; - - if (pid==0) return -1; - - ret = waitpid (pid, &status, options); - - if (ret == pid) { - if (WEXITSTATUS(status) || WIFSIGNALED(status)) { - pid=0; - } - } else { - if (ret != 0) { - if (errno == ECHILD) { - /* no currently running children, reset pid */ - pid=0; - } - } /* else the process is still running */ - } - return status; -} - -bool -SystemExec::is_running () -{ - int status=0; - if (pid==0) return false; - if (::waitpid(pid, &status, WNOHANG)==0) return true; - return false; -} - -int -SystemExec::start (int stderr_mode) -{ - if (is_running()) { - return 0; // mmh what to return here? - } - int r; - - if (::pipe(pin) < 0 || ::pipe(pout) < 0 || ::pipe(pok) < 0) { - /* Something unexpected went wrong creating a pipe. */ - return -1; - } - - r = ::fork(); - if (r < 0) { - /* failed to fork */ - return -2; - } - - if (r > 0) { - /* main */ - pid=r; - - /* check if execve was successful. */ - close_fd(pok[1]); - char buf; - for ( ;; ) { - ssize_t n = ::read(pok[0], &buf, 1 ); - if ( n==1 ) { - /* child process returned from execve */ - pid=0; - close_fd(pok[0]); - 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 ) - continue; - } - break; - } - 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); - - thread_active=true; - if (rv) { - thread_active=false; - terminate(); - return -2; - } -#endif - return 0; /* all systems go - return to main */ - } - - /* child process - exec external process */ - close_fd(pok[0]); - ::fcntl(pok[1], F_SETFD, FD_CLOEXEC); - - close_fd(pin[1]); - if (pin[0] != STDIN_FILENO) { - ::dup2(pin[0], STDIN_FILENO); - } - close_fd(pin[0]); - close_fd(pout[0]); - if (pout[1] != STDOUT_FILENO) { - ::dup2(pout[1], STDOUT_FILENO); - } - - if (stderr_mode == 2) { - /* merge STDERR into output */ - if (pout[1] != STDERR_FILENO) { - ::dup2(pout[1], STDERR_FILENO); - } - } else if (stderr_mode == 1) { - /* ignore STDERR */ - ::close(STDERR_FILENO); - } else { - /* keep STDERR */ - } - - if (pout[1] != STDOUT_FILENO && pout[1] != STDERR_FILENO) { - close_fd(pout[1]); - } - - if (nicelevel !=0) { - ::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 }; - close_allv(good_fds); - - ::execve(argp[0], argp, 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; -} - -void -SystemExec::output_interposer() -{ - int rfd=pout[0]; - char buf[BUFSIZ]; - ssize_t r; - unsigned long l = 1; - - ioctl(rfd, FIONBIO, &l); // set non-blocking I/O - - for (;fcntl(rfd, F_GETFL)!=-1;) { - r = read(rfd, buf, sizeof(buf)); - if (r < 0 && (errno == EINTR || errno == EAGAIN)) { - ::usleep(1000); - continue; - } - if (r <= 0) { - break; - } - buf[r]=0; - std::string rv = std::string(buf,r); // TODO: check allocation strategy - ReadStdout(rv, r);/* EMIT SIGNAL */ - } - Terminated();/* EMIT SIGNAL */ -} - -void -SystemExec::close_stdin() -{ - if (pin[1]<0) return; - close_fd(pin[0]); - close_fd(pin[1]); - close_fd(pout[0]); - close_fd(pout[1]); -} - -int -SystemExec::write_to_stdin(std::string d, size_t len) -{ - const char *data; - ssize_t r; - size_t c; - ::pthread_mutex_lock(&write_lock); - - data=d.c_str(); - if (len == 0) { - len=(d.length()); - } - c=0; - while (c < len) { - for (;;) { - r=::write(pin[1], data+c, len-c); - if (r < 0 && (errno == EINTR || errno == EAGAIN)) { - sleep(1); - continue; - } - if ((size_t) r != (len-c)) { - ::pthread_mutex_unlock(&write_lock); - return c; - } - break; - } - c += r; - } - fsync(pin[1]); - ::pthread_mutex_unlock(&write_lock); - return c; -} - -#endif // end UNIX process |