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: