diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 259984944c..fab1e3b8bf 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -165,6 +165,8 @@ typedef struct uv_buf_t { typedef int uv_file; +typedef HANDLE uv_os_handle_t; + typedef SOCKET uv_os_sock_t; typedef HANDLE uv_thread_t; @@ -351,7 +353,10 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); #define uv_pipe_connection_fields \ uv_timer_t* eof_timer; \ uv_write_t ipc_header_write_req; \ - int ipc_pid; \ + union { \ + int pid; \ + int *p_pid; \ + } ipc_pid; \ uint64_t remaining_ipc_rawdata_bytes; \ struct { \ WSAPROTOCOL_INFOW* socket_info; \ diff --git a/include/uv.h b/include/uv.h index e52ae64313..40ee4ba8a1 100644 --- a/include/uv.h +++ b/include/uv.h @@ -904,20 +904,41 @@ UV_EXTERN uv_handle_type uv_guess_handle(uv_file file); * uv_pipe_t is a subclass of uv_stream_t * * Representing a pipe stream or pipe server. On Windows this is a Named - * Pipe. On Unix this is a UNIX domain socket. + * Pipe. On Unix this is a UNIX domain sockets + * + * A single uv_pipe_t always represents one end of a pipe. You can use + * uv_pipe_link to create a pair of connected pipe ends. */ struct uv_pipe_s { UV_HANDLE_FIELDS UV_STREAM_FIELDS UV_PIPE_PRIVATE_FIELDS - int ipc; /* non-zero if this pipe is used for passing handles */ +}; + +enum uv_pipe_flags { + UV_PIPE_IPC = 0x01, + UV_PIPE_SPAWN_SAFE = 0x02, + UV_PIPE_READABLE = 0x04, + UV_PIPE_WRITEABLE = 0x08, }; /* * Initialize a pipe. The last argument is a boolean to indicate if * this pipe will be used for handle passing between processes. */ -UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc); +UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int flags); + +/* + * Creates a pipe and assigns the two pipe ends to the given uv_pipe_t's + */ +UV_EXTERN int uv_pipe_link(uv_pipe_t *read, uv_pipe_t *write); + +/* + * Attempt to synchronously close the given pipe. This will only work if the pipe is + * inactive (i.e. not reading, writing listening, connecting, etc. Otherwise this function + * will abort() + */ +UV_EXTERN void uv_pipe_close_sync(uv_pipe_t *pipe); /* * Opens an existing file descriptor or HANDLE as a pipe. @@ -927,7 +948,7 @@ UV_EXTERN void uv_pipe_open(uv_pipe_t*, uv_file file); UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name); UV_EXTERN void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, - const char* name, uv_connect_cb cb); + const char* name, uv_connect_cb cb); /* * This setting applies to Windows only. @@ -1163,30 +1184,22 @@ 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_INHERIT_FD = 0x02, - UV_INHERIT_STREAM = 0x04, - - /* When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE - * determine the direction of flow, from the child process' perspective. Both - * flags may be specified to create a duplex data stream. - */ - UV_READABLE_PIPE = 0x10, - UV_WRITABLE_PIPE = 0x20 -} uv_stdio_flags; + UV_STREAM = 0x00, //uv_stream_t* + UV_RAW_FD = 0x01, //fd for both unix and windows + UV_RAW_HANDLE = 0x02, //HANDLE on windows, same as UV_RAW_FD on unix +} uv_stdio_type; typedef struct uv_stdio_container_s { - uv_stdio_flags flags; - - union { - uv_stream_t* stream; - int fd; - } data; + uv_stdio_type type; + union { + uv_stream_t *stream; + uv_file fd; + uv_os_handle_t os_handle; + } data; } uv_stdio_container_t; +/* uv_spawn() options */ typedef struct uv_process_options_s { uv_exit_cb exit_cb; /* Called after the process exits. */ const char* file; /* Path to program to execute. */ @@ -1221,16 +1234,16 @@ typedef struct uv_process_options_s { uv_gid_t gid; /* - * The `stdio` field points to an array of uv_stdio_container_t structs that + * The `stdio` field points to an array of uv_pipe_t structs that * describe the file descriptors that will be made available to the child * process. The convention is that stdio[0] points to stdin, fd 1 is used for * stdout, and fd 2 is stderr. * * Note that on windows file descriptors greater than 2 are available to the - * child process only if the child processes uses the MSVCRT runtime. + * child process only if the child processes uses the MSVCRT. */ int stdio_count; - uv_stdio_container_t* stdio; + uv_stdio_container_t *stdio; } uv_process_options_t; /* diff --git a/src/win/internal.h b/src/win/internal.h index f5d0d17212..5367f7621e 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -70,8 +70,14 @@ #define UV_HANDLE_SHARED_TCP_SOCKET 0x40000000 /* Only used by uv_pipe_t handles. */ -#define UV_HANDLE_NON_OVERLAPPED_PIPE 0x01000000 -#define UV_HANDLE_PIPESERVER 0x02000000 +#define UV_HANDLE_PIPE_IPC 0x01000000 +#define UV_HANDLE_PIPE_SPAWN_SAFE 0x02000000 +#define UV_HANDLE_PIPE_READABLE 0x04000000 +#define UV_HANDLE_PIPE_WRITEABLE 0x08000000 +#define UV_HANDLE_NON_OVERLAPPED_PIPE 0x10000000 +#define UV_HANDLE_PIPESERVER 0x20000000 +#define UV_HANDLE_PIPE_IPC_CLIENT 0x40000000 + /* Only used by uv_tty_t handles. */ #define UV_HANDLE_TTY_RAW 0x01000000 diff --git a/src/win/pipe.c b/src/win/pipe.c index 0551047123..b653b86e18 100644 --- a/src/win/pipe.c +++ b/src/win/pipe.c @@ -73,20 +73,24 @@ static void uv_unique_pipe_name(char* ptr, char* name, size_t size) { } -int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { +int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int flags) { uv_stream_init(loop, (uv_stream_t*)handle); handle->type = UV_NAMED_PIPE; handle->reqs_pending = 0; handle->handle = INVALID_HANDLE_VALUE; handle->name = NULL; - handle->ipc_pid = 0; + handle->ipc_pid.pid = 0; handle->remaining_ipc_rawdata_bytes = 0; handle->pending_ipc_info.socket_info = NULL; handle->pending_ipc_info.tcp_connection = 0; - handle->ipc = ipc; handle->non_overlapped_writes_tail = NULL; + handle->flags |= ((flags&UV_PIPE_IPC)?UV_HANDLE_PIPE_IPC:0)| + ((flags&UV_PIPE_SPAWN_SAFE)?UV_HANDLE_PIPE_SPAWN_SAFE:0)| + ((flags&UV_PIPE_READABLE)?UV_HANDLE_PIPE_READABLE:0)| + ((flags&UV_PIPE_WRITEABLE)?UV_HANDLE_PIPE_WRITEABLE:0); + uv_req_init(loop, (uv_req_t*) &handle->ipc_header_write_req); loop->counters.pipe_init++; @@ -94,7 +98,6 @@ int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { return 0; } - static void uv_pipe_connection_init(uv_pipe_t* handle) { uv_connection_init((uv_stream_t*) handle); handle->read_req.data = handle; @@ -212,6 +215,147 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, return err; } +static int uv_create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* read, + uv_pipe_t* write) { + char pipe_name[64]; + SECURITY_ATTRIBUTES sa; + DWORD server_access = 0; + DWORD client_access = 0; + uv_pipe_t *server_pipe, *client_pipe; + + if (read->flags & UV_HANDLE_PIPE_SPAWN_SAFE || write->flags & UV_HANDLE_PIPE_IPC) { + server_access |= PIPE_ACCESS_OUTBOUND | + ((write->flags&UV_HANDLE_PIPE_READABLE)?PIPE_ACCESS_INBOUND:0); + client_access |= GENERIC_READ | + ((read->flags&UV_HANDLE_PIPE_WRITEABLE)?GENERIC_WRITE:FILE_WRITE_ATTRIBUTES); + server_pipe=write; + client_pipe=read; + } + else { + server_access |= PIPE_ACCESS_INBOUND; + if(read->flags&UV_HANDLE_PIPE_WRITEABLE) + server_access |= PIPE_ACCESS_OUTBOUND; + client_access |= GENERIC_WRITE; + if(write->flags&UV_HANDLE_PIPE_READABLE) + client_access|=GENERIC_READ; + server_pipe=read; + client_pipe=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; + + client_pipe->handle = CreateFileA(pipe_name, + client_access, + 0, + &sa, + OPEN_EXISTING, + (client_pipe->flags&UV_HANDLE_PIPE_SPAWN_SAFE) ? 0 : FILE_FLAG_OVERLAPPED, + NULL); + if (client_pipe->handle == 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(client_pipe->handle, + &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; + } + } + + if(client_pipe->flags&UV_HANDLE_PIPE_SPAWN_SAFE) + { + client_pipe->flags|=UV_HANDLE_NON_OVERLAPPED_PIPE; + } + uv_pipe_connection_init(client_pipe); + + return 0; + + error: + if (server_pipe->handle != INVALID_HANDLE_VALUE) { + uv_pipe_cleanup(loop, server_pipe); + } + + if (client_pipe->handle != INVALID_HANDLE_VALUE) { + uv_pipe_cleanup(loop, client_pipe); + } + + return -1; +} + +int uv_pipe_link(uv_pipe_t* read, uv_pipe_t* write) +{ + int err; + assert(read->loop==write->loop); + assert(read->flags&UV_HANDLE_PIPE_READABLE); + assert(write->flags&UV_HANDLE_PIPE_WRITEABLE); + assert(!(write->flags&read->flags&UV_HANDLE_PIPE_IPC)); + if((read->flags & UV_HANDLE_PIPE_SPAWN_SAFE) + && (write->flags & UV_HANDLE_PIPE_SPAWN_SAFE)) + { + if (!CreatePipe(&(read->handle),&(read->handle),NULL,65536)) { + uv__set_sys_error(read->loop, GetLastError()); + err = -1; + goto done; + } + read->flags|=UV_HANDLE_NON_OVERLAPPED_PIPE; + write->flags|=UV_HANDLE_NON_OVERLAPPED_PIPE; + uv_pipe_connection_init(read); + uv_pipe_connection_init(write); + err = 0; + } else { + err = uv_create_stdio_pipe_pair(read->loop,read,write); + if(read->flags&UV_HANDLE_PIPE_IPC) { + write->flags|=UV_HANDLE_PIPE_IPC_CLIENT; + write->ipc_pid.p_pid=&(read->ipc_pid.pid); + } else if(write->flags&UV_HANDLE_PIPE_IPC) { + read->flags|=UV_HANDLE_PIPE_IPC_CLIENT; + read->ipc_pid.p_pid=&(write->ipc_pid.pid); + } + } +done: + return err; +} + +void uv_pipe_close_sync(uv_pipe_t *pipe) +{ + assert(!(pipe->flags&(UV_HANDLE_LISTENING|UV_HANDLE_BOUND| + UV_HANDLE_READING))&&pipe->reqs_pending==0); + pipe->close_cb=NULL; + uv_pipe_cleanup(pipe->loop,pipe); + uv_pipe_endgame(pipe->loop,pipe); +} static int uv_set_pipe_handle(uv_loop_t* loop, uv_pipe_t* handle, HANDLE pipeHandle, DWORD duplex_flags) { @@ -726,7 +870,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { uv_pipe_t* pipe_client; uv_pipe_accept_t* req; - if (server->ipc) { + if (server->flags&UV_HANDLE_PIPE_IPC) { if (!server->pending_ipc_info.socket_info) { /* No valid pending sockets. */ uv__set_sys_error(loop, WSAEWOULDBLOCK); @@ -1096,7 +1240,7 @@ static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req, req->wait_handle = INVALID_HANDLE_VALUE; memset(&req->overlapped, 0, sizeof(req->overlapped)); - if (handle->ipc) { + if (handle->flags&UV_HANDLE_PIPE_IPC) { assert(!(handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); ipc_frame.header.flags = 0; @@ -1104,7 +1248,7 @@ static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req, if (send_handle) { tcp_send_handle = (uv_tcp_t*)send_handle; - if (uv_tcp_duplicate_socket(tcp_send_handle, handle->ipc_pid, + if (uv_tcp_duplicate_socket(tcp_send_handle, handle->ipc_pid.pid, &ipc_frame.socket_info)) { return -1; } @@ -1243,7 +1387,7 @@ int uv_pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, int uv_pipe_write2(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, uv_buf_t bufs[], int bufcnt, uv_stream_t* send_handle, uv_write_cb cb) { - if (!handle->ipc) { + if (!(handle->flags&UV_HANDLE_PIPE_IPC)) { uv__set_artificial_error(loop, UV_EINVAL); return -1; } @@ -1334,7 +1478,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, break; } - if (handle->ipc) { + if (handle->flags&UV_HANDLE_PIPE_IPC) { /* Use the IPC framing protocol to read the incoming data. */ if (handle->remaining_ipc_rawdata_bytes == 0) { /* We're reading a new frame. First, read the header. */ @@ -1403,7 +1547,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, &bytes, NULL)) { /* Successful read */ - if (handle->ipc) { + if (handle->flags&UV_HANDLE_PIPE_IPC) { assert(handle->remaining_ipc_rawdata_bytes >= bytes); handle->remaining_ipc_rawdata_bytes = handle->remaining_ipc_rawdata_bytes - bytes; @@ -1668,9 +1812,9 @@ void uv_pipe_open(uv_pipe_t* pipe, uv_file file) { uv_pipe_connection_init(pipe); pipe->handle = os_handle; - if (pipe->ipc) { + if (pipe->flags&UV_HANDLE_PIPE_IPC) { assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); - pipe->ipc_pid = uv_parent_pid(); - assert(pipe->ipc_pid != -1); + pipe->ipc_pid.pid = uv_parent_pid(); + assert(pipe->ipc_pid.pid != -1); } } diff --git a/src/win/process.c b/src/win/process.c index 332ef0f58a..7fff9a4f9c 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -637,91 +637,6 @@ wchar_t* make_program_env(char** env_block) { return dst; } - -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; @@ -777,7 +692,7 @@ static int create_nul_handle(uv_loop_t* loop, HANDLE* handle_ptr, uv__set_sys_error(loop, GetLastError()); return -1; } - + *handle_ptr = handle; return 0; } @@ -993,147 +908,122 @@ static int init_child_stdio(uv_loop_t* loop, uv_process_options_t* options, } 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); - } + uv_stream_t *stream; + HANDLE stream_handle, child_handle; + int crt_flags; + + if (i < options->stdio_count) { + if(options->stdio[i].type != UV_STREAM) { + /* Inherit a raw FD. */ + HANDLE child_handle; + + if(options->stdio[i].type == UV_RAW_FD) { + /* Make an inheritable duplicate of the handle. */ + if (duplicate_fd(loop, options->stdio[i].data.fd, &child_handle) < 0) { + goto error; + } + } else if(options->stdio[i].type == UV_RAW_HANDLE) { + if (duplicate_fd(loop, options->stdio[i].data.fd, &child_handle) < 0) { + goto error; + } + } else { + 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; + goto done; + } + + stream = options->stdio[i].data.stream; + } else { + stream = NULL; + } + + if(stream == NULL) { + /* 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; + } + continue; + } + + switch (stream->type) { + case UV_TTY: + stream_handle = ((uv_tty_t*) stream)->handle; + crt_flags = FOPEN | FDEV; + break; + case UV_NAMED_PIPE: + stream_handle = ((uv_pipe_t*) stream)->handle; + crt_flags = FOPEN | FPIPE; + break; + default: + 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; } +done: *buffer_ptr = buffer; return 0; - error: +error: close_and_free_child_stdio(buffer); return -1; } @@ -1245,10 +1135,11 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, process->pid = info.dwProcessId; if (options.stdio_count > 0 && - options.stdio[0].flags & UV_CREATE_PIPE && + options.stdio[0].type == UV_STREAM && + options.stdio[0].data.stream != NULL && 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; + options.stdio[0].data.stream->flags & UV_HANDLE_PIPE_IPC_CLIENT) { + *(((uv_pipe_t*)options.stdio[0].data.stream)->ipc_pid.p_pid) = info.dwProcessId; } /* Setup notifications for when the child process exits. */ diff --git a/test/benchmark-spawn.c b/test/benchmark-spawn.c index 74cc3d238f..143c57aabf 100644 --- a/test/benchmark-spawn.c +++ b/test/benchmark-spawn.c @@ -102,6 +102,7 @@ void on_read(uv_stream_t* pipe, ssize_t nread, uv_buf_t buf) { static void spawn() { uv_stdio_container_t stdio[2]; + uv_pipe_t child_stdout; int r; ASSERT(process_open == 0); @@ -114,17 +115,22 @@ static void spawn() { options.args = args; options.exit_cb = exit_cb; - uv_pipe_init(loop, &out, 0); + uv_pipe_init(loop, &out, UV_PIPE_READABLE); + uv_pipe_init(loop, &child_stdout, UV_PIPE_SPAWN_SAFE|UV_PIPE_WRITEABLE); + uv_pipe_link(&out,&child_stdout); 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; + options.stdio[0].type = UV_STREAM; + options.stdio[0].data.stream = NULL; + options.stdio[1].type = UV_STREAM; + options.stdio[1].data.stream = (uv_stream_t*)&child_stdout; r = uv_spawn(loop, &process, options); ASSERT(r == 0); + uv_pipe_close_sync(&child_stdout); + process_open = 1; pipe_open = 1; output_used = 0; diff --git a/test/test-ipc.c b/test/test-ipc.c index 61add0b4b8..a0bb0c3472 100644 --- a/test/test-ipc.c +++ b/test/test-ipc.c @@ -201,10 +201,16 @@ void spawn_helper(uv_pipe_t* channel, char* args[3]; int r; uv_stdio_container_t stdio[1]; + uv_pipe_t child_channel; - r = uv_pipe_init(uv_default_loop(), channel, 1); + r = uv_pipe_init(uv_default_loop(), channel, UV_PIPE_IPC| + UV_PIPE_READABLE|UV_PIPE_WRITEABLE); ASSERT(r == 0); - ASSERT(channel->ipc); + r = uv_pipe_init(uv_default_loop(), &child_channel, + UV_PIPE_READABLE|UV_PIPE_WRITEABLE); + ASSERT(r == 0); + + uv_pipe_link(channel,&child_channel); exepath_size = sizeof(exepath); r = uv_exepath(exepath, &exepath_size); @@ -221,13 +227,14 @@ void spawn_helper(uv_pipe_t* channel, options.exit_cb = exit_cb; 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[0].type = UV_STREAM; + options.stdio[0].data.stream = (uv_stream_t*)&child_channel; options.stdio_count = 1; r = uv_spawn(uv_default_loop(), process, options); ASSERT(r == 0); + + uv_pipe_close_sync(&child_channel); } diff --git a/test/test-spawn.c b/test/test-spawn.c index 51ed2e59d7..8db55a0fb4 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -171,21 +171,27 @@ TEST_IMPL(spawn_exit_code) { TEST_IMPL(spawn_stdout) { int r; - uv_pipe_t out; + uv_pipe_t out, child_stdout; uv_stdio_container_t stdio[2]; init_process_options("spawn_helper2", exit_cb); - uv_pipe_init(uv_default_loop(), &out, 0); + uv_pipe_init(uv_default_loop(), &out, UV_PIPE_READABLE); + uv_pipe_init(uv_default_loop(), &child_stdout, UV_PIPE_SPAWN_SAFE|UV_PIPE_WRITEABLE); + uv_pipe_link(&out,&child_stdout); + 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[0].type = UV_STREAM; + options.stdio[0].data.stream = NULL; + options.stdio[1].type = UV_STREAM; + options.stdio[1].data.stream = (uv_stream_t*)&child_stdout; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); + uv_pipe_close_sync(&child_stdout); + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); @@ -220,8 +226,9 @@ TEST_IMPL(spawn_stdout_to_file) { file = r; options.stdio = stdio; - options.stdio[0].flags = UV_IGNORE; - options.stdio[1].flags = UV_INHERIT_FD; + options.stdio[0].type = UV_STREAM; + options.stdio[0].data.stream = NULL; + options.stdio[1].type = UV_RAW_FD; options.stdio[1].data.fd = file; options.stdio_count = 2; @@ -260,22 +267,31 @@ TEST_IMPL(spawn_stdin) { uv_write_t write_req; uv_buf_t buf; uv_stdio_container_t stdio[2]; + uv_pipe_t child_pipes[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); + uv_pipe_init(uv_default_loop(), &out, UV_PIPE_READABLE); + uv_pipe_init(uv_default_loop(), &in, UV_PIPE_WRITEABLE); + uv_pipe_init(uv_default_loop(), &child_pipes[0], UV_PIPE_READABLE|UV_PIPE_SPAWN_SAFE); + uv_pipe_init(uv_default_loop(), &child_pipes[1], UV_PIPE_WRITEABLE|UV_PIPE_SPAWN_SAFE); + uv_pipe_link(&child_pipes[0],&in); + uv_pipe_link(&out,&child_pipes[1]); + 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[0].type = UV_STREAM; + options.stdio[0].data.stream = (uv_stream_t*)&child_pipes[0]; + options.stdio[1].type = UV_STREAM; + options.stdio[1].data.stream = (uv_stream_t*)&child_pipes[1]; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); + uv_pipe_close_sync(&child_pipes[0]); + uv_pipe_close_sync(&child_pipes[1]); + buf.base = buffer; buf.len = sizeof(buffer); r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); @@ -297,22 +313,30 @@ TEST_IMPL(spawn_stdin) { TEST_IMPL(spawn_stdio_greater_than_3) { int r; - uv_pipe_t pipe; + uv_pipe_t pipe, child_pipe; uv_stdio_container_t stdio[4]; + uv_stdio_container_t ignore; + + ignore.type=UV_STREAM; + ignore.data.stream = NULL; init_process_options("spawn_helper5", exit_cb); - uv_pipe_init(uv_default_loop(), &pipe, 0); + uv_pipe_init(uv_default_loop(), &pipe, UV_PIPE_READABLE); + uv_pipe_init(uv_default_loop(), &child_pipe, UV_PIPE_WRITEABLE|UV_PIPE_SPAWN_SAFE); + uv_pipe_link(&pipe,&child_pipe); options.stdio = stdio; - options.stdio[0].flags = UV_IGNORE; - options.stdio[1].flags = UV_IGNORE; - options.stdio[2].flags = UV_IGNORE; - options.stdio[3].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[3].data.stream = (uv_stream_t*)&pipe; + options.stdio[0] = ignore; + options.stdio[1] = ignore; + options.stdio[2] = ignore; + options.stdio[3].type = UV_STREAM; + options.stdio[3].data.stream = (uv_stream_t*)&child_pipe; options.stdio_count = 4; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); + + uv_pipe_close_sync(&child_pipe); r = uv_read_start((uv_stream_t*) &pipe, on_alloc, on_read); ASSERT(r == 0); @@ -403,6 +427,7 @@ TEST_IMPL(spawn_detached) { TEST_IMPL(spawn_and_kill_with_std) { int r; uv_pipe_t in, out, err; + uv_pipe_t child_pipes[3]; uv_write_t write; char message[] = "Nancy's joining me because the message this evening is " "not my message but ours."; @@ -411,27 +436,49 @@ TEST_IMPL(spawn_and_kill_with_std) { init_process_options("spawn_helper4", kill_cb); - r = uv_pipe_init(uv_default_loop(), &in, 0); + r = uv_pipe_init(uv_default_loop(), &in, UV_PIPE_WRITEABLE); ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &child_pipes[0], UV_PIPE_READABLE| + UV_PIPE_SPAWN_SAFE); - r = uv_pipe_init(uv_default_loop(), &out, 0); + r = uv_pipe_init(uv_default_loop(), &out, UV_PIPE_READABLE); ASSERT(r == 0); - r = uv_pipe_init(uv_default_loop(), &err, 0); + r = uv_pipe_init(uv_default_loop(), &child_pipes[1], UV_PIPE_WRITEABLE| + UV_PIPE_SPAWN_SAFE); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &err, UV_PIPE_READABLE); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &child_pipes[2], UV_PIPE_WRITEABLE| + UV_PIPE_SPAWN_SAFE); + ASSERT(r == 0); + + r= uv_pipe_link(&child_pipes[0],&in); + ASSERT(r==0); + r= uv_pipe_link(&out,&child_pipes[1]); + ASSERT(r==0); + r= uv_pipe_link(&err,&child_pipes[2]); ASSERT(r == 0); 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[0].type = UV_STREAM; + options.stdio[0].data.stream = (uv_stream_t*)&child_pipes[0]; + options.stdio[1].type = UV_STREAM; + options.stdio[1].data.stream = (uv_stream_t*)&child_pipes[1]; + options.stdio[2].type = UV_STREAM; + options.stdio[2].data.stream = (uv_stream_t*)&child_pipes[2]; options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); + uv_pipe_close_sync(&child_pipes[0]); + uv_pipe_close_sync(&child_pipes[1]); + uv_pipe_close_sync(&child_pipes[2]); + buf = uv_buf_init(message, sizeof message); r = uv_write(&write, (uv_stream_t*) &in, &buf, 1, write_cb); ASSERT(r == 0); @@ -463,23 +510,31 @@ TEST_IMPL(spawn_and_ping) { uv_pipe_t in, out; uv_buf_t buf; uv_stdio_container_t stdio[2]; + uv_pipe_t child_pipes[2]; int r; init_process_options("spawn_helper3", exit_cb); buf = uv_buf_init("TEST", 4); - uv_pipe_init(uv_default_loop(), &out, 0); - uv_pipe_init(uv_default_loop(), &in, 0); + uv_pipe_init(uv_default_loop(), &out, UV_PIPE_READABLE); + uv_pipe_init(uv_default_loop(), &child_pipes[0], UV_PIPE_READABLE|UV_PIPE_SPAWN_SAFE); + uv_pipe_init(uv_default_loop(), &in, UV_PIPE_WRITEABLE); + uv_pipe_init(uv_default_loop(), &child_pipes[1], UV_PIPE_WRITEABLE|UV_PIPE_SPAWN_SAFE); + uv_pipe_link(&child_pipes[0],&in); + uv_pipe_link(&out,&child_pipes[1]); 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[0].type = UV_STREAM; + options.stdio[0].data.stream = (uv_stream_t*)&child_pipes[0]; + options.stdio[1].type = UV_STREAM; + options.stdio[1].data.stream = (uv_stream_t*)&child_pipes[1]; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); + uv_pipe_close_sync(&child_pipes[0]); + uv_pipe_close_sync(&child_pipes[1]); + /* Sending signum == 0 should check if the * child process is still alive, not kill it. */ @@ -540,19 +595,15 @@ TEST_IMPL(kill) { #ifdef _WIN32 TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { int r; - uv_pipe_t out; + uv_pipe_t out,child_stdout; 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.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; + uv_pipe_init(uv_default_loop(), &out, UV_PIPE_READABLE); + uv_pipe_init(uv_default_loop(), &child_stdout, UV_PIPE_WRITEABLE|UV_PIPE_SPAWN_SAFE); /* Create a pipe that'll cause a collision. */ _snprintf(name, sizeof(name), "\\\\.\\pipe\\uv\\%p-%d", &out, GetCurrentProcessId()); @@ -566,9 +617,19 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { NULL); ASSERT(pipe_handle != INVALID_HANDLE_VALUE); + uv_pipe_link(&out, &child_stdout); + options.stdio = stdio; + options.stdio[0].type = UV_STREAM; + options.stdio[0].data.stream = NULL; + options.stdio[1].type = UV_STREAM; + options.stdio[1].data.stream = (uv_stream_t*)&child_stdout; + options.stdio_count = 2; + r = uv_spawn(uv_default_loop(), &process, options); ASSERT(r == 0); + uv_pipe_close_sync(&child_stdout); + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); diff --git a/test/test-stdio-over-pipes.c b/test/test-stdio-over-pipes.c index 7603027fb8..5e8fa291cc 100644 --- a/test/test-stdio-over-pipes.c +++ b/test/test-stdio-over-pipes.c @@ -116,24 +116,33 @@ TEST_IMPL(stdio_over_pipes) { int r; uv_process_t process; uv_stdio_container_t stdio[2]; + uv_pipe_t child_pipes[2]; loop = uv_default_loop(); init_process_options("stdio_over_pipes_helper", exit_cb); - uv_pipe_init(loop, &out, 0); - uv_pipe_init(loop, &in, 0); + uv_pipe_init(loop, &out, UV_PIPE_READABLE); + uv_pipe_init(loop, &in, UV_PIPE_WRITEABLE); + uv_pipe_init(loop, &child_pipes[0], UV_PIPE_SPAWN_SAFE|UV_PIPE_READABLE); + uv_pipe_init(loop, &child_pipes[1], UV_PIPE_SPAWN_SAFE|UV_PIPE_WRITEABLE); + + uv_pipe_link(&child_pipes[0],&in); + uv_pipe_link(&out,&child_pipes[1]); 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[0].type = UV_STREAM; + options.stdio[0].data.stream = (uv_stream_t*)&child_pipes[0]; + options.stdio[1].type = UV_STREAM; + options.stdio[1].data.stream = (uv_stream_t*)&child_pipes[1]; options.stdio_count = 2; r = uv_spawn(loop, &process, options); ASSERT(r == 0); + uv_pipe_close_sync(&child_pipes[0]); + uv_pipe_close_sync(&child_pipes[1]); + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0);