Use PBD::GlibSemaphore on windows to signal peak
[ardour.git] / gtk2_ardour / system_exec.cc
index 00b212710e63392a610d702523b9eac09c0fc83f..55dbc60bb4c2cf80c367ca1484e453692c07a2dd 100644 (file)
@@ -1,5 +1,6 @@
 /*
     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
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 */
-#ifdef WITH_VIDEOTIMELINE
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <unistd.h>
 
+#include <assert.h>
+#include <dirent.h>
+
 #ifdef __WIN32__
 #include <windows.h>
 #else
 #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 __WIN32__
+/*
+ * 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)
 {
@@ -293,6 +399,10 @@ 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;
@@ -409,18 +519,33 @@ 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(100000);
+               ::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) {
-               ::fprintf(stderr, "Child process is running. trying SIGTERM\n");
                ::kill(pid, SIGTERM);
-               ::usleep(10000);
+               ::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);
@@ -436,12 +561,23 @@ int
 SystemExec::wait (int options)
 {
        int status=0;
+       int ret;
+
        if (pid==0) return -1;
-       if (pid==::waitpid(pid, &status, options)) {
-               pid=0;
-       }
-       if (errno == ECHILD) {
-               pid=0;
+
+       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;
 }
@@ -479,18 +615,18 @@ SystemExec::start (int stderr_mode)
                pid=r;
 
                /* check if execve was successful. */
-               ::close(pok[1]);
+               close_fd(pok[1]);
                char buf;
                for ( ;; ) {
-                       int n = ::read(pok[0], &buf, 1 );
+                       ssize_t n = ::read(pok[0], &buf, 1 );
                        if ( n==1 ) {
                                /* child process returned from execve */
                                pid=0;
-                               ::close(pok[0]);
-                               ::close(pin[1]);
-                               ::close(pin[0]);
-                               ::close(pout[1]);
-                               ::close(pout[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 ) {
@@ -499,7 +635,7 @@ SystemExec::start (int stderr_mode)
                        }
                        break;
                }
-               ::close(pok[0]);
+               close_fd(pok[0]);
                /* child started successfully */
 
 #if 0
@@ -515,17 +651,17 @@ SystemExec::start (int stderr_mode)
                }
                if (r == 0) {
                        /* 2nd child process - catch stdout */
-                       ::close(pin[1]);
-                       ::close(pout[1]);
+                       close_fd(pin[1]);
+                       close_fd(pout[1]);
                        output_interposer();
                        exit(0);
                }
-               ::close(pout[1]);
-               ::close(pin[0]);
-               ::close(pout[0]);
+               close_fd(pout[1]);
+               close_fd(pin[0]);
+               close_fd(pout[0]);
 #else /* use pthread */
-               ::close(pout[1]);
-               ::close(pin[0]);
+               close_fd(pout[1]);
+               close_fd(pin[0]);
                int rv = pthread_create(&thread_id_tt, NULL, interposer_thread, this);
 
                thread_active=true;
@@ -539,15 +675,15 @@ SystemExec::start (int stderr_mode)
        }
 
        /* child process - exec external process */
-       ::close(pok[0]);
+       close_fd(pok[0]);
        ::fcntl(pok[1], F_SETFD, FD_CLOEXEC);
 
-       ::close(pin[1]);
+       close_fd(pin[1]);
        if (pin[0] != STDIN_FILENO) {
          ::dup2(pin[0], STDIN_FILENO);
        }
-       ::close(pin[0]);
-       ::close(pout[0]);
+       close_fd(pin[0]);
+       close_fd(pout[0]);
        if (pout[1] != STDOUT_FILENO) {
                ::dup2(pout[1], STDOUT_FILENO);
        }
@@ -565,7 +701,7 @@ SystemExec::start (int stderr_mode)
        }
 
        if (pout[1] != STDOUT_FILENO && pout[1] != STDERR_FILENO) {
-               ::close(pout[1]);
+               close_fd(pout[1]);
        }
 
        if (nicelevel !=0) {
@@ -588,11 +724,16 @@ SystemExec::start (int stderr_mode)
        signal(SIGPIPE, SIG_DFL);
 #endif
 
+#ifndef __WIN32__
+       int good_fds[1] = { 0 };
+       close_allv(good_fds);
+#endif
+
        ::execve(argp[0], argp, envp);
        /* if we reach here something went wrong.. */
        char buf = 0;
        (void) ::write(pok[1], &buf, 1 );
-       (void) ::close(pok[1]);
+       close_fd(pok[1]);
        exit(-1);
        return -1;
 }
@@ -602,7 +743,11 @@ SystemExec::output_interposer()
 {
        int rfd=pout[0];
        char buf[BUFSIZ];
-       size_t r;
+       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)) {
@@ -623,18 +768,18 @@ void
 SystemExec::close_stdin()
 {
        if (pin[1]<0) return;
-       ::close(pin[0]);
-       ::close(pin[1]);
-       ::close(pout[0]);
-       ::close(pout[1]);
-       pin[1] = - 1; // mark as closed
+       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;
-       size_t r,c;
+       ssize_t r;
+       size_t c;
        ::pthread_mutex_lock(&write_lock);
 
        data=d.c_str();
@@ -649,7 +794,7 @@ SystemExec::write_to_stdin(std::string d, size_t len)
                                sleep(1);
                                continue;
                        }
-                       if (r != (len-c)) {
+                       if ((size_t) r != (len-c)) {
                                ::pthread_mutex_unlock(&write_lock);
                                return c;
                        }
@@ -663,5 +808,3 @@ SystemExec::write_to_stdin(std::string d, size_t len)
 }
 
 #endif // end UNIX process
-
-#endif /* WITH_VIDEOTIMELINE */