diff --git a/include/uv-private/uv-unix.h b/include/uv-private/uv-unix.h index 21078fe3..1c33684a 100644 --- a/include/uv-private/uv-unix.h +++ b/include/uv-private/uv-unix.h @@ -33,6 +33,7 @@ #include #include #include +#include #include /* Note: May be cast to struct iovec. See writev(2). */ @@ -43,6 +44,10 @@ typedef struct { typedef int uv_file; +/* Platform-specific definitions for uv_spawn support. */ +typedef gid_t uv_gid_t; +typedef uid_t uv_uid_t; + /* Platform-specific definitions for uv_dlopen support. */ typedef void* uv_lib_t; #define UV_DYNAMIC /* empty */ diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index e620f8b0..4d8ac9c6 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -137,6 +137,10 @@ typedef struct uv_buf_t { typedef int uv_file; +/* Platform-specific definitions for uv_spawn support. */ +typedef unsigned char uv_uid_t; +typedef unsigned char uv_gid_t; + /* Platform-specific definitions for uv_dlopen support. */ typedef HMODULE uv_lib_t; #define UV_DYNAMIC FAR WINAPI diff --git a/include/uv.h b/include/uv.h index ed6100ae..c6f15e84 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1058,6 +1058,34 @@ struct uv_process_s { UV_EXTERN int uv_spawn(uv_loop_t*, uv_process_t*, uv_process_options_t options); + +/* Temporary fix for node. Do no use. */ +enum uv_process_flags { + UV_PROCESS_SETUID = (1 << 0), + UV_PROCESS_SETGID = (1 << 1), + UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2) +}; + +/* Temporary fix for node. Do not use. */ +typedef struct uv_process_options2_s { + uv_exit_cb exit_cb; /* Called after the process exits. */ + const char* file; /* Path to program to execute. */ + char** args; + char** env; + char* cwd; + unsigned int flags; + uv_pipe_t* stdin_stream; + uv_pipe_t* stdout_stream; + uv_pipe_t* stderr_stream; + uv_uid_t uid; + uv_gid_t gid; +} uv_process_options2_t; + +/* Temporary fix for node. Do not use. */ +UV_EXTERN int uv_spawn2(uv_loop_t*, uv_process_t*, + uv_process_options2_t options); + + /* * Kills the process with the specified signal. The user must still * call uv_close on the process. diff --git a/src/unix/process.c b/src/unix/process.c index 5581d8b8..89c2c77c 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -161,8 +161,8 @@ static int uv__process_init_pipe(uv_pipe_t* handle, int fds[2], int flags) { # define SPAWN_WAIT_EXEC 1 #endif -int uv_spawn(uv_loop_t* loop, uv_process_t* process, - uv_process_options_t options) { +int uv_spawn2(uv_loop_t* loop, uv_process_t* process, + uv_process_options2_t options) { /* * Save environ in the case that we get it clobbered * by the child process. @@ -179,6 +179,12 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, pid_t pid; int flags; + assert(options.file != NULL); + assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | + UV_PROCESS_SETGID | + UV_PROCESS_SETUID))); + + uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); loop->counters.process_init++; @@ -268,6 +274,16 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, _exit(127); } + if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) { + perror("setgid()"); + _exit(127); + } + + if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) { + perror("setuid()"); + _exit(127); + } + environ = options.env; execvp(options.file, options.args); diff --git a/src/uv-common.c b/src/uv-common.c index 3143bd2d..c5172f62 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -261,3 +261,29 @@ int uv_tcp_connect6(uv_connect_t* req, return uv__tcp_connect6(req, handle, address, cb); } + + +/* Thunk that converts uv_process_options_t into uv_process_options2_t, */ +/* and then calls uv_spawn2. */ +int uv_spawn(uv_loop_t* loop, uv_process_t* process, + uv_process_options_t options) { + uv_process_options2_t options2; + + options2.exit_cb = options.exit_cb; + options2.file = options.file; + options2.args = options.args; + options2.cwd = options.cwd; + options2.env = options.env; + options2.stdin_stream = options.stdin_stream; + options2.stdout_stream = options.stdout_stream; + options2.stderr_stream = options.stderr_stream; + + options2.flags = 0; + if (options.windows_verbatim_arguments) { + options2.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; + } + + /* No need to set gid and uid. */ + + return uv_spawn2(loop, process, options2); +} diff --git a/src/win/process.c b/src/win/process.c index a23ba0b1..71620a00 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -860,8 +860,8 @@ static int duplicate_std_handle(uv_loop_t* loop, DWORD id, HANDLE* dup) { } -int uv_spawn(uv_loop_t* loop, uv_process_t* process, - uv_process_options_t options) { +int uv_spawn2(uv_loop_t* loop, uv_process_t* process, + uv_process_options2_t options) { int err = 0, keep_child_stdio_open = 0; wchar_t* path = NULL; int size; @@ -872,17 +872,22 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, STARTUPINFOW startup; PROCESS_INFORMATION info; - if (!options.file) { - uv__set_artificial_error(loop, UV_EINVAL); + if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { + uv__set_sys_error(loop, UV_ENOTSUP); return -1; } + assert(options.file != NULL); + assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | + UV_PROCESS_SETGID | + UV_PROCESS_SETUID))); + uv_process_init(loop, process); process->exit_cb = options.exit_cb; UTF8_TO_UTF16(options.file, application); arguments = options.args ? make_program_args(options.args, - options.windows_verbatim_arguments) : NULL; + options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS) : NULL; env = options.env ? make_program_env(options.env) : NULL; if (options.cwd) {