diff --git a/src/Init/System/IO.lean b/src/Init/System/IO.lean index 5666a25d8f..5dbaad1fa6 100644 --- a/src/Init/System/IO.lean +++ b/src/Init/System/IO.lean @@ -1355,6 +1355,8 @@ structure SpawnArgs extends StdioConfig where and `some` sets the variable to the new value, adding it if necessary. Variables are processed from left to right. -/ env : Array (String × Option String) := #[] + /-- Inherit environment variables from the spawning process. -/ + inheritEnv : Bool := true /-- Starts the child process in a new session and process group using `setsid`. Currently a no-op on non-POSIX platforms. diff --git a/src/runtime/process.cpp b/src/runtime/process.cpp index 50b8bc3907..ed8d63d41c 100644 --- a/src/runtime/process.cpp +++ b/src/runtime/process.cpp @@ -172,7 +172,7 @@ static void setup_stdio(SECURITY_ATTRIBUTES * saAttr, HANDLE * theirs, object ** // This code is adapted from: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx static obj_res spawn(string_ref const & proc_name, array_ref const & args, stdio stdin_mode, stdio stdout_mode, stdio stderr_mode, option_ref const & cwd, array_ref>> const & env, - bool _do_setsid) { + bool inherit_env, bool _do_setsid) { HANDLE child_stdin = GetStdHandle(STD_INPUT_HANDLE); HANDLE child_stdout = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE child_stderr = GetStdHandle(STD_ERROR_HANDLE); @@ -229,38 +229,45 @@ static obj_res spawn(string_ref const & proc_name, array_ref const & std::unique_ptr new_env(nullptr); - if (env.size()) { - auto * esp = GetEnvironmentStrings(); - - std::unordered_map new_env_vars; // C++17 gives us no-copy std::string_view for this, much better! - for (auto & entry : env) { - new_env_vars[entry.fst().data()] = entry.snd() ? entry.snd().get()->data() : std::string{}; - } + if (env.size() || !inherit_env) { static constexpr auto env_buf_size = 0x7fff; // according to MS docs 0x7fff is the max total size of env block new_env = std::make_unique(env_buf_size); - // First copy old evars not in new evars. - char *new_envp = new_env.get(), *key_begin = esp; - while (*key_begin) { - char *key_end = strchr(key_begin, '='); - char *entry_end = key_end + strlen(key_end); - if (!new_env_vars.count({key_begin, key_end})) { - new_envp = std::copy(key_begin, entry_end + 1, new_envp); - } - key_begin = entry_end + 1; - } - // Then copy new evars if nonempty - for(const auto & ev : new_env_vars) { - if (ev.second.empty()) continue; - // Check if the destination buffer has enough room. - if (new_envp + ev.first.length() + 1 + ev.second.length() + 1 > new_env.get() + env_buf_size - 1) break; - new_envp = std::copy(ev.first.cbegin(), ev.first.cend(), new_envp); - *new_envp++ = '='; - new_envp = std::copy(ev.second.cbegin(), ev.second.cend(), new_envp); - *new_envp++ = '\0'; - } - *new_envp = '\0'; + char *new_envp = new_env.get(); - FreeEnvironmentStrings(esp); + if (env.size()) { + std::unordered_map new_env_vars; // C++17 gives us no-copy std::string_view for this, much better! + for (auto & entry : env) { + new_env_vars[entry.fst().data()] = entry.snd() ? entry.snd().get()->data() : std::string{}; + } + + // First copy old evars not in new evars. + if (inherit_env) { + auto *esp = GetEnvironmentStrings(); + char *key_begin = esp; + while (*key_begin) { + char *key_end = strchr(key_begin, '='); + char *entry_end = key_end + strlen(key_end); + if (!new_env_vars.count({key_begin, key_end})) { + new_envp = std::copy(key_begin, entry_end + 1, new_envp); + } + key_begin = entry_end + 1; + } + FreeEnvironmentStrings(esp); + } + + // Then copy new evars if nonempty + for(const auto & ev : new_env_vars) { + if (ev.second.empty()) continue; + // Check if the destination buffer has enough room. + if (new_envp + ev.first.length() + 1 + ev.second.length() + 1 > new_env.get() + env_buf_size - 1) break; + new_envp = std::copy(ev.first.cbegin(), ev.first.cend(), new_envp); + *new_envp++ = '='; + new_envp = std::copy(ev.second.cbegin(), ev.second.cend(), new_envp); + *new_envp++ = '\0'; + } + } + + *new_envp = '\0'; } // Create the child process. @@ -423,9 +430,13 @@ static optional setup_stdio(stdio cfg) { lean_unreachable(); } +#ifdef __APPLE__ +extern "C" char **environ; +#endif + static obj_res spawn(string_ref const & proc_name, array_ref const & args, stdio stdin_mode, stdio stdout_mode, stdio stderr_mode, option_ref const & cwd, array_ref>> const & env, - bool do_setsid) { + bool inherit_env, bool do_setsid) { /* Setup stdio based on process configuration. */ auto stdin_pipe = setup_stdio(stdin_mode); auto stdout_pipe = setup_stdio(stdout_mode); @@ -434,6 +445,13 @@ static obj_res spawn(string_ref const & proc_name, array_ref const & int pid = fork(); if (pid == 0) { + if (!inherit_env) { +#ifdef __APPLE__ + environ = NULL; +#else + clearenv(); +#endif + } for (auto & entry : env) { if (entry.snd()) { setenv(entry.fst().data(), entry.snd().get()->data(), true); @@ -549,7 +567,8 @@ extern "C" LEAN_EXPORT obj_res lean_io_process_spawn(obj_arg args_, obj_arg) { stderr_mode, cnstr_get_ref_t>(args, 3), cnstr_get_ref_t>>>(args, 4), - cnstr_get_uint8(args.raw(), 5 * sizeof(object *))); + cnstr_get_uint8(args.raw(), 5 * sizeof(object *)), + cnstr_get_uint8(args.raw(), 5 * sizeof(object *) + 1)); } catch (int err) { return lean_io_result_mk_error(decode_io_error(err, nullptr)); } catch (std::system_error const & err) {