Browse Source

deps: upgrade libuv to 7556590

v0.9.1-release
Fedor Indutny 13 years ago
parent
commit
761e0c460a
  1. 55
      deps/uv/include/uv.h
  2. 3
      deps/uv/src/unix/core.c
  3. 6
      deps/uv/src/unix/internal.h
  4. 6
      deps/uv/src/unix/loop.c
  5. 204
      deps/uv/src/unix/process.c
  6. 8
      deps/uv/src/unix/sunos.c
  7. 32
      deps/uv/src/uv-common.h
  8. 21
      deps/uv/src/win/core.c
  9. 192
      deps/uv/src/win/process.c
  10. 8
      deps/uv/test/benchmark-spawn.c
  11. 5
      deps/uv/test/runner-win.c
  12. 10
      deps/uv/test/test-ipc.c
  13. 2
      deps/uv/test/test-list.h
  14. 101
      deps/uv/test/test-spawn.c
  15. 11
      deps/uv/test/test-stdio-over-pipes.c
  16. 24
      src/process_wrap.cc

55
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_strerror(uv_err_t err);
UV_EXTERN const char* uv_err_name(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 \ #define UV_REQ_FIELDS \
/* public */ \ /* public */ \
void* data; \ void* data; \
UV_REQ_EXTRA_FIELDS \
/* private */ \ /* private */ \
ngx_queue_t active_queue; \
UV_REQ_PRIVATE_FIELDS \ UV_REQ_PRIVATE_FIELDS \
/* read-only */ \ /* read-only */ \
uv_req_type type; \ 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 \ #define UV_HANDLE_FIELDS \
/* read-only */ \ /* read-only */ \
uv_loop_t* loop; \ uv_loop_t* loop; \
@ -394,7 +383,6 @@ struct uv_shutdown_s {
uv_handle_type type; \ uv_handle_type type; \
/* private */ \ /* private */ \
UV_HANDLE_PRIVATE_FIELDS \ UV_HANDLE_PRIVATE_FIELDS \
UV_HANDLE_EXTRA_FIELDS \
/* The abstract base class of all handles. */ /* The abstract base class of all handles. */
struct uv_handle_s { 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_EXTERN void uv_freeaddrinfo(struct addrinfo* ai);
/* uv_spawn() options */ /* 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 { typedef struct uv_process_options_s {
uv_exit_cb exit_cb; /* Called after the process exits. */ uv_exit_cb exit_cb; /* Called after the process exits. */
const char* file; /* Path to program to execute. */ const char* file; /* Path to program to execute. */
@ -1200,14 +1209,12 @@ typedef struct uv_process_options_s {
*/ */
uv_uid_t uid; uv_uid_t uid;
uv_gid_t gid; uv_gid_t gid;
/* /*
* The user should supply pointers to initialized uv_pipe_t structs for * A container of stdio streams (stdin/stdout/stderr)
* stdio. This is used to to send or receive input from the subprocess.
* The user is responsible for calling uv_close on them.
*/ */
uv_pipe_t* stdin_stream; uv_stdio_container_t* stdio;
uv_pipe_t* stdout_stream; int stdio_count;
uv_pipe_t* stderr_stream;
} uv_process_options_t; } uv_process_options_t;
/* /*
@ -1659,15 +1666,11 @@ struct uv_loop_s {
uv_counters_t counters; uv_counters_t counters;
/* The last error */ /* The last error */
uv_err_t last_err; uv_err_t last_err;
/* Loop reference counting */
unsigned int active_handles;
ngx_queue_t active_reqs;
/* User data - use this for whatever. */ /* User data - use this for whatever. */
void* data; 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
}; };

3
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) { static int uv__should_block(uv_loop_t* loop) {
return ngx_queue_empty(&loop->idle_handles) return loop->active_handles && ngx_queue_empty(&loop->idle_handles);
&& !ngx_queue_empty(&loop->active_handles);
} }

6
deps/uv/src/unix/internal.h

@ -115,11 +115,7 @@ inline static void uv__req_init(uv_loop_t* loop,
uv_req_type type) { uv_req_type type) {
loop->counters.req_init++; loop->counters.req_init++;
req->type = type; req->type = type;
#ifndef UV_LEAN_AND_MEAN uv__req_register(loop, req);
ngx_queue_insert_tail(&loop->active_reqs, &req->active_queue);
#else
loop->active_reqs++;
#endif
} }
#define uv__req_init(loop, req, type) \ #define uv__req_init(loop, req, type) \
uv__req_init((loop), (uv_req_t*)(req), (type)) uv__req_init((loop), (uv_req_t*)(req), (type))

6
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)); 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); RB_INIT(&loop->ares_handles);
ngx_queue_init(&loop->active_reqs);
ngx_queue_init(&loop->idle_handles); ngx_queue_init(&loop->idle_handles);
ngx_queue_init(&loop->check_handles); ngx_queue_init(&loop->check_handles);
ngx_queue_init(&loop->prepare_handles); ngx_queue_init(&loop->prepare_handles);

204
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 * Used for initializing stdio streams like options.stdin_stream. Returns
* zero on success. * zero on success.
*/ */
static int uv__process_init_pipe(uv_pipe_t* handle, int fds[2], int flags) { static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2],
if (handle->type != UV_NAMED_PIPE) { int writable) {
errno = EINVAL; 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; return -1;
} }
}
if (handle->ipc) static int uv__process_stdio_flags(uv_stdio_container_t* container,
return uv__make_socketpair(fds, flags); int writable) {
else if (container->data.stream->type == UV_NAMED_PIPE &&
return uv__make_pipe(fds, flags); ((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 #ifndef SPAWN_WAIT_EXEC
# define SPAWN_WAIT_EXEC 1 # define SPAWN_WAIT_EXEC 1
#endif #endif
@ -162,16 +222,21 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
* by the child process. * by the child process.
*/ */
char** save_our_env = environ; char** save_our_env = environ;
int stdin_pipe[2] = { -1, -1 };
int stdout_pipe[2] = { -1, -1 }; int* pipes = malloc(2 * options.stdio_count * sizeof(int));
int stderr_pipe[2] = { -1, -1 };
#if SPAWN_WAIT_EXEC #if SPAWN_WAIT_EXEC
int signal_pipe[2] = { -1, -1 }; int signal_pipe[2] = { -1, -1 };
struct pollfd pfd; struct pollfd pfd;
#endif #endif
int status; int status;
pid_t pid; pid_t pid;
int flags; int i;
if (pipes == NULL) {
errno = ENOMEM;
goto error;
}
assert(options.file != NULL); assert(options.file != NULL);
assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | 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; process->exit_cb = options.exit_cb;
if (options.stdin_stream && /* Init pipe pairs */
uv__process_init_pipe(options.stdin_stream, stdin_pipe, 0)) { for (i = 0; i < options.stdio_count; i++) {
goto error; 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;
} }
if (options.stderr_stream && /* Create socketpairs/pipes, or use raw fd */
uv__process_init_pipe(options.stderr_stream, stderr_pipe, 0)) { for (i = 0; i < options.stdio_count; i++) {
goto error; 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 /* 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 (pid == 0) {
if (stdin_pipe[0] >= 0) { /* Child */
close(stdin_pipe[1]);
dup2(stdin_pipe[0], STDIN_FILENO); /* Dup fds */
} else { for (i = 0; i < options.stdio_count; i++) {
/* Reset flags that might be set by Node */ /*
uv__cloexec(STDIN_FILENO, 0); * stdin has swapped ends of pipe
uv__nonblock(STDIN_FILENO, 0); * (it's the only one readable stream)
} */
int close_fd = i == 0 ? pipes[i * 2 + 1] : pipes[i * 2];
if (stdout_pipe[1] >= 0) { int use_fd = i == 0 ? pipes[i * 2] : pipes[i * 2 + 1];
close(stdout_pipe[0]);
dup2(stdout_pipe[1], STDOUT_FILENO); if (use_fd >= 0) {
} else { close(close_fd);
/* Reset flags that might be set by Node */ dup2(use_fd, i);
uv__cloexec(STDOUT_FILENO, 0); } else {
uv__nonblock(STDOUT_FILENO, 0); /* Reset flags that might be set by Node */
} uv__cloexec(i, 0);
uv__nonblock(i, 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)) { 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); ev_child_start(process->loop->ev, &process->child_watcher);
process->child_watcher.data = process; process->child_watcher.data = process;
if (stdin_pipe[1] >= 0) { for (i = 0; i < options.stdio_count; i++) {
assert(options.stdin_stream); if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
assert(stdin_pipe[0] >= 0); int j;
close(stdin_pipe[0]); /* Close all opened streams */
uv__nonblock(stdin_pipe[1], 1); for (j = 0; j < i; j++) {
flags = UV_STREAM_WRITABLE | uv__process_close_stream(&options.stdio[j]);
(options.stdin_stream->ipc ? UV_STREAM_READABLE : 0); }
uv__stream_open((uv_stream_t*)options.stdin_stream, stdin_pipe[1],
flags);
}
if (stdout_pipe[0] >= 0) { goto error;
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);
} }
if (stderr_pipe[0] >= 0) { free(pipes);
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);
}
return 0; return 0;
error: error:
uv__set_sys_error(process->loop, errno); uv__set_sys_error(process->loop, errno);
close(stdin_pipe[0]);
close(stdin_pipe[1]); for (i = 0; i < options.stdio_count; i++) {
close(stdout_pipe[0]); close(pipes[i * 2]);
close(stdout_pipe[1]); close(pipes[i * 2 + 1]);
close(stderr_pipe[0]); }
close(stderr_pipe[1]);
free(pipes);
return -1; return -1;
} }

8
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. */ /* We don't support any flags yet. */
assert(!flags); assert(!flags);
if (loop->fs_fd == -1) { if (loop->fs_fd == -1) {
if ((portfd = port_create()) == -1) { if ((portfd = port_create()) == -1) {
uv__set_sys_error(loop, errno); uv__set_sys_error(loop, errno);
return -1; return -1;
} }
loop->fs_fd = portfd; loop->fs_fd = portfd;
first_run = 1; first_run = 1;
} }

32
deps/uv/src/uv-common.h

@ -82,25 +82,11 @@ int uv__tcp_connect6(uv_connect_t* req,
struct sockaddr_in6 address, struct sockaddr_in6 address,
uv_connect_cb cb); 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) { UNUSED static int uv__has_active_reqs(const uv_loop_t* loop) {
return !ngx_queue_empty(&loop->active_reqs); 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) { UNUSED static void uv__req_register(uv_loop_t* loop, uv_req_t* req) {
ngx_queue_insert_tail(&loop->active_reqs, &req->active_queue); 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); ngx_queue_remove(&req->active_queue);
} }
#else /* UV_LEAN_AND_MEAN */
UNUSED static int uv__has_active_handles(const uv_loop_t* loop) { UNUSED static int uv__has_active_handles(const uv_loop_t* loop) {
return loop->active_handles > 0; 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) { UNUSED static void uv__active_handle_add(uv_handle_t* h) {
h->loop->active_handles++; h->loop->active_handles++;
} }
UNUSED static void uv__active_handle_rm(uv_handle_t* h) { UNUSED static void uv__active_handle_rm(uv_handle_t* h) {
assert(h->loop->active_handles > 0);
h->loop->active_handles--; 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_add(h) uv__active_handle_add((uv_handle_t*)(h))
#define uv__active_handle_rm(h) uv__active_handle_rm((uv_handle_t*)(h)) #define uv__active_handle_rm(h) uv__active_handle_rm((uv_handle_t*)(h))

21
deps/uv/src/win/core.c

@ -66,13 +66,8 @@ static void uv_loop_init(uv_loop_t* loop) {
uv_update_time(loop); uv_update_time(loop);
#ifndef UV_LEAN_AND_MEAN
ngx_queue_init(&loop->active_handles);
ngx_queue_init(&loop->active_reqs); ngx_queue_init(&loop->active_reqs);
#else
loop->active_handles = 0; loop->active_handles = 0;
loop->active_reqs = 0;
#endif
loop->pending_reqs_tail = NULL; 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) \
# define UV_LOOP_ALIVE(loop) \ ((loop)->active_handles > 0 || \
(!ngx_queue_empty(&(loop)->active_handles) || \ !ngx_queue_empty(&(loop)->active_reqs) || \
!ngx_queue_empty(&(loop)->active_reqs) || \ (loop)->endgame_handles != NULL)
(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_ONCE(loop, poll) \ #define UV_LOOP_ONCE(loop, poll) \
do { \ do { \

192
deps/uv/src/win/process.c

@ -22,6 +22,7 @@
#include "uv.h" #include "uv.h"
#include "internal.h" #include "internal.h"
#include <io.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -639,26 +640,28 @@ static DWORD WINAPI spawn_failure(void* data) {
char* buf = NULL; char* buf = NULL;
DWORD count, written; DWORD count, written;
WriteFile(child_stderr, syscall, sizeof(syscall) - 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 | count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_FROM_SYSTEM |
NULL, FORMAT_MESSAGE_IGNORE_INSERTS,
process->spawn_errno, NULL,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), process->spawn_errno,
(LPSTR) &buf, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
0, (LPSTR) &buf,
NULL); 0,
NULL);
if (buf != NULL && count > 0) {
WriteFile(child_stderr, buf, count, &written, NULL); if (buf != NULL && count > 0) {
LocalFree(buf); WriteFile(child_stderr, buf, count, &written, NULL);
} else { LocalFree(buf);
WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL); } else {
} WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL);
}
FlushFileBuffers(child_stderr); FlushFileBuffers(child_stderr);
}
/* Post completed */ /* Post completed */
POST_COMPLETION_FOR_REQ(loop, &process->exit_req); 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++) { for (i = 0; i < ARRAY_SIZE(process->child_stdio); i++) {
handle = process->child_stdio[i]; handle = process->child_stdio[i];
if (handle != NULL && handle != INVALID_HANDLE_VALUE) { if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle); CloseHandle(handle);
process->child_stdio[i] = INVALID_HANDLE_VALUE; 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) { static int duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) {
HANDLE handle; HANDLE current_process;
HANDLE current_process = GetCurrentProcess();
handle = GetStdHandle(id);
if (handle == NULL) { current_process = GetCurrentProcess();
*dup = NULL;
return 0;
} else if (handle == INVALID_HANDLE_VALUE) {
*dup = INVALID_HANDLE_VALUE;
uv__set_sys_error(loop, GetLastError());
return -1;
}
if (!DuplicateHandle(current_process, if (!DuplicateHandle(current_process,
handle, 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, int uv_spawn(uv_loop_t* loop, uv_process_t* process,
uv_process_options_t options) { uv_process_options_t options) {
int err = 0, keep_child_stdio_open = 0; int err = 0, keep_child_stdio_open = 0;
wchar_t* path = NULL; wchar_t* path = NULL;
int size; int size, i, overlapped;
DWORD server_access, child_access;
BOOL result; BOOL result;
wchar_t* application_path = NULL, *application = NULL, *arguments = NULL, wchar_t* application_path = NULL, *application = NULL, *arguments = NULL,
*env = NULL, *cwd = NULL; *env = NULL, *cwd = NULL;
HANDLE* child_stdio = process->child_stdio; HANDLE* child_stdio = process->child_stdio;
STARTUPINFOW startup; STARTUPINFOW startup;
PROCESS_INFORMATION info; PROCESS_INFORMATION info;
uv_pipe_t* pipe;
if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
uv__set_artificial_error(loop, UV_ENOTSUP); uv__set_artificial_error(loop, UV_ENOTSUP);
return -1; 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.file != NULL);
assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
UV_PROCESS_SETGID | UV_PROCESS_SETGID |
@ -927,59 +942,60 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
application_path = application; application_path = application;
} }
/* Create stdio pipes. */ for (i = 0; i < options.stdio_count || i < 3; i++) {
if (options.stdin_stream) { if (i >= options.stdio_count ||
if (options.stdin_stream->ipc) { options.stdio[i].flags == UV_IGNORE) {
err = uv_create_stdio_pipe_pair( child_stdio[i] = INVALID_HANDLE_VALUE;
loop, continue;
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);
} }
} else {
err = duplicate_std_handle(loop, STD_INPUT_HANDLE, &child_stdio[0]);
}
if (err) {
goto done;
}
if (options.stdout_stream) { if (options.stdio[i].flags & UV_RAW_FD) {
err = uv_create_stdio_pipe_pair( err = duplicate_fd(loop, options.stdio[i].data.fd, &child_stdio[i]);
loop, options.stdout_stream, } else if (options.stdio[i].data.stream->type == UV_NAMED_PIPE) {
&child_stdio[1], pipe = (uv_pipe_t*)options.stdio[i].data.stream;
PIPE_ACCESS_INBOUND,
GENERIC_WRITE, if (options.stdio[i].flags & UV_CREATE_PIPE) {
0); server_access = 0;
} else { child_access = 0;
err = duplicate_std_handle(loop, STD_OUTPUT_HANDLE, &child_stdio[1]); if (pipe->ipc) {
} server_access = PIPE_ACCESS_DUPLEX;
if (err) { child_access = GENERIC_READ | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE;
goto done; 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) { if (err) {
err = uv_create_stdio_pipe_pair( goto done;
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;
} }
startup.cb = sizeof(startup); 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->process_handle = info.hProcess;
process->pid = info.dwProcessId; process->pid = info.dwProcessId;
if (options.stdin_stream && if (options.stdio_count > 0 &&
options.stdin_stream->ipc) { options.stdio[0].flags & UV_CREATE_PIPE &&
options.stdin_stream->ipc_pid = info.dwProcessId; 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. */ /* Setup notifications for when the child process exits. */

8
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() { static void spawn() {
uv_stdio_container_t stdio[2];
int r; int r;
ASSERT(process_open == 0); ASSERT(process_open == 0);
@ -114,7 +115,12 @@ static void spawn() {
options.exit_cb = exit_cb; options.exit_cb = exit_cb;
uv_pipe_init(loop, &out, 0); 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); r = uv_spawn(loop, &process, options);
ASSERT(r == 0); ASSERT(r == 0);

5
deps/uv/test/runner-win.c

@ -19,6 +19,7 @@
* IN THE SOFTWARE. * IN THE SOFTWARE.
*/ */
#include <fcntl.h>
#include <io.h> #include <io.h>
#include <malloc.h> #include <malloc.h>
#include <stdio.h> #include <stdio.h>
@ -44,6 +45,10 @@ void platform_init(int argc, char **argv) {
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX); SEM_NOOPENFILEERRORBOX);
_setmode(0, _O_BINARY);
_setmode(1, _O_BINARY);
_setmode(2, _O_BINARY);
/* Disable stdio output buffering. */ /* Disable stdio output buffering. */
setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0);

10
deps/uv/test/test-ipc.c

@ -200,6 +200,7 @@ void spawn_helper(uv_pipe_t* channel,
char exepath[1024]; char exepath[1024];
char* args[3]; char* args[3];
int r; int r;
uv_stdio_container_t stdio[1];
r = uv_pipe_init(uv_default_loop(), channel, 1); r = uv_pipe_init(uv_default_loop(), channel, 1);
ASSERT(r == 0); ASSERT(r == 0);
@ -218,7 +219,12 @@ void spawn_helper(uv_pipe_t* channel,
options.file = exepath; options.file = exepath;
options.args = args; options.args = args;
options.exit_cb = exit_cb; 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); r = uv_spawn(uv_default_loop(), process, options);
ASSERT(r == 0); ASSERT(r == 0);
@ -611,4 +617,4 @@ int ipc_helper_tcp_connection() {
ASSERT(close_cb_called == 4); ASSERT(close_cb_called == 4);
return 0; return 0;
} }

2
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_and_ping)
TEST_DECLARE (spawn_setuid_fails) TEST_DECLARE (spawn_setuid_fails)
TEST_DECLARE (spawn_setgid_fails) TEST_DECLARE (spawn_setgid_fails)
TEST_DECLARE (spawn_stdout_to_file)
TEST_DECLARE (kill) TEST_DECLARE (kill)
TEST_DECLARE (fs_file_noent) TEST_DECLARE (fs_file_noent)
TEST_DECLARE (fs_file_nametoolong) TEST_DECLARE (fs_file_nametoolong)
@ -334,6 +335,7 @@ TASK_LIST_START
TEST_ENTRY (spawn_and_ping) TEST_ENTRY (spawn_and_ping)
TEST_ENTRY (spawn_setuid_fails) TEST_ENTRY (spawn_setuid_fails)
TEST_ENTRY (spawn_setgid_fails) TEST_ENTRY (spawn_setgid_fails)
TEST_ENTRY (spawn_stdout_to_file)
TEST_ENTRY (kill) TEST_ENTRY (kill)
#ifdef _WIN32 #ifdef _WIN32
TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows)

101
deps/uv/test/test-spawn.c

@ -21,6 +21,7 @@
#include "uv.h" #include "uv.h"
#include "task.h" #include "task.h"
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -167,11 +168,16 @@ TEST_IMPL(spawn_exit_code) {
TEST_IMPL(spawn_stdout) { TEST_IMPL(spawn_stdout) {
int r; int r;
uv_pipe_t out; uv_pipe_t out;
uv_stdio_container_t stdio[2];
init_process_options("spawn_helper2", exit_cb); init_process_options("spawn_helper2", exit_cb);
uv_pipe_init(uv_default_loop(), &out, 0); 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); r = uv_spawn(uv_default_loop(), &process, options);
ASSERT(r == 0); ASSERT(r == 0);
@ -185,7 +191,59 @@ TEST_IMPL(spawn_stdout) {
ASSERT(exit_cb_called == 1); ASSERT(exit_cb_called == 1);
ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
printf("output is: %s", output); 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; return 0;
} }
@ -197,14 +255,19 @@ TEST_IMPL(spawn_stdin) {
uv_pipe_t in; uv_pipe_t in;
uv_write_t write_req; uv_write_t write_req;
uv_buf_t buf; uv_buf_t buf;
uv_stdio_container_t stdio[2];
char buffer[] = "hello-from-spawn_stdin"; char buffer[] = "hello-from-spawn_stdin";
init_process_options("spawn_helper3", exit_cb); init_process_options("spawn_helper3", exit_cb);
uv_pipe_init(uv_default_loop(), &out, 0); uv_pipe_init(uv_default_loop(), &out, 0);
uv_pipe_init(uv_default_loop(), &in, 0); uv_pipe_init(uv_default_loop(), &in, 0);
options.stdout_stream = &out; options.stdio = stdio;
options.stdin_stream = &in; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
options.stdio[0].data.stream = (uv_stream_t*)&in;
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); r = uv_spawn(uv_default_loop(), &process, options);
ASSERT(r == 0); 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 " char message[] = "Nancy's joining me because the message this evening is "
"not my message but ours."; "not my message but ours.";
uv_buf_t buf; uv_buf_t buf;
uv_stdio_container_t stdio[3];
init_process_options("spawn_helper4", kill_cb); 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); r = uv_pipe_init(uv_default_loop(), &err, 0);
ASSERT(r == 0); ASSERT(r == 0);
options.stdin_stream = &in; options.stdio = stdio;
options.stdout_stream = &out; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
options.stderr_stream = &err; options.stdio[0].data.stream = (uv_stream_t*)&in;
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); r = uv_spawn(uv_default_loop(), &process, options);
ASSERT(r == 0); ASSERT(r == 0);
@ -308,6 +377,7 @@ TEST_IMPL(spawn_and_ping) {
uv_write_t write_req; uv_write_t write_req;
uv_pipe_t in, out; uv_pipe_t in, out;
uv_buf_t buf; uv_buf_t buf;
uv_stdio_container_t stdio[2];
int r; int r;
init_process_options("spawn_helper3", exit_cb); 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(), &out, 0);
uv_pipe_init(uv_default_loop(), &in, 0); uv_pipe_init(uv_default_loop(), &in, 0);
options.stdout_stream = &out; options.stdio = stdio;
options.stdin_stream = &in; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
options.stdio[0].data.stream = (uv_stream_t*)&in;
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); r = uv_spawn(uv_default_loop(), &process, options);
ASSERT(r == 0); ASSERT(r == 0);
@ -384,11 +458,16 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) {
uv_pipe_t out; uv_pipe_t out;
char name[64]; char name[64];
HANDLE pipe_handle; HANDLE pipe_handle;
uv_stdio_container_t stdio[2];
init_process_options("spawn_helper2", exit_cb); init_process_options("spawn_helper2", exit_cb);
uv_pipe_init(uv_default_loop(), &out, 0); 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. */ /* Create a pipe that'll cause a collision. */
_snprintf(name, sizeof(name), "\\\\.\\pipe\\uv\\%p-%d", &out, GetCurrentProcessId()); _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(exit_cb_called == 1);
ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
printf("output is: %s", output); 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; return 0;
} }

11
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) { TEST_IMPL(stdio_over_pipes) {
int r; int r;
uv_process_t process; uv_process_t process;
uv_stdio_container_t stdio[2];
loop = uv_default_loop(); loop = uv_default_loop();
init_process_options("stdio_over_pipes_helper", exit_cb); init_process_options("stdio_over_pipes_helper", exit_cb);
uv_pipe_init(loop, &out, 0); uv_pipe_init(loop, &out, 0);
options.stdout_stream = &out;
uv_pipe_init(loop, &in, 0); uv_pipe_init(loop, &in, 0);
options.stdin_stream = &in;
options.stdio = stdio;
options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
options.stdio[0].data.stream = (uv_stream_t*)&in;
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); r = uv_spawn(loop, &process, options);
ASSERT(r == 0); ASSERT(r == 0);

24
src/process_wrap.cc

@ -169,12 +169,24 @@ class ProcessWrap : public HandleWrap {
options.env[envc] = NULL; 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 // options.stdin_stream
Local<Value> stdin_stream_v = js_options->Get( Local<Value> stdin_stream_v = js_options->Get(
String::NewSymbol("stdinStream")); String::NewSymbol("stdinStream"));
if (!stdin_stream_v.IsEmpty() && stdin_stream_v->IsObject()) { if (!stdin_stream_v.IsEmpty() && stdin_stream_v->IsObject()) {
PipeWrap* stdin_wrap = PipeWrap::Unwrap(stdin_stream_v->ToObject()); PipeWrap* stdin_wrap = PipeWrap::Unwrap(stdin_stream_v->ToObject());
options.stdin_stream = stdin_wrap->UVHandle(); options.stdio[0].flags = static_cast<uv_stdio_flags>(
UV_CREATE_PIPE | UV_WRITABLE_PIPE);
options.stdio[0].data.stream = reinterpret_cast<uv_stream_t*>(
stdin_wrap->UVHandle());
} }
// options.stdout_stream // options.stdout_stream
@ -182,7 +194,10 @@ class ProcessWrap : public HandleWrap {
String::NewSymbol("stdoutStream")); String::NewSymbol("stdoutStream"));
if (!stdout_stream_v.IsEmpty() && stdout_stream_v->IsObject()) { if (!stdout_stream_v.IsEmpty() && stdout_stream_v->IsObject()) {
PipeWrap* stdout_wrap = PipeWrap::Unwrap(stdout_stream_v->ToObject()); PipeWrap* stdout_wrap = PipeWrap::Unwrap(stdout_stream_v->ToObject());
options.stdout_stream = stdout_wrap->UVHandle(); options.stdio[1].flags = static_cast<uv_stdio_flags>(
UV_CREATE_PIPE | UV_READABLE_PIPE);
options.stdio[1].data.stream = reinterpret_cast<uv_stream_t*>(
stdout_wrap->UVHandle());
} }
// options.stderr_stream // options.stderr_stream
@ -190,7 +205,10 @@ class ProcessWrap : public HandleWrap {
String::NewSymbol("stderrStream")); String::NewSymbol("stderrStream"));
if (!stderr_stream_v.IsEmpty() && stderr_stream_v->IsObject()) { if (!stderr_stream_v.IsEmpty() && stderr_stream_v->IsObject()) {
PipeWrap* stderr_wrap = PipeWrap::Unwrap(stderr_stream_v->ToObject()); PipeWrap* stderr_wrap = PipeWrap::Unwrap(stderr_stream_v->ToObject());
options.stderr_stream = stderr_wrap->UVHandle(); options.stdio[2].flags = static_cast<uv_stdio_flags>(
UV_CREATE_PIPE | UV_READABLE_PIPE);
options.stdio[2].data.stream = reinterpret_cast<uv_stream_t*>(
stderr_wrap->UVHandle());
} }
// options.windows_verbatim_arguments // options.windows_verbatim_arguments

Loading…
Cancel
Save