ext/asyncengine/libuv/src/unix/process.c in asyncengine-0.0.1.testing1 vs ext/asyncengine/libuv/src/unix/process.c in asyncengine-0.0.2.alpha1
- old
+ new
@@ -23,14 +23,14 @@
#include "internal.h"
#include <assert.h>
#include <errno.h>
#include <sys/wait.h>
-#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK */
#include <poll.h>
#include <unistd.h>
#include <stdio.h>
+#include <fcntl.h>
#ifdef __APPLE__
# include <TargetConditionals.h>
#endif
@@ -66,29 +66,19 @@
}
}
int uv__make_socketpair(int fds[2], int flags) {
-#ifdef SOCK_NONBLOCK
- int fl;
-
- fl = SOCK_CLOEXEC;
-
- if (flags & UV__F_NONBLOCK)
- fl |= SOCK_NONBLOCK;
-
- if (socketpair(AF_UNIX, SOCK_STREAM|fl, 0, fds) == 0)
+#if __linux__
+ if (socketpair(AF_UNIX, SOCK_STREAM | UV__SOCK_CLOEXEC | flags, 0, fds) == 0)
return 0;
+ /* Retry on EINVAL, it means SOCK_CLOEXEC is not supported.
+ * Anything else is a genuine error.
+ */
if (errno != EINVAL)
return -1;
-
- /* errno == EINVAL so maybe the kernel headers lied about
- * the availability of SOCK_NONBLOCK. This can happen if people
- * build libuv against newer kernel headers than the kernel
- * they actually run the software on.
- */
#endif
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
return -1;
@@ -104,18 +94,11 @@
}
int uv__make_pipe(int fds[2], int flags) {
#if __linux__
- int fl;
-
- fl = O_CLOEXEC;
-
- if (flags & UV__F_NONBLOCK)
- fl |= O_NONBLOCK;
-
- if (uv__pipe2(fds, fl) == 0)
+ if (uv__pipe2(fds, flags | UV__O_CLOEXEC) == 0)
return 0;
if (errno != ENOSYS)
return -1;
#endif
@@ -137,23 +120,153 @@
/*
* Used for initializing stdio streams like options.stdin_stream. Returns
* zero on success.
*/
-static int uv__process_init_pipe(uv_pipe_t* handle, int fds[2], int flags) {
- if (handle->type != UV_NAMED_PIPE) {
- errno = EINVAL;
- return -1;
+static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2],
+ int writable) {
+ int fd = -1;
+ switch (container->flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
+ UV_INHERIT_STREAM)) {
+ case UV_IGNORE:
+ return 0;
+ case UV_CREATE_PIPE:
+ assert(container->data.stream != NULL);
+
+ if (container->data.stream->type != UV_NAMED_PIPE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return uv__make_socketpair(fds, 0);
+ case UV_INHERIT_FD:
+ case UV_INHERIT_STREAM:
+ if (container->flags & UV_INHERIT_FD) {
+ fd = container->data.fd;
+ } else {
+ fd = container->data.stream->fd;
+ }
+
+ if (fd == -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fds[writable ? 1 : 0] = fd;
+
+ return 0;
+ default:
+ assert(0 && "Unexpected flags");
+ return -1;
}
+}
- if (handle->ipc)
- return uv__make_socketpair(fds, flags);
- else
- return uv__make_pipe(fds, flags);
+
+static int uv__process_stdio_flags(uv_stdio_container_t* container,
+ int writable) {
+ if (container->data.stream->type == UV_NAMED_PIPE &&
+ ((uv_pipe_t*)container->data.stream)->ipc) {
+ return UV_STREAM_READABLE | UV_STREAM_WRITABLE;
+ } else if (writable) {
+ return UV_STREAM_WRITABLE;
+ } else {
+ return UV_STREAM_READABLE;
+ }
}
+static int uv__process_open_stream(uv_stdio_container_t* container, int fds[2],
+ int writable) {
+ int fd = fds[writable ? 1 : 0];
+ int child_fd = fds[writable ? 0 : 1];
+ int flags;
+
+ /* No need to create stream */
+ if (!(container->flags & UV_CREATE_PIPE) || fd < 0) {
+ return 0;
+ }
+
+ assert(child_fd >= 0);
+ close(child_fd);
+
+ uv__nonblock(fd, 1);
+ flags = uv__process_stdio_flags(container, writable);
+
+ return uv__stream_open((uv_stream_t*)container->data.stream, fd, flags);
+}
+
+
+static void uv__process_close_stream(uv_stdio_container_t* container) {
+ if (!(container->flags & UV_CREATE_PIPE)) return;
+
+ uv__stream_close((uv_stream_t*)container->data.stream);
+}
+
+
+static void uv__process_child_init(uv_process_options_t options,
+ int stdio_count,
+ int* pipes) {
+ int i;
+
+ if (options.flags & UV_PROCESS_DETACHED) {
+ setsid();
+ }
+
+ /* Dup fds */
+ for (i = 0; i < 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);
+ } else if (i < 3) {
+ /* `/dev/null` stdin, stdout, stderr even if they've flag UV_IGNORE */
+ use_fd = open("/dev/null", i == 0 ? O_RDONLY : O_RDWR);
+
+ if (use_fd < 0) {
+ perror("failed to open stdio");
+ _exit(127);
+ }
+ } else {
+ continue;
+ }
+
+ if (i != use_fd) {
+ dup2(use_fd, i);
+ close(use_fd);
+ } else {
+ uv__cloexec(use_fd, 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);
+}
+
+
#ifndef SPAWN_WAIT_EXEC
# define SPAWN_WAIT_EXEC 1
#endif
int uv_spawn(uv_loop_t* loop, uv_process_t* process,
@@ -161,41 +274,53 @@
/*
* Save environ in the case that we get it clobbered
* by the child process.
*/
char** save_our_env = environ;
- int stdin_pipe[2] = { -1, -1 };
- int stdout_pipe[2] = { -1, -1 };
- int stderr_pipe[2] = { -1, -1 };
+
+ int stdio_count = options.stdio_count < 3 ? 3 : options.stdio_count;
+ int* pipes = malloc(2 * stdio_count * sizeof(int));
+
#if SPAWN_WAIT_EXEC
int signal_pipe[2] = { -1, -1 };
struct pollfd pfd;
#endif
int status;
pid_t pid;
- int flags;
+ 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;
- if (options.stdin_stream &&
- uv__process_init_pipe(options.stdin_stream, stdin_pipe, 0)) {
- goto error;
+ /* Init pipe pairs */
+ for (i = 0; i < stdio_count; i++) {
+ pipes[i * 2] = -1;
+ pipes[i * 2 + 1] = -1;
}
- if (options.stdout_stream &&
- uv__process_init_pipe(options.stdout_stream, stdout_pipe, 0)) {
- goto error;
+ /* 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;
+ }
}
- if (options.stderr_stream &&
- uv__process_init_pipe(options.stderr_stream, stderr_pipe, 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) {
@@ -229,47 +354,13 @@
environ = save_our_env;
goto error;
}
if (pid == 0) {
- if (stdin_pipe[0] >= 0) {
- close(stdin_pipe[1]);
- dup2(stdin_pipe[0], STDIN_FILENO);
- } else {
- /* Reset flags that might be set by Node */
- uv__cloexec(STDIN_FILENO, 0);
- uv__nonblock(STDIN_FILENO, 0);
- }
+ /* Child */
+ uv__process_child_init(options, stdio_count, pipes);
- if (stdout_pipe[1] >= 0) {
- close(stdout_pipe[0]);
- dup2(stdout_pipe[1], STDOUT_FILENO);
- } else {
- /* Reset flags that might be set by Node */
- uv__cloexec(STDOUT_FILENO, 0);
- uv__nonblock(STDOUT_FILENO, 0);
- }
-
- if (stderr_pipe[1] >= 0) {
- close(stderr_pipe[0]);
- dup2(stderr_pipe[1], STDERR_FILENO);
- } else {
- /* Reset flags that might be set by Node */
- uv__cloexec(STDERR_FILENO, 0);
- uv__nonblock(STDERR_FILENO, 0);
- }
-
- if (options.cwd && chdir(options.cwd)) {
- perror("chdir()");
- _exit(127);
- }
-
- environ = options.env;
-
- execvp(options.file, options.args);
- perror("execvp()");
- _exit(127);
/* Execution never reaches here. */
}
/* Parent. */
@@ -295,50 +386,36 @@
ev_child_init(&process->child_watcher, uv__chld, pid, 0);
ev_child_start(process->loop->ev, &process->child_watcher);
process->child_watcher.data = process;
- if (stdin_pipe[1] >= 0) {
- assert(options.stdin_stream);
- assert(stdin_pipe[0] >= 0);
- close(stdin_pipe[0]);
- uv__nonblock(stdin_pipe[1], 1);
- flags = UV_WRITABLE | (options.stdin_stream->ipc ? UV_READABLE : 0);
- uv__stream_open((uv_stream_t*)options.stdin_stream, stdin_pipe[1],
- flags);
- }
+ 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]);
+ }
- if (stdout_pipe[0] >= 0) {
- assert(options.stdout_stream);
- assert(stdout_pipe[1] >= 0);
- close(stdout_pipe[1]);
- uv__nonblock(stdout_pipe[0], 1);
- flags = UV_READABLE | (options.stdout_stream->ipc ? UV_WRITABLE : 0);
- uv__stream_open((uv_stream_t*)options.stdout_stream, stdout_pipe[0],
- flags);
+ goto error;
+ }
}
- if (stderr_pipe[0] >= 0) {
- assert(options.stderr_stream);
- assert(stderr_pipe[1] >= 0);
- close(stderr_pipe[1]);
- uv__nonblock(stderr_pipe[0], 1);
- flags = UV_READABLE | (options.stderr_stream->ipc ? UV_WRITABLE : 0);
- uv__stream_open((uv_stream_t*)options.stderr_stream, stderr_pipe[0],
- flags);
- }
+ free(pipes);
return 0;
error:
uv__set_sys_error(process->loop, errno);
- close(stdin_pipe[0]);
- close(stdin_pipe[1]);
- close(stdout_pipe[0]);
- close(stdout_pipe[1]);
- close(stderr_pipe[0]);
- close(stderr_pipe[1]);
+
+ for (i = 0; i < stdio_count; i++) {
+ close(pipes[i * 2]);
+ close(pipes[i * 2 + 1]);
+ }
+
+ free(pipes);
+
return -1;
}
int uv_process_kill(uv_process_t* process, int signum) {
@@ -364,6 +441,7 @@
}
void uv__process_close(uv_process_t* handle) {
ev_child_stop(handle->loop->ev, &handle->child_watcher);
+ uv__handle_stop(handle);
}