Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

windows: allow specifying FDs to be inherited by a child process

Previously the only option was to create a pipe or an ipc channel. This
patch makes it possible to inherit a handle that is already open in the
parent process. There is also room for setting more than just stdin,
stdout and stderr, although this is not supported yet.
  • Loading branch information...
Igor Zinkovsky authored and piscisaureus committed May 15, 2012
1 parent e4f23aa commit 5a34f19970086b248646e1907207144a72773a14
Showing with 261 additions and 71 deletions.
  1. +25 −6 include/uv.h
  2. +114 −48 src/win/process.c
  3. +8 −2 test/benchmark-spawn.c
  4. +5 −0 test/runner-win.c
  5. +7 −1 test/test-ipc.c
  6. +2 −0 test/test-list.h
  7. +91 −12 test/test-spawn.c
  8. +9 −2 test/test-stdio-over-pipes.c
@@ -1156,6 +1156,27 @@ 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_READABLE_PIPE and UV_WRITABLE_PIPE flags are set from
* the child process perspective.
*/
UV_READABLE_PIPE = 0x02,
UV_WRITABLE_PIPE = 0x04,
UV_RAW_FD = 0x08
} uv_stdio_flags;

typedef struct uv_stdio_container_s {
uv_stdio_flags flags;

union {
uv_stream_t* stream;
int fd;
} data;
} uv_stdio_container_t;

typedef struct uv_process_options_s {
uv_exit_cb exit_cb; /* Called after the process exits. */
const char* file; /* Path to program to execute. */
@@ -1188,14 +1209,12 @@ typedef struct uv_process_options_s {
*/
uv_uid_t uid;
uv_gid_t gid;

/*
* The user should supply pointers to initialized uv_pipe_t structs for
* stdio. This is used to to send or receive input from the subprocess.
* The user is responsible for calling uv_close on them.
* A container of stdio streams (stdin/stdout/stderr)
*/
uv_pipe_t* stdin_stream;
uv_pipe_t* stdout_stream;
uv_pipe_t* stderr_stream;
uv_stdio_container_t* stdio;
int stdio_count;
} uv_process_options_t;

/*
@@ -22,6 +22,7 @@
#include "uv.h"
#include "internal.h"

#include <io.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
@@ -861,23 +862,66 @@ static int duplicate_std_handle(uv_loop_t* loop, DWORD id, HANDLE* dup) {
}


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);
}


int uv_spawn(uv_loop_t* loop, uv_process_t* process,
uv_process_options_t options) {
int err = 0, keep_child_stdio_open = 0;
wchar_t* path = NULL;
int size;
int size, i, overlapped;
DWORD server_access, child_access;
BOOL result;
wchar_t* application_path = NULL, *application = NULL, *arguments = NULL,
*env = NULL, *cwd = NULL;
HANDLE* child_stdio = process->child_stdio;
STARTUPINFOW startup;
PROCESS_INFORMATION info;
uv_pipe_t* pipe;

if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
uv__set_artificial_error(loop, UV_ENOTSUP);
return -1;
}

/* Only support FDs 0-2 */
if (options.stdio_count > 3) {
uv__set_artificial_error(loop, UV_ENOTSUP);
return -1;
}

assert(options.file != NULL);
assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
UV_PROCESS_SETGID |
@@ -927,59 +971,79 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
application_path = application;
}

/* Create stdio pipes. */
if (options.stdin_stream) {
if (options.stdin_stream->ipc) {
err = uv_create_stdio_pipe_pair(
loop,
options.stdin_stream,
&child_stdio[0],
PIPE_ACCESS_DUPLEX,
GENERIC_READ | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE,
1);
for (i = 0; i < options.stdio_count; i++) {
if (options.stdio[i].flags == UV_IGNORE) {
continue;
}

if (options.stdio[i].flags & UV_RAW_FD) {
err = duplicate_fd(loop, options.stdio[i].data.fd, &child_stdio[i]);
} else if (options.stdio[i].data.stream->type == UV_NAMED_PIPE) {
pipe = (uv_pipe_t*)options.stdio[i].data.stream;

if (options.stdio[i].flags & UV_CREATE_PIPE) {
server_access = 0;
child_access = 0;
if (pipe->ipc) {
server_access = PIPE_ACCESS_DUPLEX;
child_access = GENERIC_READ | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE;
overlapped = 1;
} else {
overlapped = 0;

if (options.stdio[i].flags & UV_READABLE_PIPE) {
server_access |= PIPE_ACCESS_OUTBOUND;
child_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
}

if (options.stdio[i].flags & UV_WRITABLE_PIPE) {
server_access |= PIPE_ACCESS_INBOUND;
child_access |= GENERIC_WRITE;
}
}

err = uv_create_stdio_pipe_pair(
loop,
pipe,
&child_stdio[i],
server_access,
child_access,
overlapped);
} else {
err = duplicate_handle(loop, pipe->handle, &child_stdio[i]);
}
} else if(options.stdio[i].data.stream->type == UV_TTY) {
err = duplicate_handle(loop,
((uv_tty_t*)options.stdio[i].data.stream)->handle, &child_stdio[i]);
} else {
err = uv_create_stdio_pipe_pair(
loop,
options.stdin_stream,
&child_stdio[0],
PIPE_ACCESS_OUTBOUND,
GENERIC_READ | FILE_WRITE_ATTRIBUTES,
0);
err = -1;
uv__set_artificial_error(loop, UV_ENOTSUP);
}

if (err) {
goto done;
}
} else {
err = duplicate_std_handle(loop, STD_INPUT_HANDLE, &child_stdio[0]);
}
if (err) {
goto done;

if (child_stdio[0] == INVALID_HANDLE_VALUE) {
err = duplicate_std_handle(loop, STD_INPUT_HANDLE, &child_stdio[0]);
if (err) {
goto done;
}
}

if (options.stdout_stream) {
err = uv_create_stdio_pipe_pair(
loop, options.stdout_stream,
&child_stdio[1],
PIPE_ACCESS_INBOUND,
GENERIC_WRITE,
0);
} else {
if (child_stdio[1] == INVALID_HANDLE_VALUE) {
err = duplicate_std_handle(loop, STD_OUTPUT_HANDLE, &child_stdio[1]);
}
if (err) {
goto done;
if (err) {
goto done;
}
}

if (options.stderr_stream) {
err = uv_create_stdio_pipe_pair(
loop,
options.stderr_stream,
&child_stdio[2],
PIPE_ACCESS_INBOUND,
GENERIC_WRITE,
0);
} else {
if (child_stdio[2] == INVALID_HANDLE_VALUE) {
err = duplicate_std_handle(loop, STD_ERROR_HANDLE, &child_stdio[2]);
}
if (err) {
goto done;
if (err) {
goto done;
}
}

startup.cb = sizeof(startup);
@@ -1007,9 +1071,11 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
process->process_handle = info.hProcess;
process->pid = info.dwProcessId;

if (options.stdin_stream &&
options.stdin_stream->ipc) {
options.stdin_stream->ipc_pid = info.dwProcessId;
if (options.stdio_count > 0 &&
options.stdio[0].flags & UV_CREATE_PIPE &&
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;
}

/* Setup notifications for when the child process exits. */
@@ -101,6 +101,7 @@ void on_read(uv_stream_t* pipe, ssize_t nread, uv_buf_t buf) {


static void spawn() {
uv_stdio_container_t stdio[2];
int r;

ASSERT(process_open == 0);
@@ -114,7 +115,12 @@ static void spawn() {
options.exit_cb = exit_cb;

uv_pipe_init(loop, &out, 0);
options.stdout_stream = &out;

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;

r = uv_spawn(loop, &process, options);
ASSERT(r == 0);
@@ -153,4 +159,4 @@ BENCHMARK_IMPL(spawn) {
(double) N / (double) (end_time - start_time) * 1000.0);

return 0;
}
}
@@ -19,6 +19,7 @@
* IN THE SOFTWARE.
*/

#include <fcntl.h>
#include <io.h>
#include <malloc.h>
#include <stdio.h>
@@ -44,6 +45,10 @@ void platform_init(int argc, char **argv) {
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX);

_setmode(0, _O_BINARY);
_setmode(1, _O_BINARY);
_setmode(2, _O_BINARY);

/* Disable stdio output buffering. */
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
@@ -200,6 +200,7 @@ void spawn_helper(uv_pipe_t* channel,
char exepath[1024];
char* args[3];
int r;
uv_stdio_container_t stdio[1];

r = uv_pipe_init(uv_default_loop(), channel, 1);
ASSERT(r == 0);
@@ -218,7 +219,12 @@ void spawn_helper(uv_pipe_t* channel,
options.file = exepath;
options.args = args;
options.exit_cb = exit_cb;
options.stdin_stream = channel;

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_count = 1;

r = uv_spawn(uv_default_loop(), process, options);
ASSERT(r == 0);
@@ -123,6 +123,7 @@ TEST_DECLARE (spawn_and_kill_with_std)
TEST_DECLARE (spawn_and_ping)
TEST_DECLARE (spawn_setuid_fails)
TEST_DECLARE (spawn_setgid_fails)
TEST_DECLARE (spawn_stdout_to_file)
TEST_DECLARE (kill)
TEST_DECLARE (fs_file_noent)
TEST_DECLARE (fs_file_nametoolong)
@@ -334,6 +335,7 @@ TASK_LIST_START
TEST_ENTRY (spawn_and_ping)
TEST_ENTRY (spawn_setuid_fails)
TEST_ENTRY (spawn_setgid_fails)
TEST_ENTRY (spawn_stdout_to_file)
TEST_ENTRY (kill)
#ifdef _WIN32
TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows)

0 comments on commit 5a34f19

Please sign in to comment.
You can’t perform that action at this time.