windows: move child stdio buffer ops to a separate file
This commit is contained in:
parent
9f44b0e393
commit
07c6ac2b55
@ -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; \
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
425
src/win/process-stdio.c
Normal file
425
src/win/process-stdio.c
Normal file
@ -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 <assert.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user