| /* 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 |
| |
| |
| /* |
| * Clear the HANDLE_FLAG_INHERIT flag from all HANDLEs that were inherited |
| * the parent process. Don't check for errors - the stdio handles may not be |
| * valid, or may be closed already. There is no guarantee that this function |
| * does a perfect job. |
| */ |
| void uv_disable_stdio_inheritance(void) { |
| HANDLE handle; |
| STARTUPINFOW si; |
| |
| /* Make the windows stdio handles non-inheritable. */ |
| handle = GetStdHandle(STD_INPUT_HANDLE); |
| if (handle != NULL && handle != INVALID_HANDLE_VALUE) |
| SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); |
| |
| handle = GetStdHandle(STD_OUTPUT_HANDLE); |
| if (handle != NULL && handle != INVALID_HANDLE_VALUE) |
| SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); |
| |
| handle = GetStdHandle(STD_ERROR_HANDLE); |
| if (handle != NULL && handle != INVALID_HANDLE_VALUE) |
| SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); |
| |
| /* Make inherited CRT FDs non-inheritable. */ |
| GetStartupInfoW(&si); |
| if (uv__stdio_verify(si.lpReserved2, si.cbReserved2)) |
| uv__stdio_noinherit(si.lpReserved2); |
| } |
| |
| |
| static uv_err_t 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; |
| uv_err_t err; |
| |
| if (flags & UV_READABLE_PIPE) { |
| /* The server needs inbound access too, otherwise CreateNamedPipe() */ |
| /* won't give us the FILE_READ_ATTRIBUTES permission. We need that to */ |
| /* probe the state of the write buffer when we're trying to shutdown */ |
| /* the pipe. */ |
| server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND; |
| client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; |
| } |
| if (flags & UV_WRITABLE_PIPE) { |
| server_access |= PIPE_ACCESS_INBOUND; |
| client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; |
| } |
| |
| /* Create server pipe handle. */ |
| err = uv_stdio_pipe_server(loop, |
| server_pipe, |
| server_access, |
| pipe_name, |
| sizeof(pipe_name)); |
| if (err.code != UV_OK) |
| 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) { |
| err = uv__new_sys_error(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) { |
| err = uv__new_sys_error(GetLastError()); |
| goto error; |
| } |
| } |
| |
| /* The server end is now readable and/or writable. */ |
| if (flags & UV_READABLE_PIPE) |
| server_pipe->flags |= UV_HANDLE_WRITABLE; |
| if (flags & UV_WRITABLE_PIPE) |
| server_pipe->flags |= UV_HANDLE_READABLE; |
| |
| *child_pipe_ptr = child_pipe; |
| return uv_ok_; |
| |
| error: |
| if (server_pipe->handle != INVALID_HANDLE_VALUE) { |
| uv_pipe_cleanup(loop, server_pipe); |
| } |
| |
| if (child_pipe != INVALID_HANDLE_VALUE) { |
| CloseHandle(child_pipe); |
| } |
| |
| return err; |
| } |
| |
| |
| static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { |
| HANDLE current_process; |
| |
| |
| /* _get_osfhandle will sometimes return -2 in case of an error. This seems */ |
| /* to happen when fd <= 2 and the process' corresponding stdio handle is */ |
| /* set to NULL. Unfortunately DuplicateHandle will happily duplicate /* |
| /* (HANDLE) -2, so this situation goes unnoticed until someone tries to */ |
| /* use the duplicate. Therefore we filter out known-invalid handles here. */ |
| if (handle == INVALID_HANDLE_VALUE || |
| handle == NULL || |
| handle == (HANDLE) -2) { |
| *dup = INVALID_HANDLE_VALUE; |
| uv__set_artificial_error(loop, UV_EBADF); |
| return -1; |
| } |
| |
| 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); |
| } |
| |
| |
| uv_err_t uv__create_nul_handle(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) { |
| return uv__new_sys_error(GetLastError()); |
| } |
| |
| *handle_ptr = handle; |
| return uv_ok_; |
| } |
| |
| |
| uv_err_t uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options, |
| BYTE** buffer_ptr) { |
| BYTE* buffer; |
| int count, i; |
| uv_err_t err; |
| |
| count = options->stdio_count; |
| |
| if (count < 0 || count > 255) { |
| /* Only support FDs 0-255 */ |
| return uv__new_artificial_error(UV_ENOTSUP); |
| } else if (count < 3) { |
| /* There should always be at least 3 stdio handles. */ |
| count = 3; |
| } |
| |
| /* Allocate the child stdio buffer */ |
| buffer = (BYTE*) malloc(CHILD_STDIO_SIZE(count)); |
| if (buffer == NULL) { |
| return uv__new_artificial_error(UV_ENOMEM); |
| } |
| |
| /* 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; |
| |
| err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i), |
| access); |
| if (err.code != UV_OK) |
| 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)); |
| |
| err = uv__create_stdio_pipe_pair(loop, |
| parent_pipe, |
| &child_pipe, |
| fdopt.flags); |
| if (err.code != UV_OK) |
| 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) { |
| /* If fdopt.data.fd is not valid and fd fd <= 2, then ignore the */ |
| /* error. */ |
| if (fdopt.data.fd <= 2 && loop->last_err.code == UV_EBADF) { |
| CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; |
| CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; |
| break; |
| } |
| 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 uv_ok_; |
| |
| error: |
| uv__stdio_destroy(buffer); |
| return err; |
| } |
| |
| |
| 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); |
| } |
| } |
| } |
| |
| |
| int uv__stdio_verify(BYTE* buffer, WORD size) { |
| unsigned int count; |
| |
| /* Check the buffer pointer. */ |
| if (buffer == NULL) |
| return 0; |
| |
| /* Verify that the buffer is at least big enough to hold the count. */ |
| if (size < CHILD_STDIO_SIZE(0)) |
| return 0; |
| |
| /* Verify if the count is within range. */ |
| count = CHILD_STDIO_COUNT(buffer); |
| if (count > 256) |
| return 0; |
| |
| /* Verify that the buffer size is big enough to hold info for N FDs. */ |
| if (size < CHILD_STDIO_SIZE(count)) |
| return 0; |
| |
| return 1; |
| } |
| |
| |
| 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); |
| } |