diff --git a/include/uv.h b/include/uv.h index fd1f7d38..9fabcd47 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1246,7 +1246,15 @@ enum uv_process_flags { * converting the argument list into a command line string. This option is * only meaningful on Windows systems. On unix it is silently ignored. */ - UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2) + UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2), + /* + * Spawn the child process in a detached state - this will make it a process + * group leader, and will effectively enable the child to keep running after + * the parent exits. Note that the child process will still keep the + * parent's event loop alive unless the parent process calls uv_unref() on + * the child's process handle. + */ + UV_PROCESS_DETACHED = (1 << 3) }; /* diff --git a/src/unix/process.c b/src/unix/process.c index 2efe896a..2d90d97d 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -240,6 +240,7 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, assert(options.file != NULL); assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | + UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID))); @@ -301,6 +302,9 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, if (pid == 0) { /* Child */ + if (options.flags & UV_PROCESS_DETACHED) { + setsid(); + } /* Dup fds */ for (i = 0; i < options.stdio_count; i++) { diff --git a/src/win/process.c b/src/win/process.c index d0a81944..fa409e3b 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -869,7 +869,8 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, int err = 0, keep_child_stdio_open = 0; wchar_t* path = NULL; int size, i, overlapped; - DWORD server_access, child_access; + DWORD server_access, child_access, + process_flags = CREATE_UNICODE_ENVIRONMENT; BOOL result; wchar_t* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; @@ -891,6 +892,7 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, assert(options.file != NULL); assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | + UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID))); @@ -1005,12 +1007,16 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, startup.hStdOutput = child_stdio[1]; startup.hStdError = child_stdio[2]; + if (options.flags & UV_PROCESS_DETACHED) { + process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; + } + if (CreateProcessW(application_path, arguments, NULL, NULL, 1, - CREATE_UNICODE_ENVIRONMENT, + process_flags, env, cwd, &startup, diff --git a/test/test-list.h b/test/test-list.h index 9358d56d..40e7dd52 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -120,6 +120,7 @@ TEST_DECLARE (spawn_exit_code) TEST_DECLARE (spawn_stdout) TEST_DECLARE (spawn_stdin) TEST_DECLARE (spawn_and_kill) +TEST_DECLARE (spawn_detached) TEST_DECLARE (spawn_and_kill_with_std) TEST_DECLARE (spawn_and_ping) TEST_DECLARE (spawn_setuid_fails) @@ -333,6 +334,7 @@ TASK_LIST_START TEST_ENTRY (spawn_stdout) TEST_ENTRY (spawn_stdin) TEST_ENTRY (spawn_and_kill) + TEST_ENTRY (spawn_detached) TEST_ENTRY (spawn_and_kill_with_std) TEST_ENTRY (spawn_and_ping) TEST_ENTRY (spawn_setuid_fails) diff --git a/test/test-spawn.c b/test/test-spawn.c index 25863de6..91b51edf 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -99,6 +99,11 @@ static void kill_cb(uv_process_t* process, int exit_status, int term_signal) { ASSERT(err.code == UV_ESRCH); } +static void detach_failure_cb(uv_process_t* process, int exit_status, int term_signal) { + uv_err_t err; + printf("detach_cb\n"); + exit_cb_called++; +} static uv_buf_t on_alloc(uv_handle_t* handle, size_t suggested_size) { uv_buf_t buf; @@ -314,6 +319,32 @@ TEST_IMPL(spawn_and_kill) { return 0; } +TEST_IMPL(spawn_detached) { + int r; + uv_err_t err; + + init_process_options("spawn_helper4", detach_failure_cb); + + options.flags |= UV_PROCESS_DETACHED; + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + uv_unref((uv_handle_t*)&process); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 0); + + err = uv_kill(process.pid, 0); + ASSERT(err.code == 0); + + err = uv_kill(process.pid, 15); + ASSERT(err.code == 0); + + return 0; +} TEST_IMPL(spawn_and_kill_with_std) { int r;