diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index f784546795..f9ffbaf4cb 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -334,17 +334,12 @@ UV_EXTERN uv_err_t uv_last_error(uv_loop_t*); UV_EXTERN const char* uv_strerror(uv_err_t err); UV_EXTERN const char* uv_err_name(uv_err_t err); -#ifndef UV_LEAN_AND_MEAN -# define UV_REQ_EXTRA_FIELDS ngx_queue_t active_queue; -#else -# define UV_REQ_EXTRA_FIELDS -#endif #define UV_REQ_FIELDS \ /* public */ \ void* data; \ - UV_REQ_EXTRA_FIELDS \ /* private */ \ + ngx_queue_t active_queue; \ UV_REQ_PRIVATE_FIELDS \ /* read-only */ \ uv_req_type type; \ @@ -378,12 +373,6 @@ struct uv_shutdown_s { }; -#ifndef UV_LEAN_AND_MEAN -# define UV_HANDLE_EXTRA_FIELDS ngx_queue_t active_queue; -#else -# define UV_HANDLE_EXTRA_FIELDS -#endif - #define UV_HANDLE_FIELDS \ /* read-only */ \ uv_loop_t* loop; \ @@ -394,7 +383,6 @@ struct uv_shutdown_s { uv_handle_type type; \ /* private */ \ UV_HANDLE_PRIVATE_FIELDS \ - UV_HANDLE_EXTRA_FIELDS \ /* The abstract base class of all handles. */ struct uv_handle_s { @@ -1168,6 +1156,27 @@ UV_EXTERN int uv_getaddrinfo(uv_loop_t*, uv_getaddrinfo_t* handle, UV_EXTERN void uv_freeaddrinfo(struct addrinfo* ai); /* uv_spawn() options */ +typedef enum { + UV_IGNORE = 0x00, + UV_CREATE_PIPE = 0x01, + /* + * UV_READABLE_PIPE and UV_WRITABLE_PIPE flags are set from + * the child process perspective. + */ + UV_READABLE_PIPE = 0x02, + UV_WRITABLE_PIPE = 0x04, + UV_RAW_FD = 0x08 +} uv_stdio_flags; + +typedef struct uv_stdio_container_s { + uv_stdio_flags flags; + + union { + uv_stream_t* stream; + int fd; + } data; +} uv_stdio_container_t; + typedef struct uv_process_options_s { uv_exit_cb exit_cb; /* Called after the process exits. */ const char* file; /* Path to program to execute. */ @@ -1200,14 +1209,12 @@ typedef struct uv_process_options_s { */ uv_uid_t uid; uv_gid_t gid; + /* - * The user should supply pointers to initialized uv_pipe_t structs for - * stdio. This is used to to send or receive input from the subprocess. - * The user is responsible for calling uv_close on them. + * A container of stdio streams (stdin/stdout/stderr) */ - uv_pipe_t* stdin_stream; - uv_pipe_t* stdout_stream; - uv_pipe_t* stderr_stream; + uv_stdio_container_t* stdio; + int stdio_count; } uv_process_options_t; /* @@ -1659,15 +1666,11 @@ struct uv_loop_s { uv_counters_t counters; /* The last error */ uv_err_t last_err; + /* Loop reference counting */ + unsigned int active_handles; + ngx_queue_t active_reqs; /* User data - use this for whatever. */ void* data; -#ifndef UV_LEAN_AND_MEAN - ngx_queue_t active_reqs; - ngx_queue_t active_handles; -#else - unsigned int active_reqs; - unsigned int active_handles; -#endif }; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 24d10a10a5..b3f232176a 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -204,8 +204,7 @@ static void uv__poll(uv_loop_t* loop, int block) { static int uv__should_block(uv_loop_t* loop) { - return ngx_queue_empty(&loop->idle_handles) - && !ngx_queue_empty(&loop->active_handles); + return loop->active_handles && ngx_queue_empty(&loop->idle_handles); } diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 63a2a8bf96..eb5be60527 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -115,11 +115,7 @@ inline static void uv__req_init(uv_loop_t* loop, uv_req_type type) { loop->counters.req_init++; req->type = type; -#ifndef UV_LEAN_AND_MEAN - ngx_queue_insert_tail(&loop->active_reqs, &req->active_queue); -#else - loop->active_reqs++; -#endif + uv__req_register(loop, req); } #define uv__req_init(loop, req, type) \ uv__req_init((loop), (uv_req_t*)(req), (type)) diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c index 14743daaf8..70057656bc 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c @@ -36,12 +36,8 @@ int uv__loop_init(uv_loop_t* loop, int default_loop) { memset(loop, 0, sizeof(*loop)); -#ifndef UV_LEAN_AND_MEAN - ngx_queue_init(&loop->active_handles); - ngx_queue_init(&loop->active_reqs); -#endif - RB_INIT(&loop->ares_handles); + ngx_queue_init(&loop->active_reqs); ngx_queue_init(&loop->idle_handles); ngx_queue_init(&loop->check_handles); ngx_queue_init(&loop->prepare_handles); diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index bccaf17147..2efe896ace 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -138,19 +138,79 @@ int uv__make_pipe(int fds[2], int flags) { * 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; +static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2], + int writable) { + if (container->flags == UV_IGNORE) { + return 0; + } else if (container->flags & 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); + } else if (container->flags & UV_RAW_FD) { + if (container->data.fd == -1) { + errno = EINVAL; + return -1; + } + + if (writable) { + fds[1] = container->data.fd; + } else { + fds[0] = container->data.fd; + } + + return 0; + } else { + 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); +} + #ifndef SPAWN_WAIT_EXEC # define SPAWN_WAIT_EXEC 1 #endif @@ -162,16 +222,21 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, * 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* 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 flags; + int i; + + if (pipes == NULL) { + errno = ENOMEM; + goto error; + } assert(options.file != NULL); assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | @@ -185,19 +250,17 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, process->exit_cb = options.exit_cb; - if (options.stdin_stream && - uv__process_init_pipe(options.stdin_stream, stdin_pipe, 0)) { - goto error; - } - - if (options.stdout_stream && - uv__process_init_pipe(options.stdout_stream, stdout_pipe, 0)) { - goto error; + /* Init pipe pairs */ + for (i = 0; i < options.stdio_count; i++) { + pipes[i * 2] = -1; + pipes[i * 2 + 1] = -1; } - if (options.stderr_stream && - uv__process_init_pipe(options.stderr_stream, stderr_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; + } } /* This pipe is used by the parent to wait until @@ -237,31 +300,25 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, } 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); - } - - 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); + /* Child */ + + /* 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)) { @@ -313,49 +370,32 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, 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_STREAM_WRITABLE | - (options.stdin_stream->ipc ? UV_STREAM_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_STREAM_READABLE | - (options.stdout_stream->ipc ? UV_STREAM_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_STREAM_READABLE | - (options.stderr_stream->ipc ? UV_STREAM_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 < options.stdio_count; i++) { + close(pipes[i * 2]); + close(pipes[i * 2 + 1]); + } + + free(pipes); + return -1; } diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index f341297bba..3cf214c04c 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -180,10 +180,10 @@ int uv_fs_event_init(uv_loop_t* loop, /* We don't support any flags yet. */ assert(!flags); if (loop->fs_fd == -1) { - if ((portfd = port_create()) == -1) { - uv__set_sys_error(loop, errno); - return -1; - } + if ((portfd = port_create()) == -1) { + uv__set_sys_error(loop, errno); + return -1; + } loop->fs_fd = portfd; first_run = 1; } diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index cede20ba6d..6e484ef547 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -82,25 +82,11 @@ int uv__tcp_connect6(uv_connect_t* req, struct sockaddr_in6 address, uv_connect_cb cb); -#ifndef UV_LEAN_AND_MEAN - -UNUSED static int uv__has_active_handles(const uv_loop_t* loop) { - return !ngx_queue_empty(&loop->active_handles); -} UNUSED static int uv__has_active_reqs(const uv_loop_t* loop) { return !ngx_queue_empty(&loop->active_reqs); } -UNUSED static void uv__active_handle_add(uv_handle_t* h) { - ngx_queue_insert_tail(&h->loop->active_handles, &h->active_queue); -} - -UNUSED static void uv__active_handle_rm(uv_handle_t* h) { - assert(uv__has_active_handles(h->loop)); - ngx_queue_remove(&h->active_queue); -} - UNUSED static void uv__req_register(uv_loop_t* loop, uv_req_t* req) { ngx_queue_insert_tail(&loop->active_reqs, &req->active_queue); } @@ -110,37 +96,19 @@ UNUSED static void uv__req_unregister(uv_loop_t* loop, uv_req_t* req) { ngx_queue_remove(&req->active_queue); } -#else /* UV_LEAN_AND_MEAN */ UNUSED static int uv__has_active_handles(const uv_loop_t* loop) { return loop->active_handles > 0; } -UNUSED static int uv__has_active_reqs(const uv_loop_t* loop) { - return loop->active_reqs > 0; -} - UNUSED static void uv__active_handle_add(uv_handle_t* h) { h->loop->active_handles++; } UNUSED static void uv__active_handle_rm(uv_handle_t* h) { - assert(h->loop->active_handles > 0); h->loop->active_handles--; } -UNUSED static void uv__req_register(uv_loop_t* loop, uv_req_t* req) { - loop->active_reqs++; - (void) req; -} - -UNUSED static void uv__req_unregister(uv_loop_t* loop, uv_req_t* req) { - assert(loop->active_reqs > 0); - loop->active_reqs--; - (void) req; -} - -#endif /* UV_LEAN_AND_MEAN */ #define uv__active_handle_add(h) uv__active_handle_add((uv_handle_t*)(h)) #define uv__active_handle_rm(h) uv__active_handle_rm((uv_handle_t*)(h)) diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index f32b9162a2..4f546eefcc 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -66,13 +66,8 @@ static void uv_loop_init(uv_loop_t* loop) { uv_update_time(loop); -#ifndef UV_LEAN_AND_MEAN - ngx_queue_init(&loop->active_handles); ngx_queue_init(&loop->active_reqs); -#else loop->active_handles = 0; - loop->active_reqs = 0; -#endif loop->pending_reqs_tail = NULL; @@ -215,18 +210,10 @@ static void uv_poll_ex(uv_loop_t* loop, int block) { } } -#ifndef UV_LEAN_AND_MEAN -# define UV_LOOP_ALIVE(loop) \ - (!ngx_queue_empty(&(loop)->active_handles) || \ - !ngx_queue_empty(&(loop)->active_reqs) || \ - (loop)->endgame_handles != NULL) -#else -# define UV_LOOP_ALIVE(loop) \ - ((loop)->active_handles > 0 && \ - (loop)->active_reqs > 0 && \ - (loop)->endgame_handles != NULL) -#endif - +#define UV_LOOP_ALIVE(loop) \ + ((loop)->active_handles > 0 || \ + !ngx_queue_empty(&(loop)->active_reqs) || \ + (loop)->endgame_handles != NULL) #define UV_LOOP_ONCE(loop, poll) \ do { \ diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index 481b945c97..e3d8a1fd94 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -22,6 +22,7 @@ #include "uv.h" #include "internal.h" +#include #include #include #include @@ -639,26 +640,28 @@ static DWORD WINAPI spawn_failure(void* data) { char* buf = NULL; DWORD count, written; - WriteFile(child_stderr, syscall, sizeof(syscall) - 1, &written, NULL); - - count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - process->spawn_errno, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR) &buf, - 0, - NULL); - - if (buf != NULL && count > 0) { - WriteFile(child_stderr, buf, count, &written, NULL); - LocalFree(buf); - } else { - WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL); - } + if (child_stderr != INVALID_HANDLE_VALUE) { + WriteFile(child_stderr, syscall, sizeof(syscall) - 1, &written, NULL); + + count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + process->spawn_errno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &buf, + 0, + NULL); + + if (buf != NULL && count > 0) { + WriteFile(child_stderr, buf, count, &written, NULL); + LocalFree(buf); + } else { + WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL); + } - FlushFileBuffers(child_stderr); + FlushFileBuffers(child_stderr); + } /* Post completed */ POST_COMPLETION_FOR_REQ(loop, &process->exit_req); @@ -673,7 +676,7 @@ static void close_child_stdio(uv_process_t* process) { for (i = 0; i < ARRAY_SIZE(process->child_stdio); i++) { handle = process->child_stdio[i]; - if (handle != NULL && handle != INVALID_HANDLE_VALUE) { + if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); process->child_stdio[i] = INVALID_HANDLE_VALUE; } @@ -830,20 +833,10 @@ done: } -static int duplicate_std_handle(uv_loop_t* loop, DWORD id, HANDLE* dup) { - HANDLE handle; - HANDLE current_process = GetCurrentProcess(); - - handle = GetStdHandle(id); +static int duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { + HANDLE current_process; - if (handle == NULL) { - *dup = NULL; - return 0; - } else if (handle == INVALID_HANDLE_VALUE) { - *dup = INVALID_HANDLE_VALUE; - uv__set_sys_error(loop, GetLastError()); - return -1; - } + current_process = GetCurrentProcess(); if (!DuplicateHandle(current_process, handle, @@ -861,23 +854,45 @@ static int duplicate_std_handle(uv_loop_t* loop, DWORD id, HANDLE* dup) { } +static int duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) { + HANDLE handle; + + if (fd == -1) { + *dup = INVALID_HANDLE_VALUE; + uv__set_artificial_error(loop, UV_EBADF); + return -1; + } + + handle = (HANDLE)_get_osfhandle(fd); + return duplicate_handle(loop, handle, dup); +} + + int uv_spawn(uv_loop_t* loop, uv_process_t* process, uv_process_options_t options) { int err = 0, keep_child_stdio_open = 0; wchar_t* path = NULL; - int size; + int size, i, overlapped; + DWORD server_access, child_access; BOOL result; wchar_t* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; HANDLE* child_stdio = process->child_stdio; STARTUPINFOW startup; PROCESS_INFORMATION info; + uv_pipe_t* pipe; if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { uv__set_artificial_error(loop, UV_ENOTSUP); return -1; } + /* Only support FDs 0-2 */ + if (options.stdio_count > 3) { + uv__set_artificial_error(loop, UV_ENOTSUP); + return -1; + } + assert(options.file != NULL); assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | UV_PROCESS_SETGID | @@ -927,59 +942,60 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, application_path = application; } - /* Create stdio pipes. */ - if (options.stdin_stream) { - if (options.stdin_stream->ipc) { - err = uv_create_stdio_pipe_pair( - loop, - options.stdin_stream, - &child_stdio[0], - PIPE_ACCESS_DUPLEX, - GENERIC_READ | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE, - 1); - } else { - err = uv_create_stdio_pipe_pair( - loop, - options.stdin_stream, - &child_stdio[0], - PIPE_ACCESS_OUTBOUND, - GENERIC_READ | FILE_WRITE_ATTRIBUTES, - 0); + for (i = 0; i < options.stdio_count || i < 3; i++) { + if (i >= options.stdio_count || + options.stdio[i].flags == UV_IGNORE) { + child_stdio[i] = INVALID_HANDLE_VALUE; + continue; } - } else { - err = duplicate_std_handle(loop, STD_INPUT_HANDLE, &child_stdio[0]); - } - if (err) { - goto done; - } - if (options.stdout_stream) { - err = uv_create_stdio_pipe_pair( - loop, options.stdout_stream, - &child_stdio[1], - PIPE_ACCESS_INBOUND, - GENERIC_WRITE, - 0); - } else { - err = duplicate_std_handle(loop, STD_OUTPUT_HANDLE, &child_stdio[1]); - } - if (err) { - goto done; - } + if (options.stdio[i].flags & UV_RAW_FD) { + err = duplicate_fd(loop, options.stdio[i].data.fd, &child_stdio[i]); + } else if (options.stdio[i].data.stream->type == UV_NAMED_PIPE) { + pipe = (uv_pipe_t*)options.stdio[i].data.stream; + + if (options.stdio[i].flags & UV_CREATE_PIPE) { + server_access = 0; + child_access = 0; + if (pipe->ipc) { + server_access = PIPE_ACCESS_DUPLEX; + child_access = GENERIC_READ | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE; + overlapped = 1; + } else { + overlapped = 0; + + if (options.stdio[i].flags & UV_READABLE_PIPE) { + server_access |= PIPE_ACCESS_OUTBOUND; + child_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; + } + + if (options.stdio[i].flags & UV_WRITABLE_PIPE) { + server_access |= PIPE_ACCESS_INBOUND; + child_access |= GENERIC_WRITE; + } + } + + err = uv_create_stdio_pipe_pair( + loop, + pipe, + &child_stdio[i], + server_access, + child_access, + overlapped); + } else { + err = duplicate_handle(loop, pipe->handle, &child_stdio[i]); + } + } else if(options.stdio[i].data.stream->type == UV_TTY) { + err = duplicate_handle(loop, + ((uv_tty_t*)options.stdio[i].data.stream)->handle, &child_stdio[i]); + } else { + err = -1; + uv__set_artificial_error(loop, UV_ENOTSUP); + } - if (options.stderr_stream) { - err = uv_create_stdio_pipe_pair( - loop, - options.stderr_stream, - &child_stdio[2], - PIPE_ACCESS_INBOUND, - GENERIC_WRITE, - 0); - } else { - err = duplicate_std_handle(loop, STD_ERROR_HANDLE, &child_stdio[2]); - } - if (err) { - goto done; + if (err) { + goto done; + } } startup.cb = sizeof(startup); @@ -1007,9 +1023,11 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, process->process_handle = info.hProcess; process->pid = info.dwProcessId; - if (options.stdin_stream && - options.stdin_stream->ipc) { - options.stdin_stream->ipc_pid = info.dwProcessId; + if (options.stdio_count > 0 && + options.stdio[0].flags & UV_CREATE_PIPE && + options.stdio[0].data.stream->type == UV_NAMED_PIPE && + ((uv_pipe_t*)options.stdio[0].data.stream)->ipc) { + ((uv_pipe_t*)options.stdio[0].data.stream)->ipc_pid = info.dwProcessId; } /* Setup notifications for when the child process exits. */ diff --git a/deps/uv/test/benchmark-spawn.c b/deps/uv/test/benchmark-spawn.c index d34f42b9fe..74cc3d238f 100644 --- a/deps/uv/test/benchmark-spawn.c +++ b/deps/uv/test/benchmark-spawn.c @@ -101,6 +101,7 @@ void on_read(uv_stream_t* pipe, ssize_t nread, uv_buf_t buf) { static void spawn() { + uv_stdio_container_t stdio[2]; int r; ASSERT(process_open == 0); @@ -114,7 +115,12 @@ static void spawn() { options.exit_cb = exit_cb; uv_pipe_init(loop, &out, 0); - options.stdout_stream = &out; + + options.stdio = stdio; + options.stdio_count = 2; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; r = uv_spawn(loop, &process, options); ASSERT(r == 0); diff --git a/deps/uv/test/runner-win.c b/deps/uv/test/runner-win.c index e758dd1dd3..ad36719c8f 100644 --- a/deps/uv/test/runner-win.c +++ b/deps/uv/test/runner-win.c @@ -19,6 +19,7 @@ * IN THE SOFTWARE. */ +#include #include #include #include @@ -44,6 +45,10 @@ void platform_init(int argc, char **argv) { SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + _setmode(0, _O_BINARY); + _setmode(1, _O_BINARY); + _setmode(2, _O_BINARY); + /* Disable stdio output buffering. */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); diff --git a/deps/uv/test/test-ipc.c b/deps/uv/test/test-ipc.c index 1ea0e7f963..61add0b4b8 100644 --- a/deps/uv/test/test-ipc.c +++ b/deps/uv/test/test-ipc.c @@ -200,6 +200,7 @@ void spawn_helper(uv_pipe_t* channel, char exepath[1024]; char* args[3]; int r; + uv_stdio_container_t stdio[1]; r = uv_pipe_init(uv_default_loop(), channel, 1); ASSERT(r == 0); @@ -218,7 +219,12 @@ void spawn_helper(uv_pipe_t* channel, options.file = exepath; options.args = args; options.exit_cb = exit_cb; - options.stdin_stream = channel; + + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | + UV_READABLE_PIPE | UV_WRITABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)channel; + options.stdio_count = 1; r = uv_spawn(uv_default_loop(), process, options); ASSERT(r == 0); @@ -611,4 +617,4 @@ int ipc_helper_tcp_connection() { ASSERT(close_cb_called == 4); return 0; -} \ No newline at end of file +} diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 63683f28ed..ffb1fe2346 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -123,6 +123,7 @@ TEST_DECLARE (spawn_and_kill_with_std) TEST_DECLARE (spawn_and_ping) TEST_DECLARE (spawn_setuid_fails) TEST_DECLARE (spawn_setgid_fails) +TEST_DECLARE (spawn_stdout_to_file) TEST_DECLARE (kill) TEST_DECLARE (fs_file_noent) TEST_DECLARE (fs_file_nametoolong) @@ -334,6 +335,7 @@ TASK_LIST_START TEST_ENTRY (spawn_and_ping) TEST_ENTRY (spawn_setuid_fails) TEST_ENTRY (spawn_setgid_fails) + TEST_ENTRY (spawn_stdout_to_file) TEST_ENTRY (kill) #ifdef _WIN32 TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index ccb9538f90..25863de6a5 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -21,6 +21,7 @@ #include "uv.h" #include "task.h" +#include #include #include #include @@ -167,11 +168,16 @@ TEST_IMPL(spawn_exit_code) { TEST_IMPL(spawn_stdout) { int r; uv_pipe_t out; + uv_stdio_container_t stdio[2]; init_process_options("spawn_helper2", exit_cb); uv_pipe_init(uv_default_loop(), &out, 0); - options.stdout_stream = &out; + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); @@ -185,7 +191,59 @@ TEST_IMPL(spawn_stdout) { ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ printf("output is: %s", output); - ASSERT(strcmp("hello world\n", output) == 0 || strcmp("hello world\r\n", output) == 0); + ASSERT(strcmp("hello world\n", output) == 0); + + return 0; +} + + +TEST_IMPL(spawn_stdout_to_file) { + int r; + uv_file file; + uv_fs_t fs_req; + uv_stdio_container_t stdio[2]; + + /* Setup. */ + unlink("stdout_file"); + + init_process_options("spawn_helper2", exit_cb); + + r = uv_fs_open(uv_default_loop(), &fs_req, "stdout_file", O_CREAT | O_RDWR, + S_IREAD | S_IWRITE, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&fs_req); + + file = r; + + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_RAW_FD; + options.stdio[1].data.fd = file; + options.stdio_count = 2; + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + r = uv_fs_read(uv_default_loop(), &fs_req, file, output, sizeof(output), + 0, NULL); + ASSERT(r == 12); + uv_fs_req_cleanup(&fs_req); + + r = uv_fs_close(uv_default_loop(), &fs_req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&fs_req); + + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0); + + /* Cleanup. */ + unlink("stdout_file"); return 0; } @@ -197,14 +255,19 @@ TEST_IMPL(spawn_stdin) { uv_pipe_t in; uv_write_t write_req; uv_buf_t buf; + uv_stdio_container_t stdio[2]; char buffer[] = "hello-from-spawn_stdin"; init_process_options("spawn_helper3", exit_cb); uv_pipe_init(uv_default_loop(), &out, 0); uv_pipe_init(uv_default_loop(), &in, 0); - options.stdout_stream = &out; - options.stdin_stream = ∈ + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); @@ -259,6 +322,7 @@ TEST_IMPL(spawn_and_kill_with_std) { char message[] = "Nancy's joining me because the message this evening is " "not my message but ours."; uv_buf_t buf; + uv_stdio_container_t stdio[3]; init_process_options("spawn_helper4", kill_cb); @@ -271,9 +335,14 @@ TEST_IMPL(spawn_and_kill_with_std) { r = uv_pipe_init(uv_default_loop(), &err, 0); ASSERT(r == 0); - options.stdin_stream = ∈ - options.stdout_stream = &out; - options.stderr_stream = &err; + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[2].data.stream = (uv_stream_t*)&err; + options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); @@ -308,6 +377,7 @@ TEST_IMPL(spawn_and_ping) { uv_write_t write_req; uv_pipe_t in, out; uv_buf_t buf; + uv_stdio_container_t stdio[2]; int r; init_process_options("spawn_helper3", exit_cb); @@ -315,8 +385,12 @@ TEST_IMPL(spawn_and_ping) { uv_pipe_init(uv_default_loop(), &out, 0); uv_pipe_init(uv_default_loop(), &in, 0); - options.stdout_stream = &out; - options.stdin_stream = ∈ + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); @@ -384,11 +458,16 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { uv_pipe_t out; char name[64]; HANDLE pipe_handle; + uv_stdio_container_t stdio[2]; init_process_options("spawn_helper2", exit_cb); uv_pipe_init(uv_default_loop(), &out, 0); - options.stdout_stream = &out; + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; /* Create a pipe that'll cause a collision. */ _snprintf(name, sizeof(name), "\\\\.\\pipe\\uv\\%p-%d", &out, GetCurrentProcessId()); @@ -414,7 +493,7 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ printf("output is: %s", output); - ASSERT(strcmp("hello world\n", output) == 0 || strcmp("hello world\r\n", output) == 0); + ASSERT(strcmp("hello world\n", output) == 0); return 0; } diff --git a/deps/uv/test/test-stdio-over-pipes.c b/deps/uv/test/test-stdio-over-pipes.c index 0a3f04c692..7603027fb8 100644 --- a/deps/uv/test/test-stdio-over-pipes.c +++ b/deps/uv/test/test-stdio-over-pipes.c @@ -115,14 +115,21 @@ static void on_read(uv_stream_t* tcp, ssize_t nread, uv_buf_t rdbuf) { TEST_IMPL(stdio_over_pipes) { int r; uv_process_t process; + uv_stdio_container_t stdio[2]; + loop = uv_default_loop(); init_process_options("stdio_over_pipes_helper", exit_cb); uv_pipe_init(loop, &out, 0); - options.stdout_stream = &out; uv_pipe_init(loop, &in, 0); - options.stdin_stream = ∈ + + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; r = uv_spawn(loop, &process, options); ASSERT(r == 0); diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 52b2120b47..ce29853565 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -169,12 +169,24 @@ class ProcessWrap : public HandleWrap { options.env[envc] = NULL; } + uv_stdio_container_t stdio[3]; + memset(stdio, 0, sizeof(stdio)); + + options.stdio = stdio; + options.stdio_count = 3; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_IGNORE; + options.stdio[2].flags = UV_IGNORE; + // options.stdin_stream Local stdin_stream_v = js_options->Get( String::NewSymbol("stdinStream")); if (!stdin_stream_v.IsEmpty() && stdin_stream_v->IsObject()) { PipeWrap* stdin_wrap = PipeWrap::Unwrap(stdin_stream_v->ToObject()); - options.stdin_stream = stdin_wrap->UVHandle(); + options.stdio[0].flags = static_cast( + UV_CREATE_PIPE | UV_WRITABLE_PIPE); + options.stdio[0].data.stream = reinterpret_cast( + stdin_wrap->UVHandle()); } // options.stdout_stream @@ -182,7 +194,10 @@ class ProcessWrap : public HandleWrap { String::NewSymbol("stdoutStream")); if (!stdout_stream_v.IsEmpty() && stdout_stream_v->IsObject()) { PipeWrap* stdout_wrap = PipeWrap::Unwrap(stdout_stream_v->ToObject()); - options.stdout_stream = stdout_wrap->UVHandle(); + options.stdio[1].flags = static_cast( + UV_CREATE_PIPE | UV_READABLE_PIPE); + options.stdio[1].data.stream = reinterpret_cast( + stdout_wrap->UVHandle()); } // options.stderr_stream @@ -190,7 +205,10 @@ class ProcessWrap : public HandleWrap { String::NewSymbol("stderrStream")); if (!stderr_stream_v.IsEmpty() && stderr_stream_v->IsObject()) { PipeWrap* stderr_wrap = PipeWrap::Unwrap(stderr_stream_v->ToObject()); - options.stderr_stream = stderr_wrap->UVHandle(); + options.stdio[2].flags = static_cast( + UV_CREATE_PIPE | UV_READABLE_PIPE); + options.stdio[2].data.stream = reinterpret_cast( + stderr_wrap->UVHandle()); } // options.windows_verbatim_arguments