Author: mturk
Date: Thu Jan  7 06:24:35 2010
New Revision: 896761

URL: http://svn.apache.org/viewvc?rev=896761&view=rev
Log:
Implement detached exec

Modified:
    commons/sandbox/runtime/trunk/src/main/native/configure
    commons/sandbox/runtime/trunk/src/main/native/include/acr_exec.h
    commons/sandbox/runtime/trunk/src/main/native/include/acr_signals.h
    commons/sandbox/runtime/trunk/src/main/native/include/acr_string.h
    commons/sandbox/runtime/trunk/src/main/native/os/unix/exec.c
    commons/sandbox/runtime/trunk/src/main/native/os/unix/signals.c
    commons/sandbox/runtime/trunk/src/main/native/os/win32/exec.c
    commons/sandbox/runtime/trunk/src/main/native/os/win32/signals.c
    commons/sandbox/runtime/trunk/src/main/native/shared/args.c
    commons/sandbox/runtime/trunk/src/main/native/shared/sigaction.c
    commons/sandbox/runtime/trunk/src/main/native/shared/string.c
    commons/sandbox/runtime/trunk/src/main/native/test/testsuite.c

Modified: commons/sandbox/runtime/trunk/src/main/native/configure
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/configure?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/configure (original)
+++ commons/sandbox/runtime/trunk/src/main/native/configure Thu Jan  7 06:24:35 
2010
@@ -1207,6 +1207,7 @@
 #define HAVE_SIGSETJMP        `have_function x sigsetjmp`
 #define HAVE_SIGACTION        `have_function x sigaction`
 #define HAVE_FDWALK           `have_function x fdwalk`
+#define HAVE_SETRLIMIT        `have_function x setrlimit`
 #define HAVE_DUP3             `have_function c dup3`
 #define HAVE_EPOLL_CREATE1    `have_function c epoll_create1`
 #define HAVE_ACCEPT4          `have_function c accept4`

Modified: commons/sandbox/runtime/trunk/src/main/native/include/acr_exec.h
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/include/acr_exec.h?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/include/acr_exec.h (original)
+++ commons/sandbox/runtime/trunk/src/main/native/include/acr_exec.h Thu Jan  7 
06:24:35 2010
@@ -31,6 +31,25 @@
  *
  */
 
+/** Process limits
+ */
+typedef struct acr_proc_limit_t {
+    /** Process timeout  */
+    acr_time_t     timeout;
+#ifdef RLIMIT_CPU
+    struct rlimit *cpu;
+#endif
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)
+    struct rlimit *mem;
+#endif
+#ifdef RLIMIT_NPROC
+    struct rlimit *nproc;
+#endif
+#ifdef RLIMIT_NOFILE
+    struct rlimit *nofile;
+#endif
+} acr_proc_limit_t;
+
 /** The ACR process type */
 typedef struct acr_exec_t {
     /** Process id */
@@ -41,21 +60,32 @@
     acr_sbuf_t          serr;
     /** Data for  STDIN  */
     struct iovec        data;
-    /** Process timeout  */
-    acr_time_t          timeout;
     /** Process flags    */
     int                 flags;
+    /** Process limits */
+    acr_proc_limit_t    limit;
+    /** User Id for child process */
+    acr_uid_t           uid;
+    /** Group Id for child process */
+    acr_gid_t           gid;
+    /** User logon password.
+     * Used for Windows and su mode.
+     */
+    acr_pchar_t        *password;
+    /** Process working directory */
+    acr_pchar_t        *currdir;
+
     /** Process exit value  */
     int                 exitval;
     /** Process exit reason  */
     int                 exitwhy;
-    /** Process directory */
-    acr_pchar_t        *currdir;
 } acr_exec_t;
 
 #define ACR_PROC_HAS_STDIN      0x0001
 #define ACR_PROC_HAS_STDOUT     0x0002
 #define ACR_PROC_HAS_STDERR     0x0004
+#define ACR_PROC_USE_PATH       0x0010
+#define ACR_PROC_DETACHED       0x0100
 
 /**
  * Create new executable object structure
@@ -94,9 +124,21 @@
  *             environment of the calling process is used.
  * @return Exit reason.
  */
-ACR_DECLARE(int) ACR_ExecShellScript(acr_exec_t *ep, const acr_pchar_t *file,
+ACR_DECLARE(int) ACR_ExecShellScript(acr_exec_t *exe, const acr_pchar_t *file,
                                      const acr_pchar_t *const *argv,
                                      acr_pchar_t *const *envp);
+/**
+ * Execute program.
+ * @param exe The executable object
+ * @param executable Program to execute
+ * @param argv Parameters for the program.
+ * @param envp Optional environment passed to the program. If null the current
+ *             environment of the calling process is used.
+ * @return Exit reason.
+ */
+ACR_DECLARE(int) ACR_ExecProgram(acr_exec_t *ep, const char *executable,
+                                 const char *const *argv,
+                                 char *const *envp);
 
 /**
  * Get executed program output stream.

Modified: commons/sandbox/runtime/trunk/src/main/native/include/acr_signals.h
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/include/acr_signals.h?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/include/acr_signals.h 
(original)
+++ commons/sandbox/runtime/trunk/src/main/native/include/acr_signals.h Thu Jan 
 7 06:24:35 2010
@@ -63,11 +63,9 @@
  * Set the signal handler function for a given signal
  * @param signo The signal (eg... SIGHUP)
  * @param function The function to get called on signal.
- * @param flags Signal handler flags.
  * @return previous signal handler function
  */
-ACR_DECLARE(acr_sigfunc_t *) ACR_Signal(int signo, acr_sigfunc_t *func,
-                                        int flags);
+ACR_DECLARE(acr_sigfunc_t *) ACR_Signal(int signo, acr_sigfunc_t *func);
 
 /**
  * Set default signal handler function for a given signal
@@ -78,7 +76,18 @@
  *       for a given signal bypassing the original signal handler process
  *       might have setup before signal initialization took place.
  */
-ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_DFL(int signo, int flags);
+ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_DFL(int signo);
+
+/**
+ * Ignore signal handler function for a given signal
+ * @param signo The signal (eg... SIGHUP)
+ * @param flags Signal handler flags.
+ * @return previous signal handler function
+ * @remark Function sets the os default signal handler forcing SIG_IGN
+ *       for a given signal bypassing the original signal handler process
+ *       might have setup before signal initialization took place.
+ */
+ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_IGN(int signo);
 
 /**
  * Block the signal

Modified: commons/sandbox/runtime/trunk/src/main/native/include/acr_string.h
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/include/acr_string.h?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/include/acr_string.h 
(original)
+++ commons/sandbox/runtime/trunk/src/main/native/include/acr_string.h Thu Jan  
7 06:24:35 2010
@@ -87,6 +87,27 @@
  */
 ACR_DECLARE(char *)ACR_GetJavaStringA(JNIEnv *env, jstring s, char *b);
 
+/** Convert java string array to platform char string array.
+ * @param env Current JNI environment.
+ * @param a String array to convert.
+ * @remark When done use ACR_FreeStringArrayA to free the allocated memory.
+ */
+ACR_DECLARE(char **) ACR_GetJavaStringArrayA(JNIEnv *env, jobjectArray a);
+
+/** Convert java string array to wide char string array.
+ * @param env Current JNI environment.
+ * @param a String array to convert.
+ * @remark When done use ACR_FreeStringArrayW to free the allocated memory.
+ */
+ACR_DECLARE(wchar_t **) ACR_GetJavaStringArrayW(JNIEnv *env, jobjectArray a);
+
+/** Free allocated memory used by string array.
+ * @param arr Array to free.
+ * @remark Use this function only for arrays allocated using
+ *         ACR_GetJavaStringArray.
+ */
+ACR_DECLARE(void) ACR_FreeStringArray(void **arr);
+
 /** Convert wchar_t to java string
  * @param env Current JNI environment.
  * @param s String to convert.

Modified: commons/sandbox/runtime/trunk/src/main/native/os/unix/exec.c
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/os/unix/exec.c?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/os/unix/exec.c (original)
+++ commons/sandbox/runtime/trunk/src/main/native/os/unix/exec.c Thu Jan  7 
06:24:35 2010
@@ -29,6 +29,7 @@
 #include "acr_time.h"
 #include "acr_args.h"
 #include "acr_exec.h"
+#include "acr_env.h"
 
 /**
  * Posix process execution functions
@@ -50,19 +51,12 @@
 #define PIPE_STDERR         4
 #define PIPE_STDERR_RDS     4
 #define PIPE_STDERR_WRS     5
-#define PIPE_SIGNAL         6
-#define PIPE_SIGNAL_RDS     6
-#define PIPE_SIGNAL_WRS     7
-
-/* Internal structure for passing data
- * across the signal pipe
- */
-typedef struct _info_c {
-    pid_t       pid;    /* Pid */
-    int         err;    /* Error from grand child */
-    int         sig;    /* Signal number from child */
-    int         len;    /* Payload data length */
-} _info_c;
+#define PIPE_SIGERR         6
+#define PIPE_SIGERR_RDS     6
+#define PIPE_SIGERR_WRS     7
+#define PIPE_SIGPID         8
+#define PIPE_SIGPID_RDS     8
+#define PIPE_SIGPID_WRS     9
 
 ACR_DECLARE(acr_exec_t *) ACR_ExecNew(int flags)
 {
@@ -71,9 +65,8 @@
 
     if (!(ep = s_calloc(acr_exec_t, 1)))
         return NULL;
-    ep->pid     = -1;
-    ep->timeout = -1;
-    ep->flags   = flags;
+    ep->pid   = -1;
+    ep->flags = flags;
 
     if (flags & ACR_PROC_HAS_STDOUT) {
         if (!acr_sbuf_new(&ep->sout, NULL, PROC_BUFFER_SIZE,
@@ -100,6 +93,53 @@
     return NULL;
 }
 
+static int limit_proc(acr_proc_limit_t *limit)
+{
+#if HAVE_SETRLIMIT
+#ifdef RLIMIT_CPU
+    if (limit->cpu != NULL) {
+        if ((setrlimit(RLIMIT_CPU, limit->cpu)) != 0) {
+            return errno;
+        }
+    }
+#endif
+#ifdef RLIMIT_NPROC
+    if (limit->nproc != NULL) {
+        if ((setrlimit(RLIMIT_NPROC, limit->nproc)) != 0) {
+            return errno;
+        }
+    }
+#endif
+#ifdef RLIMIT_NOFILE
+    if (limit->nofile != NULL) {
+        if ((setrlimit(RLIMIT_NOFILE,limit->nofile)) != 0) {
+            return errno;
+        }
+    }
+#endif
+#if defined(RLIMIT_AS)
+    if (limit->mem != NULL) {
+        if ((setrlimit(RLIMIT_AS, limit->mem)) != 0) {
+            return errno;
+        }
+    }
+#elif defined(RLIMIT_DATA)
+    if (limit->mem != NULL) {
+        if ((setrlimit(RLIMIT_DATA, limit->mem)) != 0) {
+            return errno;
+        }
+    }
+#elif defined(RLIMIT_VMEM)
+    if (limit->mem != NULL) {
+        if ((setrlimit(RLIMIT_VMEM, limit->mem)) != 0) {
+            return errno;
+        }
+    }
+#endif
+#endif
+    return 0;
+}
+
 static int do_exec(acr_exec_t *ep, const char *cmdline,
                    char **argv,
                    char *const *envp)
@@ -109,16 +149,26 @@
     int i;
     int rc = 0;
     int exitval;
-    int pipes[8] = { -1, -1, -1, -1,  -1, -1, -1, -1 };
-    _info_c info = { -1,  0,  0,  0 };
+    int pipes[10] = { -1, -1, -1, -1,  -1, -1, -1, -1, -1, -1 };
+    int   sigerr  = 0;
+    pid_t sigpid  = 0;
 
     /* By default process terminates when writting to a
      * pipe with no readers.
      * Ignore SIGPIPE
      */
-    ACR_Signal(SIGPIPE, SIG_IGN, SA_RESTART);
+    ACR_Signal(SIGPIPE, SIG_IGN);
 
-    /* Create pipes */
+    /* Sanity check.
+     * Daemons have no stream pipes
+     */
+    if (ep->flags & ACR_PROC_DETACHED) {
+        ep->flags &= ~(ACR_PROC_HAS_STDIN  |
+                       ACR_PROC_HAS_STDOUT |
+                       ACR_PROC_HAS_STDERR);
+    }
+    /* Create standard stream pipes     
+     */
     if (ep->flags & ACR_PROC_HAS_STDIN && ep->data.iov_len) {
         if ((rc = pipepair(&pipes[PIPE_STDINP], ACR_PIPE_READ_BLOCK)))
             goto cleanup;
@@ -132,9 +182,15 @@
             goto cleanup;
     }
     /* Create signaling pipe that is used both for reporting the
-     * failed execv and syncing with execv.
+     * failed exec and syncing with exec.
      */
-    if ((rc = pipepair(&pipes[PIPE_SIGNAL], ACR_PIPE_FULL_BLOCK)))
+    if ((rc = pipepair(&pipes[PIPE_SIGERR], ACR_PIPE_FULL_BLOCK)))
+        goto cleanup;
+    /* In case of daemon process, create a signaling pipe that is used
+     * for reporting the granchild's pid to the parent.
+     */
+    if ((ep->flags & ACR_PROC_DETACHED) &&
+        (rc = pipepair(&pipes[PIPE_SIGPID], ACR_PIPE_FULL_BLOCK)))
         goto cleanup;
 
     pid = fork();
@@ -156,14 +212,19 @@
         i_close(&pipes[PIPE_STDINP_WRS]);
         i_close(&pipes[PIPE_STDOUT_RDS]);
         i_close(&pipes[PIPE_STDERR_RDS]);
-        i_close(&pipes[PIPE_SIGNAL_RDS]);
+        i_close(&pipes[PIPE_SIGERR_RDS]);
+        i_close(&pipes[PIPE_SIGPID_RDS]);
 
-        /* Make sure the signaling pipe is closed on exec.
+        /* Make sure the signaling pipes are closed on exec.
          * This forces parent to wait until actual
          * exec is performed or until the error is
          * written to the signal pipe.
+         * In case of detached process the close 
          */
-        acr_cloexec(pipes[PIPE_SIGNAL_WRS]);
+        if (!(ep->flags & ACR_PROC_DETACHED)) {
+            acr_cloexec(pipes[PIPE_SIGERR_WRS]);
+            acr_cloexec(pipes[PIPE_SIGPID_WRS]);
+        }
 
         if (ep->currdir && chdir(ep->currdir) == -1) {
             /* Failed changing the current directoty */
@@ -172,7 +233,7 @@
         }
         /* Set the real os SIGCHLD child handler.
          */
-        ACR_SIG_DFL(SIGCHLD, SA_RESTART);
+        ACR_SIG_DFL(SIGCHLD);
 
         close(STDIN_FILENO);
         if (pipes[PIPE_STDINP_RDS] == -1) {
@@ -188,6 +249,7 @@
             rc = ACR_GET_OS_ERROR();
             goto child_cleanup;
         }
+        i_close(&pipes[PIPE_STDINP_RDS]);
 
         close(STDOUT_FILENO);
         close(STDERR_FILENO);
@@ -214,21 +276,101 @@
         }
         i_close(&pipes[PIPE_STDOUT_WRS]);
         i_close(&pipes[PIPE_STDERR_WRS]);
-        i_close(&pipes[PIPE_SIGNAL_WRS]);
+
+        /* Only try to switch if we are running as root
+         */
+        if (ep->gid != -1 && geteuid() == (uid_t)0) {
+            if (setgid(ep->gid)) {
+                rc = ACR_GET_OS_ERROR();
+                goto child_cleanup;
+            }
+        }
+        if (ep->uid != -1 && geteuid() == (uid_t)0) {
+            if (setuid(ep->uid)) {
+                rc = ACR_GET_OS_ERROR();
+                goto child_cleanup;
+            }
+        }
+        if ((rc = limit_proc(&ep->limit)))
+            goto child_cleanup;
+
         if (argv == NULL)
             argv = (char **)args;
+        if (ep->flags & ACR_PROC_DETACHED) {
+            /* Time to do detach the process.
+             */
+
+            /* Should this be configurable ?
+             */            
+            umask(0077);
+
+            if (chdir("/") == -1) {
+                rc = ACR_GET_OS_ERROR();
+                goto child_cleanup;
+            }
+            /* Second fork
+             */
+            pid = fork();
+            if (pid < 0) {
+                /* Second fork failed.
+                 * Time to bail out
+                 */
+                rc = ACR_GET_OS_ERROR();
+                goto child_cleanup;
+            }
+            else if (pid == 0) {
+                /* We are inside grand child process
+                 */
+                acr_cloexec(pipes[PIPE_SIGERR_WRS]);
+                acr_cloexec(pipes[PIPE_SIGPID_WRS]);
+
+                if (setsid() == -1) {
+                    rc = ACR_GET_OS_ERROR();
+                    goto child_cleanup;
+                }
+                /* Setup daemon signaling.
+                 */
+                if (ACR_SIG_IGN(SIGTTOU) == SIG_ERR) {
+                    rc = ACR_GET_OS_ERROR();
+                    goto child_cleanup;
+                }
+                if (ACR_SIG_IGN(SIGTTIN) == SIG_ERR) {
+                    rc = ACR_GET_OS_ERROR();
+                    goto child_cleanup;
+                }
+                if (ACR_SIG_IGN(SIGTSTP) == SIG_ERR) {
+                    rc = ACR_GET_OS_ERROR();
+                    goto child_cleanup;
+                }
+
+                /* Report our pid to the parent
+                 */
+                pid = getpid();
+                f_write(pipes[PIPE_SIGPID_WRS], &pid, sizeof(pid_t));
+            }
+            else {
+                /* We are done with the master child.
+                 */
+                exit(0);
+            }
+        }
         if (envp)
             execve(argv[0], (char * const *)argv, envp);
-        else
-            execv(argv[0],  (char * const *)argv);
+        else {
+            if (ep->flags & ACR_PROC_USE_PATH) {
+                if (!getenv("PATH"))
+                    ACR_EnvSet("PATH", DEFAULT_PATH);
+                execvp(argv[0], (char * const *)argv);
+            }
+            else
+                execv(argv[0],  (char * const *)argv);
+        }
         rc = ACR_GET_OS_ERROR();
 
 child_cleanup:
         /* Still here?
          */
-        info.pid = getpid();
-        info.err = rc;
-        f_write(pipes[PIPE_SIGNAL_WRS], &info, sizeof(info));
+        f_write(pipes[PIPE_SIGERR_WRS], &rc, sizeof(int));
         /* Alternative to exit() could be
          * kill(info.pid, 9);
          */
@@ -237,38 +379,54 @@
     else {
         /* Parent process */
         struct pollfd ps[4];
-        int    npipes    = 0;
-        int    running   = ep->flags & (ACR_PROC_HAS_STDOUT | 
ACR_PROC_HAS_STDERR);
-        pid_t  child     = pid;
-        const char *inpp = (const char *)ep->data.iov_base;
-        int    polltime  = ep->timeout > 0 ? PROC_TIMEOUT_STEP : -1;
-        acr_time_t endat = 0;
+        int    npipes     = 0;
+        int    pipelining = ep->flags & (ACR_PROC_HAS_STDOUT | 
ACR_PROC_HAS_STDERR);
+        pid_t  child      = pid;
+        const char *inpp  = (const char *)ep->data.iov_base;
+        int    polltime   = ep->limit.timeout > 0 ? PROC_TIMEOUT_STEP : -1;
+        acr_time_t endat  = 0;
         /* Close parent side of pipes
          */
         i_close(&pipes[PIPE_STDINP_RDS]);
         i_close(&pipes[PIPE_STDOUT_WRS]);
         i_close(&pipes[PIPE_STDERR_WRS]);
-        i_close(&pipes[PIPE_SIGNAL_WRS]);
+        i_close(&pipes[PIPE_SIGERR_WRS]);
+        i_close(&pipes[PIPE_SIGPID_WRS]);
 
-        if (ep->timeout > 0)
-            endat = ACR_TimeNow() + ep->timeout;
+        if (ep->limit.timeout > 0)
+            endat = ACR_TimeNow() + ep->limit.timeout;
         /* Handshake with the child process.
          * If the read is sucessful it means that
          * either init or execv inside child failed.
+         * However in case of detached proces this
+         * is valid struct informing about grandchild pid.
          */
-        rd = r_read(pipes[PIPE_SIGNAL_RDS], &info, sizeof(info));
-        i_close(&pipes[PIPE_SIGNAL_RDS]);
-        if (rd == sizeof(info)) {
+        rd = r_read(pipes[PIPE_SIGERR_RDS], &sigerr, sizeof(int));
+        i_close(&pipes[PIPE_SIGERR_RDS]);
+        if (rd == sizeof(int)) {
             /* We have received error status
              * from the child
              */
-            running = 0;
+            pipelining = 0;
         }
         else {
             /* Guard againts partial reads */
-            info.err = 0;
+            sigerr = 0;
+        }
+        if (ep->flags & ACR_PROC_DETACHED) {
+            rd = r_read(pipes[PIPE_SIGPID_RDS], &sigpid, sizeof(pid_t));
+            i_close(&pipes[PIPE_SIGPID_RDS]);
+            if (rd == sizeof(pid_t)) {
+                /* We have received the grand child's pid.
+                 */
+                pipelining = 0;
+            }
+            else {
+                /* Guard againts partial reads */
+                sigpid = 0;
+            }
         }
-        while (running) {
+        while (pipelining) {
             npipes = 0;
             if (pipes[PIPE_STDINP_WRS] != -1) {
                 ps[npipes].fd = pipes[PIPE_STDOUT_WRS];
@@ -296,7 +454,8 @@
                 if (rc < 1) {
                     if (polltime == -1 || rc == -1)
                         break;
-                    /* No events fired within a second
+                    /* No events fired within PROC_TIMEOUT_STEP.
+                     * Check if we reached the abolute
                      */
                     if (ACR_TimeNow() < endat)
                         continue;
@@ -356,10 +515,11 @@
             else {
                 /* All pipes are closed
                  */
-                running = 0;
+                pipelining = 0;
             }
         }
-        /* Finished reading child output streams
+        /* Finished child stream processing.
+         * Cleanup pipes.
          */
         i_close(&pipes[PIPE_STDINP_WRS]);
         i_close(&pipes[PIPE_STDOUT_RDS]);
@@ -368,15 +528,36 @@
             child = waitpid(pid, &exitval, WUNTRACED);
         } while (child == -1 && errno == EINTR);
 
-        if (info.err) {
+        if (sigerr) {
             /* Error reported from child
              */
             ep->exitwhy = ACR_CHILD_ERROR;
-            ep->exitval = info.err;
-            ep->pid     = info.pid;
+            ep->exitval = sigerr;
+            ep->pid     = sigpid ? sigpid : pid;
 
             return ep->exitwhy;
         }
+        if (ep->flags & ACR_PROC_DETACHED) {
+            if (sigpid) {
+                /* We have a pid from the grand child
+                 */
+                ep->exitwhy = ACR_CHILD_DONE;
+                ep->exitval = 0;
+                ep->pid     = sigpid;
+
+                return ep->exitwhy;
+            }
+            else {
+                /* We didn't recive the valid grandchild's pid
+                 * XXX: Check for a valid return value
+                 */
+                ep->exitwhy = ACR_CHILD_ERROR;
+                ep->exitval = ACR_EGENERAL;
+                ep->pid     = pid;
+
+                return ep->exitwhy;
+            }
+        }
         switch (child) {
             case 0:
                 /* Unexpected condition
@@ -412,15 +593,15 @@
             break;
         }
     }
-    ACR_Signal(SIGPIPE, SIG_DFL, 0);
+    ACR_Signal(SIGPIPE, SIG_DFL);
 
     return ep->exitwhy;
 cleanup:
     ep->exitwhy = ACR_PARENT_ERROR;
     ep->exitval = rc;
-    for (i = 0; i < 8; i++)
+    for (i = 0; i < 10; i++)
         s_close(pipes[i]);
-    ACR_Signal(SIGPIPE, SIG_DFL, 0);
+    ACR_Signal(SIGPIPE, SIG_DFL);
     return ep->exitwhy;
 }
 
@@ -449,6 +630,25 @@
     return rc;
 }
 
+ACR_DECLARE(int) ACR_ExecProgram(acr_exec_t *ep, const char *executable,
+                                 const char *const *argv,
+                                 char *const *envp)
+{
+    int    rc;
+    char **args = NULL;
+    const char *sa[2] = { executable, NULL };
+
+    if (!(args = ACR_MergeArgsA((const char *const *)sa, argv))) {
+        ep->exitval = ACR_GET_OS_ERROR();
+        ep->exitwhy = ACR_PARENT_ERROR;
+        return ep->exitwhy;
+    }
+    rc = do_exec(ep, executable, args, envp);
+    x_free(args);
+
+    return rc;
+}
+
 ACR_DECLARE(const char *) ACR_ExecStream(acr_exec_t *e, int which)
 {
     if (e) {

Modified: commons/sandbox/runtime/trunk/src/main/native/os/unix/signals.c
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/os/unix/signals.c?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/os/unix/signals.c (original)
+++ commons/sandbox/runtime/trunk/src/main/native/os/unix/signals.c Thu Jan  7 
06:24:35 2010
@@ -240,8 +240,7 @@
 }
 #endif /* DARWIN */
 
-ACR_DECLARE(acr_sigfunc_t *) ACR_Signal(int signo, acr_sigfunc_t *func,
-                                        int flags)
+ACR_DECLARE(acr_sigfunc_t *) ACR_Signal(int signo, acr_sigfunc_t *func)
 {
     int rc = -1;
     struct sigaction act, oact;
@@ -254,18 +253,20 @@
 
     sigemptyset(&act.sa_mask);
     act.sa_handler = func;
-    act.sa_flags   = flags;
+    act.sa_flags   = 0;
 #ifdef SA_INTERRUPT             /* SunOS */
     act.sa_flags |= SA_INTERRUPT;
 #endif
-    if ((signo == SIGCHLD) && (func == SIG_IGN)) {
+    if (signo != SIGALRM)
+        act.sa_flags |= SA_RESTART;
 #ifdef SA_NOCLDSTOP
+    if (signo == SIGCHLD)
         act.sa_flags |= SA_NOCLDSTOP;
 #endif
 #ifdef SA_NOCLDWAIT
+    if (signo == SIGCHLD)
         act.sa_flags |= SA_NOCLDWAIT;
 #endif
-    }
 #if defined(__NetBSD__) || defined(DARWIN)
     /* ignoring SIGCHLD or leaving the default disposition doesn't avoid 
zombies,
      * and there is no SA_NOCLDWAIT flag, so catch the signal and reap status 
in
@@ -289,7 +290,7 @@
         return oact.sa_handler;
 }
 
-ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_DFL(int signo, int flags)
+ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_DFL(int signo)
 {
     int rc;
     struct sigaction act, oact;
@@ -302,10 +303,62 @@
 
     sigemptyset(&act.sa_mask);
     act.sa_handler = SIG_DFL;
-    act.sa_flags   = flags;
+    act.sa_flags   = 0;
+#ifdef SA_INTERRUPT             /* SunOS */
+    act.sa_flags |= SA_INTERRUPT;
+#endif
+    if (signo != SIGALRM)
+        act.sa_flags |= SA_RESTART;
+#ifdef SA_NOCLDSTOP
+    if (signo == SIGCHLD)
+        act.sa_flags |= SA_NOCLDSTOP;
+#endif
+#ifdef SA_NOCLDWAIT
+    if (signo == SIGCHLD)
+        act.sa_flags |= SA_NOCLDWAIT;
+#endif
+    rc = sigaction(signo, &act, &oact);
+    if (rc < 0)
+        return SIG_ERR;
+    else
+        return oact.sa_handler;
+}
+
+ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_IGN(int signo)
+{
+    int rc;
+    struct sigaction act, oact;
+
+    if (signo < 1 || signo > ACR_NUMSIG) {
+        errno = EINVAL;
+        return SIG_ERR;
+    }
+    memset(&act, 0, sizeof(struct sigaction));
+
+    sigemptyset(&act.sa_mask);
+    act.sa_handler = SIG_IGN;
+    act.sa_flags   = 0;
 #ifdef SA_INTERRUPT             /* SunOS */
     act.sa_flags |= SA_INTERRUPT;
 #endif
+    if (signo != SIGALRM)
+        act.sa_flags |= SA_RESTART;
+#ifdef SA_NOCLDSTOP
+    if (signo == SIGCHLD)
+        act.sa_flags |= SA_NOCLDSTOP;
+#endif
+#ifdef SA_NOCLDWAIT
+    if (signo == SIGCHLD)
+        act.sa_flags |= SA_NOCLDWAIT;
+#endif
+#if defined(__NetBSD__) || defined(DARWIN)
+    /* ignoring SIGCHLD or leaving the default disposition doesn't avoid 
zombies,
+     * and there is no SA_NOCLDWAIT flag, so catch the signal and reap status 
in
+     * the handler to avoid zombies
+     */
+    if (signo == SIGCHLD)
+        act.sa_handler = avoid_zombies;
+#endif
     rc = sigaction(signo, &act, &oact);
     if (rc < 0)
         return SIG_ERR;

Modified: commons/sandbox/runtime/trunk/src/main/native/os/win32/exec.c
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/os/win32/exec.c?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/os/win32/exec.c (original)
+++ commons/sandbox/runtime/trunk/src/main/native/os/win32/exec.c Thu Jan  7 
06:24:35 2010
@@ -19,14 +19,125 @@
 #include "acr_arch.h"
 #include "acr_port.h"
 #include "acr_error.h"
+#include "acr_memory.h"
+#include "acr_signals.h"
 #include "acr_string.h"
+#include "acr_sbuf.h"
 #include "acr_descriptor.h"
 #include "acr_file.h"
-#include "acr_port.h"
+#include "acr_pipe.h"
+#include "acr_time.h"
+#include "acr_args.h"
+#include "acr_exec.h"
+#include "acr_env.h"
 
 /**
  * Windows process execution functions
  *
  */
 
+#define PROC_TIMEOUT_STEP   100
+#define PROC_BUFFER_SIZE    512
+
+#define PIPE_STDINP         0
+#define PIPE_STDINP_RDS     0
+#define PIPE_STDINP_WRS     1
+#define PIPE_STDOUT         2
+#define PIPE_STDOUT_RDS     2
+#define PIPE_STDOUT_WRS     3
+#define PIPE_STDERR         4
+#define PIPE_STDERR_RDS     4
+#define PIPE_STDERR_WRS     5
+#define PIPE_SIGERR         6
+#define PIPE_SIGERR_RDS     6
+#define PIPE_SIGERR_WRS     7
+#define PIPE_SIGPID         8
+#define PIPE_SIGPID_RDS     8
+#define PIPE_SIGPID_WRS     9
+
+ACR_DECLARE(acr_exec_t *) ACR_ExecNew(int flags)
+{
+    int rc = 0;
+    acr_exec_t *ep;
+
+    if (!(ep = s_calloc(acr_exec_t, 1)))
+        return NULL;
+    ep->pid   = -1;
+    ep->flags = flags;
+
+    if (flags & ACR_PROC_HAS_STDOUT) {
+        if (!acr_sbuf_new(&ep->sout, NULL, PROC_BUFFER_SIZE,
+                          ACR_SBUF_AUTOEXTEND)) {
+            rc = ACR_GET_OS_ERROR();
+            goto cleanup;
+        }
+    }
+    if (flags & ACR_PROC_HAS_STDERR) {
+        if (!acr_sbuf_new(&ep->serr, NULL, PROC_BUFFER_SIZE,
+                          ACR_SBUF_AUTOEXTEND)) {
+            rc = ACR_GET_OS_ERROR();
+            goto cleanup;
+        }
+    }
+    return ep;
+
+cleanup:
+    acr_sbuf_delete(&ep->sout);
+    acr_sbuf_delete(&ep->serr);
+    x_free(ep);
+
+    ACR_SET_OS_ERROR(rc);
+    return NULL;
+}
+
+ACR_DECLARE(const char *) ACR_ExecStream(acr_exec_t *e, int which)
+{
+    if (e) {
+        switch (which) {
+            case STDOUT_FILENO:
+                return acr_sbuf_data(&e->sout);
+            break;
+            case STDERR_FILENO:
+                return acr_sbuf_data(&e->serr);
+            break;
+        }
+    }
+    return NULL;
+}
+
+ACR_DECLARE(int) ACR_ExecStdinSet(acr_exec_t *e,
+                                   const void *data, size_t len)
+{
+    if (e && (e->flags & ACR_PROC_HAS_STDIN)) {
+        e->data.iov_base = (void *)data;
+        e->data.iov_len  = len;
+        return 0;
+    }
+    return ACR_EINVAL;
+}
+
+ACR_DECLARE(int) ACR_ExecDirSet(acr_exec_t *e,
+                                const wchar_t *path)
+{
+    if (e) {
+        x_free(e->currdir);
+        if (path) {
+            if (!(e->currdir = x_wcsdup(path)))
+                return ACR_ENOMEM;
+        }
+        return 0;
+    }
+    return ACR_EINVAL;
+}
+
+ACR_DECLARE(void) ACR_ExecFree(acr_exec_t *e)
+{
+    if (e) {
+        x_free(e->currdir);
+        acr_sbuf_delete(&e->sout);
+        acr_sbuf_delete(&e->serr);
+
+        x_free(e);
+    }
+}
 

Modified: commons/sandbox/runtime/trunk/src/main/native/os/win32/signals.c
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/os/win32/signals.c?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/os/win32/signals.c (original)
+++ commons/sandbox/runtime/trunk/src/main/native/os/win32/signals.c Thu Jan  7 
06:24:35 2010
@@ -817,8 +817,7 @@
     }
 }
 
-ACR_DECLARE(acr_sigfunc_t *) ACR_Signal(int signo, acr_sigfunc_t *func,
-                                        int flags)
+ACR_DECLARE(acr_sigfunc_t *) ACR_Signal(int signo, acr_sigfunc_t *func)
 {
     acr_sigfunc_t *orghandler;
 
@@ -839,9 +838,14 @@
     return orghandler;
 }
 
-ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_DFL(int signo, int flags)
+ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_DFL(int signo)
 {
-    return ACR_Signal(signo, SIG_DFL, flags);
+    return ACR_Signal(signo, SIG_DFL);
+}
+
+ACR_DECLARE(acr_sigfunc_t *) ACR_SIG_IGN(int signo)
+{
+    return ACR_Signal(signo, SIG_IGN);
 }
 
 ACR_DECLARE(int) ACR_SignalSetKey(const wchar_t *key)

Modified: commons/sandbox/runtime/trunk/src/main/native/shared/args.c
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/shared/args.c?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/shared/args.c (original)
+++ commons/sandbox/runtime/trunk/src/main/native/shared/args.c Thu Jan  7 
06:24:35 2010
@@ -516,95 +516,6 @@
 
 #endif /* WIN32 */
 
-ACR_DECLARE(char **) ACR_DuplicateArgsA(const char *const *argv)
-{
-    size_t arglen;
-    size_t argnum = 1;
-    size_t length = 0;
-    char  *buffer;
-    char  **dupargv  = NULL;
-    const char *const *ap = argv;
-
-    while (*ap) {
-        ++argnum;
-        length += (strlen(*ap) + 1);
-        ap++;
-    }
-    if (!length) {
-        /* Nothing to duplicate
-         */
-        return NULL;
-    }
-    arglen = ACR_ALIGN_DEFAULT(argnum * sizeof(char *));
-    buffer = x_malloc(arglen + length + 1);
-    if (!buffer)
-        return NULL;
-    argnum = 0;
-    ap = argv;
-    dupargv = (char **)buffer;
-    buffer += arglen;
-    while (*ap) {
-        size_t len = strlen(*ap) + 1;
-        memcpy(buffer, *ap, len);
-        dupargv[argnum++] = buffer;
-        buffer += len;
-        ap++;
-    }
-    *buffer = '\0';
-    dupargv[argnum] = NULL;
-
-    return dupargv;
-}
-
-ACR_DECLARE(wchar_t **) ACR_DuplicateArgsW(const wchar_t *const *argv)
-{
-    size_t    arglen;
-    size_t    argnum = 1;
-    size_t    length = 0;
-    wchar_t  *buffer;
-    wchar_t **dupargv  = NULL;
-    const wchar_t *const *ap = argv;
-
-    while (*ap) {
-        ++argnum;
-        length += (wcslen(*ap) + 1);
-        ap++;
-    }
-    if (!length) {
-        /* Nothing to duplicate
-         */
-        return NULL;
-    }
-    arglen = ACR_ALIGN_DEFAULT(argnum * sizeof(wchar_t *));
-    buffer = x_malloc(arglen + ((length + 3) * sizeof(wchar_t)));
-    if (!buffer)
-        return NULL;
-    argnum = 0;
-    ap = argv;
-    dupargv = (wchar_t **)buffer;
-    buffer += (arglen / sizeof(wchar_t));
-    while (*ap) {
-        size_t len = wcslen(*ap) + 1;
-        if (argnum == 0 && wcspbrk(*ap, L" \t")) {
-            buffer[0] = L'"';
-            memcpy(buffer + 1, *ap, len * sizeof(wchar_t));
-            buffer[len + 1] = L'"';
-        }
-        else
-            memcpy(buffer, *ap, len * sizeof(wchar_t));
-        dupargv[argnum++] = buffer;
-        buffer += len;
-        ap++;
-    }
-    /* Add double zero termination.
-     * dupargv[0] can be used as zero separated
-     * double zero terminated string
-     */
-    *buffer = L'\0';
-    dupargv[argnum] = NULL;
-
-    return dupargv;
-}
 
 ACR_DECLARE(char **) ACR_MergeArgsA(const char *const *argv1,
                                     const char *const *argv2)
@@ -647,19 +558,19 @@
     buffer += arglen;
     if (ap1) {
         while (*ap1) {
-            size_t len = strlen(*ap1) + 1;
-            memcpy(buffer, *ap1, len);
+            arglen = strlen(*ap1) + 1;
+            memcpy(buffer, *ap1, arglen);
             dupargv[argnum++] = buffer;
-            buffer += len;
+            buffer += arglen;
             ap1++;
         }
     }
     if (ap2) {
         while (*ap2) {
-            size_t len = strlen(*ap2) + 1;
-            memcpy(buffer, *ap2, len);
+            arglen = strlen(*ap2) + 1;
+            memcpy(buffer, *ap2, arglen);
             dupargv[argnum++] = buffer;
-            buffer += len;
+            buffer += arglen;
             ap2++;
         }
     }
@@ -710,31 +621,31 @@
     buffer += (arglen / sizeof(wchar_t));
     if (ap1) {
         while (*ap1) {
-            size_t len = wcslen(*ap1) + 1;
+            arglen = wcslen(*ap1) + 1;
             if (argnum == 0 && wcspbrk(*ap1, L" \t")) {
                 buffer[0] = L'"';
-                memcpy(buffer + 1, *ap1, len * sizeof(wchar_t));
-                buffer[len + 1] = L'"';
+                memcpy(buffer + 1, *ap1, arglen * sizeof(wchar_t));
+                buffer[arglen + 1] = L'"';
             }
             else
-                memcpy(buffer, *ap1, len * sizeof(wchar_t));
+                memcpy(buffer, *ap1, arglen * sizeof(wchar_t));
             dupargv[argnum++] = buffer;
-            buffer += len;
+            buffer += arglen;
             ap1++;
         }
     }
     if (ap2) {
         while (*ap2) {
-            size_t len = wcslen(*ap2) + 1;
+            arglen = wcslen(*ap2) + 1;
             if (argnum == 0 && wcspbrk(*ap2, L" \t")) {
                 buffer[0] = L'"';
-                memcpy(buffer + 1, *ap2, len * sizeof(wchar_t));
-                buffer[len + 1] = L'"';
+                memcpy(buffer + 1, *ap2, arglen * sizeof(wchar_t));
+                buffer[arglen + 1] = L'"';
             }
             else
-                memcpy(buffer, *ap2, len * sizeof(wchar_t));
+                memcpy(buffer, *ap2, arglen * sizeof(wchar_t));
             dupargv[argnum++] = buffer;
-            buffer += len;
+            buffer += arglen;
             ap2++;
         }
     }
@@ -748,3 +659,13 @@
     return dupargv;
 }
 
+ACR_DECLARE(char **) ACR_DuplicateArgsA(const char *const *argv)
+{
+    return ACR_MergeArgsA(argv, NULL);
+}
+
+ACR_DECLARE(wchar_t **) ACR_DuplicateArgsW(const wchar_t *const *argv)
+{
+    return ACR_MergeArgsW(argv, NULL);
+}
+

Modified: commons/sandbox/runtime/trunk/src/main/native/shared/sigaction.c
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/shared/sigaction.c?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/shared/sigaction.c (original)
+++ commons/sandbox/runtime/trunk/src/main/native/shared/sigaction.c Thu Jan  7 
06:24:35 2010
@@ -318,7 +318,7 @@
             handler = SIG_ERR;
         break;
     }
-    if (ACR_Signal(signum, handler, 0) == SIG_ERR)
+    if (ACR_Signal(signum, handler) == SIG_ERR)
         return ACR_GET_OS_ERROR();
     else
         return 0;

Modified: commons/sandbox/runtime/trunk/src/main/native/shared/string.c
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/shared/string.c?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/shared/string.c (original)
+++ commons/sandbox/runtime/trunk/src/main/native/shared/string.c Thu Jan  7 
06:24:35 2010
@@ -1883,6 +1883,70 @@
     return arr;
 }
 
+ACR_DECLARE(void) ACR_FreeStringArray(void **arr)
+{
+    if (arr) {
+        void **ap = arr;
+        while (*ap) {
+            x_free(*ap);
+            ap++;
+        }
+        x_free(arr);
+    }
+}
+
+ACR_DECLARE(char **) ACR_GetJavaStringArrayA(JNIEnv *_E,
+                                             jobjectArray arr)
+{
+    char **ret = NULL;
+    jsize  cnt;
+    jsize  i;
+    if (!arr)
+        return NULL;
+
+    cnt = (*_E)->GetArrayLength(_E, arr);
+    ret = (char **)ACR_Malloc(_E, THROW_NMARK, (cnt + 1) * sizeof(char *));
+    if (!ret)
+        return NULL;
+    for (i = 0; i < cnt; i++) {
+        jstring str = (*_E)->GetObjectArrayElement(_E, arr, i);
+        if (str) {
+            ret[i] = ACR_GetJavaStringA(_E, str, NULL);
+            (*_E)->DeleteLocalRef(_E, str);
+        }
+        else
+            break;
+    }
+    ret[i] = NULL;
+    return ret;
+}
+
+ACR_DECLARE(wchar_t **) ACR_GetJavaStringArrayW(JNIEnv *_E,
+                                                jobjectArray arr)
+{
+    wchar_t **ret = NULL;
+    jsize  cnt;
+    jsize  i;
+    if (!arr)
+        return NULL;
+
+    cnt = (*_E)->GetArrayLength(_E, arr);
+    ret = (wchar_t **)ACR_Malloc(_E, THROW_NMARK, (cnt + 1) * sizeof(wchar_t 
*));
+    if (!ret)
+        return NULL;
+    for (i = 0; i < cnt; i++) {
+        jstring str = (*_E)->GetObjectArrayElement(_E, arr, i);
+        if (str) {
+            ret[i] = ACR_GetJavaStringW(_E, str, NULL);
+            (*_E)->DeleteLocalRef(_E, str);
+        }
+        else
+            break;
+    }
+    ret[i] = NULL;
+    return ret;
+}
+
 ACR_DECLARE(char **) ACR_MszStrToArrayA(JNIEnv *_E,
                                         const char *str)
 {

Modified: commons/sandbox/runtime/trunk/src/main/native/test/testsuite.c
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/native/test/testsuite.c?rev=896761&r1=896760&r2=896761&view=diff
==============================================================================
--- commons/sandbox/runtime/trunk/src/main/native/test/testsuite.c (original)
+++ commons/sandbox/runtime/trunk/src/main/native/test/testsuite.c Thu Jan  7 
06:24:35 2010
@@ -575,13 +575,13 @@
         return ACR_EINVAL;
     }
     exe = ACR_ExecNew(ACR_PROC_HAS_STDOUT);
-    exe->timeout = ACR_USEC_PER_SEC * 2;
+    exe->limit.timeout = ACR_USEC_PER_SEC * 2;
     rc = ACR_ExecShellCmd(exe, argv[0], NULL);
 
     fprintf(stdout, "[STDOUT]:\n%s", ACR_ExecStream(exe, 1));
     fprintf(stdout, "[STDERR]:\n%s", ACR_ExecStream(exe, 2));
-    fprintf(stdout, "\n[EXITWHY]: %d\n[EXITVAL]: %d\n",
-            exe->exitwhy, exe->exitval);
+    fprintf(stdout, "\n[EXITWHY]: %d\n[EXITVAL]: %d\n[EXITPID]: %d\n",
+            exe->exitwhy, exe->exitval, exe->pid);
     ACR_ExecFree(exe);
     return 0;
 }
@@ -598,8 +598,46 @@
 
     fprintf(stdout, "[STDOUT]:\n%s", ACR_ExecStream(exe, 1));
     fprintf(stdout, "[STDERR]:\n%s", ACR_ExecStream(exe, 2));
-    fprintf(stdout, "\n[EXITWHY]: %d\n[EXITVAL]: %d\n",
-            exe->exitwhy, exe->exitval);
+    fprintf(stdout, "\n[EXITWHY]: %d\n[EXITVAL]: %d\n[EXITPID]: %d\n",
+            exe->exitwhy, exe->exitval, exe->pid);
+    ACR_ExecFree(exe);
+    return 0;
+}
+
+static int test_exec3(int argc, const char *const argv[])
+{
+    int rc;
+    acr_exec_t *exe;
+    if (argc < 1) {
+        return ACR_EINVAL;
+    }
+    exe = ACR_ExecNew(ACR_PROC_HAS_STDOUT | ACR_PROC_HAS_STDERR);
+    rc = ACR_ExecProgram(exe, argv[0], &argv[1], NULL);
+
+    fprintf(stdout, "[STDOUT]:\n%s", ACR_ExecStream(exe, 1));
+    fprintf(stdout, "[STDERR]:\n%s", ACR_ExecStream(exe, 2));
+    fprintf(stdout, "\n[EXITWHY]: %d\n[EXITVAL]: %d\n[EXITPID]: %d\n",
+            exe->exitwhy, exe->exitval, exe->pid);
+    ACR_ExecFree(exe);
+    return 0;
+}
+
+static int test_exec4(int argc, const char *const argv[])
+{
+    int rc;
+    acr_exec_t *exe;
+    if (argc < 1) {
+        return ACR_EINVAL;
+    }
+    exe = ACR_ExecNew(ACR_PROC_DETACHED);
+    rc = ACR_ExecProgram(exe, argv[0], &argv[1], NULL);
+
+    fprintf(stdout, "[STDOUT]:\n%s", ACR_ExecStream(exe, 1));
+    fputc('\n', stdout);
+    fprintf(stdout, "[STDERR]:\n%s", ACR_ExecStream(exe, 2));
+    fputc('\n', stdout);
+    fprintf(stdout, "\n[EXITWHY]: %d\n[EXITVAL]: %d\n[EXITPID]: %d\n",
+            exe->exitwhy, exe->exitval, exe->pid);
     ACR_ExecFree(exe);
     return 0;
 }
@@ -695,6 +733,12 @@
         else if (!strcasecmp(run_test, "shrun")) {
             rv = test_exec2(argc, argv);
         }
+        else if (!strcasecmp(run_test, "start")) {
+            rv = test_exec3(argc, argv);
+        }
+        else if (!strcasecmp(run_test, "daemon")) {
+            rv = test_exec4(argc, argv);
+        }
     }
 
 cleanup:


Reply via email to