From 2d7d63704709cda2e6fbdf157fa8c3a8406988df Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 24 Jun 2014 15:39:01 -0700 Subject: [PATCH] windows: Read the PATH env var of the child The unix and windows process implementations diverge in their behavior when dealing with subprocesses that are spawned with a relative path. With unix the *child's* PATH environment variable is read, whereas with windows the *parent's* environment variable is read. This commit brings the two implementation in line with respect to their behavior of reading PATH by having both read the *child's* PATH environment variable. This involves looking into the user-provided environment on windows and extracting the PATH variable specifically so it can be inspected later on. --- src/win/process.c | 26 +++++++++++++++++++++----- test/test-list.h | 2 ++ test/test-spawn.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/win/process.c b/src/win/process.c index 70c1c23205..5394aafc7d 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -813,6 +813,20 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { return 0; } +/* + * Attempt to find the value of the PATH environment variable in the child's + * preprocessed environment. + * + * If found, a pointer into `env` is returned. If not found, NULL is returned. + */ +static WCHAR* find_path(WCHAR *env) { + for (; env != NULL && *env != 0; env += wcslen(env)) { + if (wcsncmp(env, L"PATH=", 5) == 0) + return &env[5]; + } + + return NULL; +} /* * Called on Windows thread-pool thread to indicate that @@ -910,7 +924,7 @@ int uv_spawn(uv_loop_t* loop, const uv_process_options_t* options) { int i; int err = 0; - WCHAR* path = NULL; + WCHAR* path = NULL, *alloc_path = NULL; BOOL result; WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; @@ -984,7 +998,8 @@ int uv_spawn(uv_loop_t* loop, } /* Get PATH environment variable. */ - { + path = find_path(env); + if (path == NULL) { DWORD path_len, r; path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); @@ -993,11 +1008,12 @@ int uv_spawn(uv_loop_t* loop, goto done; } - path = (WCHAR*) malloc(path_len * sizeof(WCHAR)); - if (path == NULL) { + alloc_path = (WCHAR*) malloc(path_len * sizeof(WCHAR)); + if (alloc_path == NULL) { err = ERROR_OUTOFMEMORY; goto done; } + path = alloc_path; r = GetEnvironmentVariableW(L"PATH", path, path_len); if (r == 0 || r >= path_len) { @@ -1131,7 +1147,7 @@ int uv_spawn(uv_loop_t* loop, free(arguments); free(cwd); free(env); - free(path); + free(alloc_path); if (process->child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ diff --git a/test/test-list.h b/test/test-list.h index 55eaee1f67..6dbe22307e 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -192,6 +192,7 @@ TEST_DECLARE (spawn_stdout_to_file) TEST_DECLARE (spawn_stdout_and_stderr_to_file) TEST_DECLARE (spawn_auto_unref) TEST_DECLARE (spawn_closed_process_io) +TEST_DECLARE (spawn_reads_child_path) TEST_DECLARE (fs_poll) TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (kill) @@ -520,6 +521,7 @@ TASK_LIST_START TEST_ENTRY (spawn_stdout_and_stderr_to_file) TEST_ENTRY (spawn_auto_unref) TEST_ENTRY (spawn_closed_process_io) + TEST_ENTRY (spawn_reads_child_path) TEST_ENTRY (fs_poll) TEST_ENTRY (fs_poll_getpath) TEST_ENTRY (kill) diff --git a/test/test-spawn.c b/test/test-spawn.c index 0b4047768b..57f0862f94 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -1291,3 +1291,38 @@ TEST_IMPL(closed_fd_events) { return 0; } #endif /* !_WIN32 */ + +TEST_IMPL(spawn_reads_child_path) { + int r; + int len; + char path[1024]; + char *env[2] = {path, NULL}; + + /* Set up the process, but make sure that the file to run is relative and */ + /* requires a lookup into PATH */ + init_process_options("spawn_helper1", exit_cb); + options.file = "run-tests"; + args[0] = "run-tests"; + + /* Set up the PATH env variable */ + for (len = strlen(exepath); + exepath[len - 1] != '/' && exepath[len - 1] != '\\'; + len--); + exepath[len] = 0; + strcpy(path, "PATH="); + strcpy(path + 5, exepath); + + options.env = env; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +}