int uv_spawn(uv_loop_t* loop, uv_process_t* process, const uv_process_options_t* options) { int signal_pipe[2] = { -1, -1 }; int (*pipes)[2]; int stdio_count; QUEUE* q; ssize_t r; pid_t pid; int err; int exec_errorno; int i; assert(options->file != NULL); assert(!(options->flags & ~(UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); QUEUE_INIT(&process->queue); stdio_count = options->stdio_count; if (stdio_count < 3) stdio_count = 3; err = -ENOMEM; pipes = malloc(stdio_count * sizeof(*pipes)); if (pipes == NULL) goto error; for (i = 0; i < stdio_count; i++) { pipes[i][0] = -1; pipes[i][1] = -1; } for (i = 0; i < options->stdio_count; i++) { err = uv__process_init_stdio(options->stdio + i, pipes[i]); if (err) goto error; } /* This pipe is used by the parent to wait until * the child has called `execve()`. We need this * to avoid the following race condition: * * if ((pid = fork()) > 0) { * kill(pid, SIGTERM); * } * else if (pid == 0) { * execve("/bin/cat", argp, envp); * } * * The parent sends a signal immediately after forking. * Since the child may not have called `execve()` yet, * there is no telling what process receives the signal, * our fork or /bin/cat. * * To avoid ambiguity, we create a pipe with both ends * marked close-on-exec. Then, after the call to `fork()`, * the parent polls the read end until it EOFs or errors with EPIPE. */ err = uv__make_pipe(signal_pipe, 0); if (err) goto error; uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD); /* Acquire write lock to prevent opening new fds in worker threads */ uv_rwlock_wrlock(&loop->cloexec_lock); pid = fork(); if (pid == -1) { err = -errno; uv_rwlock_wrunlock(&loop->cloexec_lock); uv__close(signal_pipe[0]); uv__close(signal_pipe[1]); goto error; } if (pid == 0) { uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]); abort(); } /* Release lock in parent process */ uv_rwlock_wrunlock(&loop->cloexec_lock); uv__close(signal_pipe[1]); process->status = 0; exec_errorno = 0; do r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno)); while (r == -1 && errno == EINTR); if (r == 0) ; /* okay, EOF */ else if (r == sizeof(exec_errorno)) ; /* okay, read errorno */ else if (r == -1 && errno == EPIPE) ; /* okay, got EPIPE */ else abort(); uv__close(signal_pipe[0]); for (i = 0; i < options->stdio_count; i++) { err = uv__process_open_stream(options->stdio + i, pipes[i], i == 0); if (err == 0) continue; while (i--) uv__process_close_stream(options->stdio + i); goto error; } /* Only activate this handle if exec() happened successfully */ if (exec_errorno == 0) { q = uv__process_queue(loop, pid); QUEUE_INSERT_TAIL(q, &process->queue); uv__handle_start(process); } process->pid = pid; process->exit_cb = options->exit_cb; free(pipes); return exec_errorno; error: if (pipes != NULL) { for (i = 0; i < stdio_count; i++) { if (i < options->stdio_count) if (options->stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM)) continue; if (pipes[i][0] != -1) close(pipes[i][0]); if (pipes[i][1] != -1) close(pipes[i][1]); } free(pipes); } return err; }
int uv_spawn(uv_loop_t* loop, uv_process_t* process, uv_process_options_t options) { /* * Save environ in the case that we get it clobbered * by the child process. */ char** save_our_env = environ; int* pipes = malloc(2 * options.stdio_count * sizeof(int)); #if SPAWN_WAIT_EXEC int signal_pipe[2] = { -1, -1 }; struct pollfd pfd; #endif int status; pid_t pid; int i; if (pipes == NULL) { errno = ENOMEM; goto error; } assert(options.file != NULL); assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); loop->counters.process_init++; uv__handle_start(process); process->exit_cb = options.exit_cb; /* Init pipe pairs */ for (i = 0; i < options.stdio_count; i++) { pipes[i * 2] = -1; pipes[i * 2 + 1] = -1; } /* Create socketpairs/pipes, or use raw fd */ for (i = 0; i < options.stdio_count; i++) { if (uv__process_init_stdio(&options.stdio[i], pipes + i * 2, i != 0)) { goto error; } } /* This pipe is used by the parent to wait until * the child has called `execve()`. We need this * to avoid the following race condition: * * if ((pid = fork()) > 0) { * kill(pid, SIGTERM); * } * else if (pid == 0) { * execve("/bin/cat", argp, envp); * } * * The parent sends a signal immediately after forking. * Since the child may not have called `execve()` yet, * there is no telling what process receives the signal, * our fork or /bin/cat. * * To avoid ambiguity, we create a pipe with both ends * marked close-on-exec. Then, after the call to `fork()`, * the parent polls the read end until it sees POLLHUP. */ #if SPAWN_WAIT_EXEC if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK)) goto error; #endif pid = fork(); if (pid == -1) { #if SPAWN_WAIT_EXEC close(signal_pipe[0]); close(signal_pipe[1]); #endif environ = save_our_env; goto error; } if (pid == 0) { /* Child */ if (options.flags & UV_PROCESS_DETACHED) { setsid(); } /* Dup fds */ for (i = 0; i < options.stdio_count; i++) { /* * stdin has swapped ends of pipe * (it's the only one readable stream) */ int close_fd = i == 0 ? pipes[i * 2 + 1] : pipes[i * 2]; int use_fd = i == 0 ? pipes[i * 2] : pipes[i * 2 + 1]; if (use_fd >= 0) { close(close_fd); dup2(use_fd, i); } else { /* Reset flags that might be set by Node */ uv__cloexec(i, 0); uv__nonblock(i, 0); } } if (options.cwd && chdir(options.cwd)) { perror("chdir()"); _exit(127); } if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) { perror("setgid()"); _exit(127); } if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) { perror("setuid()"); _exit(127); } environ = options.env; execvp(options.file, options.args); perror("execvp()"); _exit(127); /* Execution never reaches here. */ } /* Parent. */ /* Restore environment. */ environ = save_our_env; #if SPAWN_WAIT_EXEC /* POLLHUP signals child has exited or execve()'d. */ close(signal_pipe[1]); do { pfd.fd = signal_pipe[0]; pfd.events = POLLIN|POLLHUP; pfd.revents = 0; errno = 0, status = poll(&pfd, 1, -1); } while (status == -1 && (errno == EINTR || errno == ENOMEM)); assert((status == 1) && "poll() on pipe read end failed"); close(signal_pipe[0]); #endif process->pid = pid; ev_child_init(&process->child_watcher, uv__chld, pid, 0); ev_child_start(process->loop->ev, &process->child_watcher); process->child_watcher.data = process; for (i = 0; i < options.stdio_count; i++) { if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) { int j; /* Close all opened streams */ for (j = 0; j < i; j++) { uv__process_close_stream(&options.stdio[j]); } goto error; } } free(pipes); return 0; error: uv__set_sys_error(process->loop, errno); for (i = 0; i < options.stdio_count; i++) { close(pipes[i * 2]); close(pipes[i * 2 + 1]); } free(pipes); return -1; }