void rktio_process_forget(rktio_t *rktio, rktio_process_t *sp) { #ifdef RKTIO_SYSTEM_UNIX # if defined(CENTRALIZED_SIGCHILD) if (!sp->done) { centralized_done_with_process_id(sp->pid, sp->in_group); centralized_ended_child(); } # else if (!((System_Child *)sp->handle)->done) { void **unused_pid; unused_pid = malloc(sizeof(void *) * 2); unused_pid[0] = (void *)(intptr_t)sp->pid; unused_pid[1] = unused_pids; rktio->need_to_check_children = 1; } free(sp->handle); # endif #endif #ifdef RKTIO_SYSTEM_WINDOWS CloseHandle(sp->handle); #endif free(sp); }
rktio_status_t *rktio_process_status(rktio_t *rktio, rktio_process_t *sp) { int going = 0, status = 0; rktio_status_t *result; #if defined(RKTIO_SYSTEM_UNIX) # if defined(CENTRALIZED_SIGCHILD) if (sp->done) { status = sp->status; } else { if (!centralized_get_child_status(sp->pid, sp->in_group, 1, &status)) { going = 1; } else { sp->done = 1; sp->status = status; centralized_ended_child(); } } # else System_Child *sc = (System_Child *)sp->handle; check_child_done(rktio, sp->pid); if (sc->done) { status = sc->status; } else going = 1; # endif #else # ifdef RKTIO_SYSTEM_WINDOWS DWORD w; if (sp->handle) { if (GetExitCodeProcess((HANDLE)sp->handle, &w)) { collect_process_time(rktio, w, sp); if (w == STILL_ACTIVE) going = 1; else status = w; } else { get_windows_error(); return NULL; } } else status = -1; # endif #endif result = malloc(sizeof(rktio_status_t)); result->running = going; result->result = (going ? 0 : status); return result; }
int rktio_poll_process_done(rktio_t *rktio, rktio_process_t *sp) { #if defined(RKTIO_SYSTEM_UNIX) # if defined(CENTRALIZED_SIGCHILD) { int status; if (!sp->done) { if (centralized_get_child_status(sp->pid, sp->in_group, 1, &status)) { sp->done = 1; sp->status = status; centralized_ended_child(); return 1; } return 0; } else return RKTIO_PROCESS_DONE; } # else { System_Child *sc; sc = (System_Child *)sp->handle; /* Check specific pid, in case the child has its own group (either given by us or given to itself): */ check_child_done(rktio, sp->pid); return sc->done; } # endif #endif #ifdef RKTIO_SYSTEM_WINDOWS { HANDLE sci = (HANDLE)sp->handle; DWORD w; if (sci) { if (GetExitCodeProcess(sci, &w)) { collect_process_time(rktio, w, sp); return (w != STILL_ACTIVE); } else return RKTIO_PROCESS_DONE; } else return RKTIO_PROCESS_DONE; get_windows_error(); return RKTIO_PROCESS_ERROR; } #endif }
static int do_subprocess_kill(rktio_t *rktio, rktio_process_t *sp, int as_kill) { #if defined(RKTIO_SYSTEM_UNIX) # if defined(CENTRALIZED_SIGCHILD) { int status; if (sp->done) return 1; centralized_wait_suspend(); /* Don't allow group checking, because we don't want to wait on a group if we haven't already: */ if (centralized_get_child_status(sp->pid, 0, 0, &status)) { sp->status = status; sp->done = 1; centralized_wait_resume(); centralized_ended_child(); return 1; } } # else { System_Child *sc = (System_Child *)sp->handle; /* Don't pass sp->pid, because we don't want to wait on a group if we haven't already: */ check_child_done(rktio, 0); if (sc->done) return 1; } # define centralized_wait_resume() /* empty */ # endif while (1) { if (sp->is_group) { if (!killpg(sp->pid, as_kill ? SIGKILL : SIGINT)) { centralized_wait_resume(); return 1; } } else { if (!kill(sp->pid, as_kill ? SIGKILL : SIGINT)) { centralized_wait_resume(); return 1; } } if (errno != EINTR) break; /* Otherwise we were interrupted. Try `kill' again. */ } get_posix_error(); centralized_wait_resume(); return 0; #endif #if defined(RKTIO_SYSTEM_WINDOWS) if (as_kill || sp->is_group) { DWORD w; if (!sp->handle) return 1; if (!as_kill) { /* must be for a group; we don't care whether the original process is still running */ if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, sp->pid)) return 1; } else if (GetExitCodeProcess((HANDLE)sp->handle, &w)) { collect_process_time(rktio, w, sp); if (w != STILL_ACTIVE) return 1; if (TerminateProcess((HANDLE)sp->handle, 1)) return 1; } get_windows_error(); return 0; } else return 1; #endif }
rktio_process_result_t *rktio_process(rktio_t *rktio, const char *command, int argc, rktio_const_string_t *argv, rktio_fd_t *stdout_fd, rktio_fd_t *stdin_fd, rktio_fd_t *stderr_fd, rktio_process_t *group_proc, const char *current_directory, rktio_envvars_t *envvars, int flags) { rktio_process_result_t *result; intptr_t to_subprocess[2], from_subprocess[2], err_subprocess[2]; int pid; #if defined(RKTIO_SYSTEM_UNIX) # if !defined(CENTRALIZED_SIGCHILD) System_Child *sc; # endif #else void *sc = 0; #endif void *env; rktio_process_t *subproc; #if defined(RKTIO_SYSTEM_WINDOWS) intptr_t spawn_status; #endif int new_process_group = (flags & RKTIO_PROCESS_NEW_GROUP); int stderr_is_stdout = (flags & RKTIO_PROCESS_STDOUT_AS_STDERR); #if defined(RKTIO_SYSTEM_WINDOWS) int windows_exact_cmdline = (flags & RKTIO_PROCESS_WINDOWS_EXACT_CMDLINE); int windows_chain_termination_to_child = (flags & RKTIO_PROCESS_WINDOWS_CHAIN_TERMINATION); int i; #endif /* avoid compiler warnings: */ to_subprocess[0] = -1; to_subprocess[1] = -1; from_subprocess[0] = -1; from_subprocess[1] = -1; err_subprocess[0] = -1; err_subprocess[1] = -1; /*--------------------------------------*/ /* Create needed pipes */ /*--------------------------------------*/ if (stdout_fd) { from_subprocess[1] = rktio_fd_system_fd(rktio, stdout_fd); RKTIO_COPY_FOR_SUBPROCESS(from_subprocess, 1); } else if (rktio_make_os_pipe(rktio, from_subprocess, RKTIO_NO_INHERIT_INPUT)) { return NULL; } if (stdin_fd) { to_subprocess[0] = rktio_fd_system_fd(rktio, stdin_fd); RKTIO_COPY_FOR_SUBPROCESS(to_subprocess, 0); } else if (rktio_make_os_pipe(rktio, to_subprocess, RKTIO_NO_INHERIT_OUTPUT)) { if (stdout_fd) { RKTIO_CLOSE_SUBPROCESS_COPY(from_subprocess, 1); } return NULL; } if (stderr_fd) { err_subprocess[1] = rktio_fd_system_fd(rktio, stderr_fd); RKTIO_COPY_FOR_SUBPROCESS(err_subprocess, 1); } else if (stderr_is_stdout) { err_subprocess[0] = from_subprocess[0]; err_subprocess[1] = from_subprocess[1]; } else if (rktio_make_os_pipe(rktio, err_subprocess, RKTIO_NO_INHERIT_INPUT)) { if (stdout_fd) { RKTIO_CLOSE_SUBPROCESS_COPY(from_subprocess, 1); } if (stdin_fd) { RKTIO_CLOSE_SUBPROCESS_COPY(to_subprocess, 0); } return NULL; } if (envvars) env = rktio_envvars_to_block(rktio, envvars); else env = NULL; #if defined(RKTIO_SYSTEM_WINDOWS) /*--------------------------------------*/ /* Execute: Windows */ /*--------------------------------------*/ /* Windows: quasi-stdin is locked, and we'll say it doesn't matter */ fflush(stdin); fflush(stdout); fflush(stderr); { char **new_argv; if (!windows_exact_cmdline) { /* protect spaces, etc. in the arguments: */ new_argv = malloc(sizeof(char *) * argc); for (i = 0; i < argc; i++) { new_argv[i] = cmdline_protect(argv[i]); } argv = new_argv; } pid = 0; spawn_status = do_spawnv(rktio, command, argc, (const char * const *)argv, windows_exact_cmdline, to_subprocess[0], from_subprocess[1], err_subprocess[1], &pid, new_process_group, windows_chain_termination_to_child, env, current_directory); if (!windows_exact_cmdline) { for (i = 0; i < argc; i++) { free(argv[i]); } free(argv); } if (spawn_status != -1) sc = (void *)spawn_status; } #else /*--------------------------------------*/ /* Execute: Unix */ /*--------------------------------------*/ { #if defined(CENTRALIZED_SIGCHILD) centralized_starting_child(); #else sc = malloc(sizeof(System_Child)); sc->id = 0; sc->done = 0; block_child_signals(rktio, 1); /* Relies on signals blocked: */ init_sigchld(rktio); #endif #if defined(__QNX__) pid = vfork(); #elif defined(SUBPROCESS_USE_FORK1) pid = fork1(); #else pid = fork(); #endif if (pid > 0) { /* This is the original process, which needs to manage the newly created child process. */ if (new_process_group || group_proc) { /* there's a race condition between this use and the exec(), and there's a race condition between the other setpgid() in the child processand sending signals from the parent process; so, we set in both, and at least one will succeed; we could perform better error checking, since EACCES is the only expected error */ int pgid = pid; if (group_proc) pgid = group_proc->pid; setpgid(pid, pgid); /* note: silent failure */ } #if defined(CENTRALIZED_SIGCHILD) { rktio_signal_handle_t *signal_fd; int status; signal_fd = rktio_get_signal_handle(rktio); centralized_register_child(pid, new_process_group || group_proc, signal_fd, &status); /* printf("SUBPROCESS %i\n", pid); */ } #else sc->next = rktio->system_children; rktio->system_children = sc; sc->id = pid; #endif } else if (!pid) { /* This is the new child process */ if (new_process_group || group_proc) { /* see also setpgid above */ int actual_pid = getpid(); int pgid = actual_pid; if (group_proc) pgid = group_proc->pid; setpgid(actual_pid, pgid); /* note: silent failure */ } } else { get_posix_error(); } #if !defined(CENTRALIZED_SIGCHILD) block_child_signals(rktio, 0); #else if (!pid) centralized_unblock_child_signal(); else if (pid == -1) centralized_ended_child(); #endif } switch (pid) { case -1: /* Close all created descriptors */ if (!stdin_fd) { rktio_reliably_close(to_subprocess[0]); rktio_reliably_close(to_subprocess[1]); } else { RKTIO_CLOSE_SUBPROCESS_COPY(to_subprocess, 0); } if (!stdout_fd) { rktio_reliably_close(from_subprocess[0]); rktio_reliably_close(from_subprocess[1]); } else { RKTIO_CLOSE_SUBPROCESS_COPY(from_subprocess, 1); } if (!stderr_fd) { if (!stderr_is_stdout) { rktio_reliably_close(err_subprocess[0]); rktio_reliably_close(err_subprocess[1]); } } else { RKTIO_CLOSE_SUBPROCESS_COPY(err_subprocess, 1); } return NULL; case 0: /* child */ { int errid; /* Copy pipe descriptors to stdin and stdout */ do { errid = MSC_IZE(dup2)(to_subprocess[0], 0); } while (errid == -1 && errno == EINTR); do { errid = MSC_IZE(dup2)(from_subprocess[1], 1); } while (errid == -1 && errno == EINTR); do { errid = MSC_IZE(dup2)(err_subprocess[1], 2); } while (errid == -1 && errno == EINTR); /* Close unwanted descriptors */ if (!stdin_fd) { rktio_reliably_close(to_subprocess[0]); rktio_reliably_close(to_subprocess[1]); } if (!stdout_fd) { rktio_reliably_close(from_subprocess[0]); rktio_reliably_close(from_subprocess[1]); } if (!stderr_fd) { if (!stderr_is_stdout) { rktio_reliably_close(err_subprocess[0]); rktio_reliably_close(err_subprocess[1]); } } rktio_close_fds_after_fork(0, 1, 2); } /* Set real CWD: */ if (!rktio_set_current_directory(rktio, current_directory)) { fprintf(stderr, "racket: chdir failed to: %s\n", current_directory); _exit(1); } /* Exec new process */ { int err, i; rktio_const_string_t *new_argv; /* add a NULL terminator */ new_argv = malloc(sizeof(char *) * (argc + 1)); for (i = 0; i < argc; i++) { new_argv[i] = argv[i]; } new_argv[i] = NULL; if (!env) env = rktio_get_environ_array(); err = MSC_IZE(execve)(command, (char **)new_argv, (char **)env); if (err) err = errno; if (envvars) free(env); /* If we get here it failed; give up */ fprintf(stderr, "exec failed (%s%serrno=%d)\n", strerror(err), "; ", err); _exit(1); } default: /* parent */ break; } #endif /*--------------------------------------*/ /* Close unneeded descriptors */ /*--------------------------------------*/ free(env); if (!stdin_fd) RKTIO_CLOSE(to_subprocess[0]); else RKTIO_CLOSE_SUBPROCESS_COPY(to_subprocess, 0); if (!stdout_fd) RKTIO_CLOSE(from_subprocess[1]); else RKTIO_CLOSE_SUBPROCESS_COPY(from_subprocess, 1); if (!stderr_fd) { if (!stderr_is_stdout) RKTIO_CLOSE(err_subprocess[1]); } else RKTIO_CLOSE_SUBPROCESS_COPY(err_subprocess, 1); /*--------------------------------------*/ /* Create new file-descriptor objects */ /*--------------------------------------*/ result = malloc(sizeof(rktio_process_result_t)); if (!stdout_fd) result->stdout_fd = rktio_system_fd(rktio, from_subprocess[0], RKTIO_OPEN_READ); else result->stdout_fd = NULL; if (!stdin_fd) result->stdin_fd = rktio_system_fd(rktio, to_subprocess[1], RKTIO_OPEN_WRITE); else result->stdin_fd = NULL; if (!stderr_fd && !stderr_is_stdout) result->stderr_fd = rktio_system_fd(rktio, err_subprocess[0], RKTIO_OPEN_READ); else result->stderr_fd = NULL; /*--------------------------------------*/ /* Return result info */ /*--------------------------------------*/ subproc = malloc(sizeof(rktio_process_t)); memset(subproc, 0, sizeof(rktio_process_t)); #if !defined(CENTRALIZED_SIGCHILD) subproc->handle = (void *)sc; #endif subproc->pid = pid; subproc->is_group = new_process_group; subproc->in_group = (new_process_group || group_proc); result->process = subproc; return result; }