diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 25998494..10a8ec0c 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -456,7 +456,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); struct uv_process_close_s { \ UV_REQ_FIELDS \ } close_req; \ - void* child_stdio_buffer; \ + BYTE* child_stdio_buffer; \ int exit_signal; \ DWORD spawn_errno; \ HANDLE wait_handle; \ diff --git a/src/win/internal.h b/src/win/internal.h index f5d0d172..b5dadb91 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -279,6 +279,17 @@ void uv_fatal_error(const int errorno, const char* syscall); uv_err_code uv_translate_sys_error(int sys_errno); +/* + * Process stdio handles. + */ +int uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options, + BYTE** buffer_ptr); +void uv__stdio_destroy(BYTE* buffer); +void uv__stdio_noinherit(BYTE* buffer); +WORD uv__stdio_size(BYTE* buffer); +HANDLE uv__stdio_handle(BYTE* buffer, int fd); + + /* * Winapi and ntapi utility functions */ diff --git a/src/win/process-stdio.c b/src/win/process-stdio.c new file mode 100644 index 00000000..ad998aee --- /dev/null +++ b/src/win/process-stdio.c @@ -0,0 +1,425 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "uv.h" +#include "internal.h" + + +/* + * The `child_stdio_buffer` buffer has the following layout: + * int number_of_fds + * unsigned char crt_flags[number_of_fds] + * HANDLE os_handle[number_of_fds] + */ +#define CHILD_STDIO_SIZE(count) \ + (sizeof(int) + \ + sizeof(unsigned char) * (count) + \ + sizeof(uintptr_t) * (count)) + +#define CHILD_STDIO_COUNT(buffer) \ + *((unsigned int*) (buffer)) + +#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \ + *((unsigned char*) (buffer) + sizeof(int) + fd) + +#define CHILD_STDIO_HANDLE(buffer, fd) \ + *((HANDLE*) ((unsigned char*) (buffer) + \ + sizeof(int) + \ + sizeof(unsigned char) * \ + CHILD_STDIO_COUNT((buffer)) + \ + sizeof(HANDLE) * (fd))) + + +/* CRT file descriptor mode flags */ +#define FOPEN 0x01 +#define FEOFLAG 0x02 +#define FCRLF 0x04 +#define FPIPE 0x08 +#define FNOINHERIT 0x10 +#define FAPPEND 0x20 +#define FDEV 0x40 +#define FTEXT 0x80 + + +static int uv__create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe, + HANDLE* child_pipe_ptr, unsigned int flags) { + char pipe_name[64]; + SECURITY_ATTRIBUTES sa; + DWORD server_access = 0; + DWORD client_access = 0; + HANDLE child_pipe = INVALID_HANDLE_VALUE; + + if (flags & UV_READABLE_PIPE) { + server_access |= PIPE_ACCESS_OUTBOUND; + client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; + } + if (flags & UV_WRITABLE_PIPE) { + server_access |= PIPE_ACCESS_INBOUND; + client_access |= GENERIC_WRITE; + } + + /* Create server pipe handle. */ + if (uv_stdio_pipe_server(loop, + server_pipe, + server_access, + pipe_name, + sizeof(pipe_name)) < 0) { + goto error; + } + + /* Create child pipe handle. */ + sa.nLength = sizeof sa; + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + child_pipe = CreateFileA(pipe_name, + client_access, + 0, + &sa, + OPEN_EXISTING, + server_pipe->ipc ? FILE_FLAG_OVERLAPPED : 0, + NULL); + if (child_pipe == INVALID_HANDLE_VALUE) { + uv__set_sys_error(loop, GetLastError()); + goto error; + } + +#ifndef NDEBUG + /* Validate that the pipe was opened in the right mode. */ + { + DWORD mode; + BOOL r = GetNamedPipeHandleState(child_pipe, + &mode, + NULL, + NULL, + NULL, + NULL, + 0); + assert(r == TRUE); + assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); + } +#endif + + /* Do a blocking ConnectNamedPipe. This should not block because we have */ + /* both ends of the pipe created. */ + if (!ConnectNamedPipe(server_pipe->handle, NULL)) { + if (GetLastError() != ERROR_PIPE_CONNECTED) { + uv__set_sys_error(loop, GetLastError()); + goto error; + } + } + + *child_pipe_ptr = child_pipe; + return 0; + + error: + if (server_pipe->handle != INVALID_HANDLE_VALUE) { + uv_pipe_cleanup(loop, server_pipe); + } + + if (child_pipe != INVALID_HANDLE_VALUE) { + CloseHandle(child_pipe); + } + + return -1; +} + + +static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { + HANDLE current_process; + + current_process = GetCurrentProcess(); + + if (!DuplicateHandle(current_process, + handle, + current_process, + dup, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) { + *dup = INVALID_HANDLE_VALUE; + uv__set_sys_error(loop, GetLastError()); + return -1; + } + + return 0; +} + + +static int uv__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 uv__duplicate_handle(loop, handle, dup); +} + + +static int uv__create_nul_handle(uv_loop_t* loop, HANDLE* handle_ptr, + DWORD access) { + HANDLE handle; + SECURITY_ATTRIBUTES sa; + + sa.nLength = sizeof sa; + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + handle = CreateFileW(L"NUL", + access, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + OPEN_EXISTING, + 0, + NULL); + if (handle == INVALID_HANDLE_VALUE) { + uv__set_sys_error(loop, GetLastError()); + return -1; + } + + *handle_ptr = handle; + return 0; +} + + +int uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options, + BYTE** buffer_ptr) { + BYTE* buffer; + int count, i; + + count = options->stdio_count; + + if (count < 0 || count > 255) { + /* Only support FDs 0-255 */ + uv__set_artificial_error(loop, UV_ENOTSUP); + return -1; + } else if (count < 3) { + /* There should always be at least 3 stdio handles. */ + count = 3; + } + + /* Allocate the child stdio buffer */ + buffer = malloc(CHILD_STDIO_SIZE(count)); + if (buffer == NULL) { + uv__set_artificial_error(loop, UV_ENOMEM); + return -1; + } + + /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */ + /* clean up on failure. */ + CHILD_STDIO_COUNT(buffer) = count; + for (i = 0; i < count; i++) { + CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; + CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; + } + + for (i = 0; i < count; i++) { + uv_stdio_container_t fdopt; + if (i < options->stdio_count) { + fdopt = options->stdio[i]; + } else { + fdopt.flags = UV_IGNORE; + } + + switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | + UV_INHERIT_STREAM)) { + case UV_IGNORE: + /* Starting a process with no stdin/stout/stderr can confuse it. */ + /* So no matter what the user specified, we make sure the first */ + /* three FDs are always open in their typical modes, e.g. stdin */ + /* be readable and stdout/err should be writable. For FDs > 2, don't */ + /* do anything - all handles in the stdio buffer are initialized with */ + /* INVALID_HANDLE_VALUE, which should be okay. */ + if (i <= 2) { + DWORD access = (i == 0) ? FILE_GENERIC_READ : + FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES; + if (uv__create_nul_handle(loop, + &CHILD_STDIO_HANDLE(buffer, i), + access) < 0) { + goto error; + } + CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; + } + break; + + case UV_CREATE_PIPE: { + /* Create a pair of two connected pipe ends; one end is turned into */ + /* an uv_pipe_t for use by the parent. The other one is given to */ + /* the child. */ + uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream; + HANDLE child_pipe; + + /* Create a new, connected pipe pair. stdio[i].stream should point */ + /* to an uninitialized, but not connected pipe handle. */ + assert(fdopt.data.stream->type == UV_NAMED_PIPE); + assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION)); + assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER)); + + if (uv__create_stdio_pipe_pair(loop, + parent_pipe, + &child_pipe, + fdopt.flags) < 0) { + goto error; + } + + CHILD_STDIO_HANDLE(buffer, i) = child_pipe; + CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; + break; + } + + case UV_INHERIT_FD: { + /* Inherit a raw FD. */ + HANDLE child_handle; + + /* Make an inheritable duplicate of the handle. */ + if (uv__duplicate_fd(loop, fdopt.data.fd, &child_handle) < 0) { + goto error; + } + + /* Figure out what the type is. */ + switch (GetFileType(child_handle)) { + case FILE_TYPE_DISK: + CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN; + break; + + case FILE_TYPE_PIPE: + CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; + + case FILE_TYPE_CHAR: + case FILE_TYPE_REMOTE: + CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; + break; + + case FILE_TYPE_UNKNOWN: + if (GetLastError != 0) { + uv__set_sys_error(loop, GetLastError()); + CloseHandle(child_handle); + goto error; + } + CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; + break; + + default: + assert(0); + } + + CHILD_STDIO_HANDLE(buffer, i) = child_handle; + break; + } + + case UV_INHERIT_STREAM: { + /* Use an existing stream as the stdio handle for the child. */ + HANDLE stream_handle, child_handle; + unsigned char crt_flags; + uv_stream_t* stream = fdopt.data.stream; + + /* Leech the handle out of the stream. */ + if (stream->type == UV_TTY) { + stream_handle = ((uv_tty_t*) stream)->handle; + crt_flags = FOPEN | FDEV; + } else if (stream->type == UV_NAMED_PIPE && + stream->flags & UV_HANDLE_CONNECTED) { + stream_handle = ((uv_pipe_t*) stream)->handle; + crt_flags = FOPEN | FPIPE; + } else { + stream_handle = INVALID_HANDLE_VALUE; + crt_flags = 0; + } + + if (stream_handle == NULL || + stream_handle == INVALID_HANDLE_VALUE) { + /* The handle is already closed, or not yet created, or the */ + /* stream type is not supported. */ + uv__set_artificial_error(loop, UV_ENOTSUP); + goto error; + } + + /* Make an inheritable copy of the handle. */ + if (uv__duplicate_handle(loop, + stream_handle, + &child_handle) < 0) { + goto error; + } + + CHILD_STDIO_HANDLE(buffer, i) = child_handle; + CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags; + break; + } + + default: + assert(0); + } + } + + *buffer_ptr = buffer; + return 0; + + error: + uv__stdio_destroy(buffer); + return -1; +} + + +void uv__stdio_destroy(BYTE* buffer) { + int i, count; + + count = CHILD_STDIO_COUNT(buffer); + for (i = 0; i < count; i++) { + HANDLE handle = CHILD_STDIO_HANDLE(buffer, i); + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + } + } + + free(buffer); +} + + +void uv__stdio_noinherit(BYTE* buffer) { + int i, count; + + count = CHILD_STDIO_COUNT(buffer); + for (i = 0; i < count; i++) { + HANDLE handle = CHILD_STDIO_HANDLE(buffer, i); + if (handle != INVALID_HANDLE_VALUE) { + SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); + } + } +} + + +WORD uv__stdio_size(BYTE* buffer) { + return (WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer))); +} + + +HANDLE uv__stdio_handle(BYTE* buffer, int fd) { + return CHILD_STDIO_HANDLE(buffer, fd); +} diff --git a/src/win/process.c b/src/win/process.c index 4166de8a..687e2541 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -34,17 +34,6 @@ #define SIGKILL 9 -/* CRT file descriptor mode flags */ -#define FOPEN 0x01 -#define FEOFLAG 0x02 -#define FCRLF 0x04 -#define FPIPE 0x08 -#define FNOINHERIT 0x10 -#define FAPPEND 0x20 -#define FDEV 0x40 -#define FTEXT 0x80 - - typedef struct env_var { const char* narrow; const wchar_t* wide; @@ -69,37 +58,6 @@ typedef struct env_var { } -/* The `child_stdio_buffer` buffer has the following layout: - * int number_of_fds - * unsigned char crt_flags[number_of_fds] - * HANDLE os_handle[number_of_fds] - */ - -#define CHILD_STDIO_SIZE(count) \ - (sizeof(int) + \ - sizeof(unsigned char) * (count) + \ - sizeof(uintptr_t) * (count)) - -#define CHILD_STDIO_COUNT(buffer) \ - *((unsigned int*) (buffer)) - -#define CHILD_STDIO_LPRESERVED2(buffer) \ - ((LPBYTE) (buffer)) - -#define CHILD_STDIO_CBRESERVED2(buffer) \ - ((WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)))) - -#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \ - *((unsigned char*) (buffer) + sizeof(int) + fd) - -#define CHILD_STDIO_HANDLE(buffer, fd) \ - *((HANDLE*) ((unsigned char*) (buffer) + \ - sizeof(int) + \ - sizeof(unsigned char) * \ - CHILD_STDIO_COUNT((buffer)) + \ - sizeof(HANDLE) * (fd))) - - static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) { uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS); handle->exit_cb = NULL; @@ -637,179 +595,6 @@ wchar_t* make_program_env(char** env_block) { } -static int uv_create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe, - HANDLE* child_pipe_ptr, unsigned int flags) { - char pipe_name[64]; - SECURITY_ATTRIBUTES sa; - DWORD server_access = 0; - DWORD client_access = 0; - HANDLE child_pipe = INVALID_HANDLE_VALUE; - - if (flags & UV_READABLE_PIPE) { - server_access |= PIPE_ACCESS_OUTBOUND; - client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; - } - if (flags & UV_WRITABLE_PIPE) { - server_access |= PIPE_ACCESS_INBOUND; - client_access |= GENERIC_WRITE; - } - - /* Create server pipe handle. */ - if (uv_stdio_pipe_server(loop, - server_pipe, - server_access, - pipe_name, - sizeof(pipe_name)) < 0) { - goto error; - } - - /* Create child pipe handle. */ - sa.nLength = sizeof sa; - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - - child_pipe = CreateFileA(pipe_name, - client_access, - 0, - &sa, - OPEN_EXISTING, - server_pipe->ipc ? FILE_FLAG_OVERLAPPED : 0, - NULL); - if (child_pipe == INVALID_HANDLE_VALUE) { - uv__set_sys_error(loop, GetLastError()); - goto error; - } - -#ifndef NDEBUG - /* Validate that the pipe was opened in the right mode. */ - { - DWORD mode; - BOOL r = GetNamedPipeHandleState(child_pipe, - &mode, - NULL, - NULL, - NULL, - NULL, - 0); - assert(r == TRUE); - assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); - } -#endif - - /* Do a blocking ConnectNamedPipe. This should not block because we have */ - /* both ends of the pipe created. */ - if (!ConnectNamedPipe(server_pipe->handle, NULL)) { - if (GetLastError() != ERROR_PIPE_CONNECTED) { - uv__set_sys_error(loop, GetLastError()); - goto error; - } - } - - *child_pipe_ptr = child_pipe; - return 0; - - error: - if (server_pipe->handle != INVALID_HANDLE_VALUE) { - uv_pipe_cleanup(loop, server_pipe); - } - - if (child_pipe != INVALID_HANDLE_VALUE) { - CloseHandle(child_pipe); - } - - return -1; -} - - -static int duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { - HANDLE current_process; - - current_process = GetCurrentProcess(); - - if (!DuplicateHandle(current_process, - handle, - current_process, - dup, - 0, - TRUE, - DUPLICATE_SAME_ACCESS)) { - *dup = INVALID_HANDLE_VALUE; - uv__set_sys_error(loop, GetLastError()); - return -1; - } - - return 0; -} - - -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); -} - - -static int create_nul_handle(uv_loop_t* loop, HANDLE* handle_ptr, - DWORD access) { - HANDLE handle; - SECURITY_ATTRIBUTES sa; - - sa.nLength = sizeof sa; - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - - handle = CreateFileW(L"NUL", - access, - FILE_SHARE_READ | FILE_SHARE_WRITE, - &sa, - OPEN_EXISTING, - 0, - NULL); - if (handle == INVALID_HANDLE_VALUE) { - uv__set_sys_error(loop, GetLastError()); - return -1; - } - - *handle_ptr = handle; - return 0; -} - - -static void set_child_stdio_noinherit(void* buffer) { - int i, count; - - count = CHILD_STDIO_COUNT(buffer); - for (i = 0; i < count; i++) { - HANDLE handle = CHILD_STDIO_HANDLE(buffer, i); - if (handle != INVALID_HANDLE_VALUE) { - SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); - } - } -} - - -static void close_and_free_child_stdio(void* buffer) { - int i, count; - - count = CHILD_STDIO_COUNT(buffer); - for (i = 0; i < count; i++) { - HANDLE handle = CHILD_STDIO_HANDLE(buffer, i); - if (handle != INVALID_HANDLE_VALUE) { - CloseHandle(handle); - } - } - - free(buffer); -} - - /* * Called on Windows thread-pool thread to indicate that * a child process has exited. @@ -852,7 +637,7 @@ static DWORD WINAPI spawn_failure(void* data) { char unknown[] = "unknown error\n"; uv_process_t* process = (uv_process_t*) data; uv_loop_t* loop = process->loop; - HANDLE child_stderr = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 2); + HANDLE child_stderr = uv__stdio_handle(process->child_stdio_buffer, 2); char* buf = NULL; DWORD count, written; @@ -952,7 +737,7 @@ void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) { /* Clean up the child stdio ends that may have been left open. */ if (handle->child_stdio_buffer != NULL) { - close_and_free_child_stdio(handle->child_stdio_buffer); + uv__stdio_destroy(handle->child_stdio_buffer); } uv__handle_close(handle); @@ -960,184 +745,6 @@ void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) { } -static int init_child_stdio(uv_loop_t* loop, uv_process_options_t* options, - void** buffer_ptr) { - void* buffer; - int count, i; - - count = options->stdio_count; - - if (count < 0 || count > 255) { - /* Only support FDs 0-255 */ - uv__set_artificial_error(loop, UV_ENOTSUP); - return -1; - } else if (count < 3) { - /* There should always be at least 3 stdio handles. */ - count = 3; - } - - /* Allocate the child stdio buffer */ - buffer = malloc(CHILD_STDIO_SIZE(count)); - if (buffer == NULL) { - uv__set_artificial_error(loop, UV_ENOMEM); - return -1; - } - - /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */ - /* clean up on failure. */ - CHILD_STDIO_COUNT(buffer) = count; - for (i = 0; i < count; i++) { - CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; - CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; - } - - for (i = 0; i < count; i++) { - uv_stdio_container_t fdopt; - if (i < options->stdio_count) { - fdopt = options->stdio[i]; - } else { - fdopt.flags = UV_IGNORE; - } - - switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | - UV_INHERIT_STREAM)) { - case UV_IGNORE: - /* Starting a process with no stdin/stout/stderr can confuse it. */ - /* So no matter what the user specified, we make sure the first */ - /* three FDs are always open in their typical modes, e.g. stdin */ - /* be readable and stdout/err should be writable. For FDs > 2, don't */ - /* do anything - all handles in the stdio buffer are initialized with */ - /* INVALID_HANDLE_VALUE, which should be okay. */ - if (i <= 2) { - DWORD access = (i == 0) ? FILE_GENERIC_READ : - FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES; - if (create_nul_handle(loop, - &CHILD_STDIO_HANDLE(buffer, i), - access) < 0) { - goto error; - } - CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; - } - break; - - case UV_CREATE_PIPE: { - /* Create a pair of two connected pipe ends; one end is turned into */ - /* an uv_pipe_t for use by the parent. The other one is given to */ - /* the child. */ - uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream; - HANDLE child_pipe; - - /* Create a new, connected pipe pair. stdio[i].stream should point */ - /* to an uninitialized, but not connected pipe handle. */ - assert(fdopt.data.stream->type == UV_NAMED_PIPE); - assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION)); - assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER)); - - if (uv_create_stdio_pipe_pair(loop, - parent_pipe, - &child_pipe, - fdopt.flags) < 0) { - goto error; - } - - CHILD_STDIO_HANDLE(buffer, i) = child_pipe; - CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; - break; - } - - case UV_INHERIT_FD: { - /* Inherit a raw FD. */ - HANDLE child_handle; - - /* Make an inheritable duplicate of the handle. */ - if (duplicate_fd(loop, fdopt.data.fd, &child_handle) < 0) { - goto error; - } - - /* Figure out what the type is. */ - switch (GetFileType(child_handle)) { - case FILE_TYPE_DISK: - CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN; - break; - - case FILE_TYPE_PIPE: - CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; - - case FILE_TYPE_CHAR: - case FILE_TYPE_REMOTE: - CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; - break; - - case FILE_TYPE_UNKNOWN: - if (GetLastError != 0) { - uv__set_sys_error(loop, GetLastError()); - CloseHandle(child_handle); - goto error; - } - CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; - break; - - default: - assert(0); - } - - CHILD_STDIO_HANDLE(buffer, i) = child_handle; - break; - } - - case UV_INHERIT_STREAM: { - /* Use an existing stream as the stdio handle for the child. */ - HANDLE stream_handle, child_handle; - unsigned char crt_flags; - uv_stream_t* stream = fdopt.data.stream; - - /* Leech the handle out of the stream. */ - if (stream->type == UV_TTY) { - stream_handle = ((uv_tty_t*) stream)->handle; - crt_flags = FOPEN | FDEV; - } else if (stream->type == UV_NAMED_PIPE && - stream->flags & UV_HANDLE_CONNECTED) { - stream_handle = ((uv_pipe_t*) stream)->handle; - crt_flags = FOPEN | FPIPE; - } else { - stream_handle = INVALID_HANDLE_VALUE; - crt_flags = 0; - } - - if (stream_handle == NULL || - stream_handle == INVALID_HANDLE_VALUE) { - /* The handle is already closed, or not yet created, or the */ - /* stream type is not supported. */ - uv__set_artificial_error(loop, UV_ENOTSUP); - goto error; - } - - /* Make an inheritable copy of the handle. */ - if (duplicate_handle(loop, - stream_handle, - &child_handle) < 0) { - goto error; - } - - CHILD_STDIO_HANDLE(buffer, i) = child_handle; - CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags; - break; - } - - default: - assert(0); - } - } - - *buffer_ptr = buffer; - return 0; - - error: - close_and_free_child_stdio(buffer); - return -1; -} - - int uv_spawn(uv_loop_t* loop, uv_process_t* process, uv_process_options_t options) { int size, err = 0, keep_child_stdio_open = 0; @@ -1208,7 +815,7 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, } - if (init_child_stdio(loop, &options, &process->child_stdio_buffer) < 0) { + if (uv__stdio_create(loop, &options, &process->child_stdio_buffer) < 0) { err = -1; goto done; } @@ -1218,11 +825,11 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, startup.lpDesktop = NULL; startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES; - startup.cbReserved2 = CHILD_STDIO_CBRESERVED2(process->child_stdio_buffer); - startup.lpReserved2 = CHILD_STDIO_LPRESERVED2(process->child_stdio_buffer); - startup.hStdInput = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 0); - startup.hStdOutput = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 1); - startup.hStdError = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 2); + startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); + startup.lpReserved2 = (BYTE*) process->child_stdio_buffer; + startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); + startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); + startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); process_flags = CREATE_UNICODE_ENVIRONMENT; if (options.flags & UV_PROCESS_DETACHED) { @@ -1289,12 +896,12 @@ done: if (process->child_stdio_buffer == NULL) { /* Something went wrong before child stdio was initialized. */ } else if (!keep_child_stdio_open) { - close_and_free_child_stdio(process->child_stdio_buffer); + uv__stdio_destroy(process->child_stdio_buffer); process->child_stdio_buffer = NULL; } else { /* We're keeping the handles open, the thread pool is going to have */ /* it's way with them. But at least make them non-inheritable. */ - set_child_stdio_noinherit(process->child_stdio_buffer); + uv__stdio_noinherit(process->child_stdio_buffer); } if (err == 0) { diff --git a/uv.gyp b/uv.gyp index 5409389c..0a519b79 100644 --- a/uv.gyp +++ b/uv.gyp @@ -149,6 +149,7 @@ 'src/win/thread.c', 'src/win/poll.c', 'src/win/process.c', + 'src/win/process-stdio.c', 'src/win/req.c', 'src/win/req-inl.h', 'src/win/stream.c',