| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- /* 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"
- #include "handle-inl.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 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;
- int 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)
- 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 = 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 = 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 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 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;
- return ERROR_INVALID_HANDLE;
- }
- current_process = GetCurrentProcess();
- if (!DuplicateHandle(current_process,
- handle,
- current_process,
- dup,
- 0,
- TRUE,
- DUPLICATE_SAME_ACCESS)) {
- *dup = INVALID_HANDLE_VALUE;
- return GetLastError();
- }
- return 0;
- }
- static int uv__duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) {
- HANDLE handle;
- if (fd == -1) {
- *dup = INVALID_HANDLE_VALUE;
- return ERROR_INVALID_HANDLE;
- }
- handle = uv__get_osfhandle(fd);
- return uv__duplicate_handle(loop, handle, dup);
- }
- int 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 GetLastError();
- }
- *handle_ptr = handle;
- return 0;
- }
- int uv__stdio_create(uv_loop_t* loop,
- const uv_process_options_t* options,
- BYTE** buffer_ptr) {
- BYTE* buffer;
- int count, i;
- int err;
- count = options->stdio_count;
- if (count < 0 || count > 255) {
- /* Only support FDs 0-255 */
- return ERROR_NOT_SUPPORTED;
- } else if (count < 3) {
- /* There should always be at least 3 stdio handles. */
- count = 3;
- }
- /* Allocate the child stdio buffer */
- buffer = (BYTE*) uv__malloc(CHILD_STDIO_SIZE(count));
- if (buffer == NULL) {
- return ERROR_OUTOFMEMORY;
- }
- /* 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)
- 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 = INVALID_HANDLE_VALUE;
- /* 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)
- 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. */
- err = uv__duplicate_fd(loop, fdopt.data.fd, &child_handle);
- if (err) {
- /* If fdopt.data.fd is not valid and fd fd <= 2, then ignore the */
- /* error. */
- if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
- 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;
- break;
- case FILE_TYPE_CHAR:
- case FILE_TYPE_REMOTE:
- CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
- break;
- case FILE_TYPE_UNKNOWN:
- if (GetLastError() != 0) {
- err = GetLastError();
- CloseHandle(child_handle);
- goto error;
- }
- CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
- break;
- default:
- assert(0);
- return -1;
- }
- 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_CONNECTION) {
- 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. */
- err = ERROR_NOT_SUPPORTED;
- goto error;
- }
- /* Make an inheritable copy of the handle. */
- err = uv__duplicate_handle(loop, stream_handle, &child_handle);
- if (err)
- goto error;
- CHILD_STDIO_HANDLE(buffer, i) = child_handle;
- CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
- break;
- }
- default:
- assert(0);
- return -1;
- }
- }
- *buffer_ptr = buffer;
- return 0;
- 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);
- }
- }
- uv__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);
- }
|