feat: IO.Process.SpawnArgs.inheritEnv (#6081)
This PR adds an `inheritEnv` field to `IO.Process.SpawnArgs`. If `false`, the spawned process does not inherit its parent's environment. For example, Lake will make use of this to ensure that build processes do not use environment variables that Lake is not properly tracking with its traces.
This commit is contained in:
parent
46769b64c9
commit
b51115dac5
2 changed files with 53 additions and 32 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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<string_ref> const & args, stdio stdin_mode, stdio stdout_mode,
|
||||
stdio stderr_mode, option_ref<string_ref> const & cwd, array_ref<pair_ref<string_ref, option_ref<string_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<string_ref> const &
|
|||
|
||||
std::unique_ptr<char[]> new_env(nullptr);
|
||||
|
||||
if (env.size()) {
|
||||
auto * esp = GetEnvironmentStrings();
|
||||
|
||||
std::unordered_map<std::string, std::string> 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<char[]>(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<std::string, std::string> 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<pipe> setup_stdio(stdio cfg) {
|
|||
lean_unreachable();
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
extern "C" char **environ;
|
||||
#endif
|
||||
|
||||
static obj_res spawn(string_ref const & proc_name, array_ref<string_ref> const & args, stdio stdin_mode, stdio stdout_mode,
|
||||
stdio stderr_mode, option_ref<string_ref> const & cwd, array_ref<pair_ref<string_ref, option_ref<string_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<string_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<option_ref<string_ref>>(args, 3),
|
||||
cnstr_get_ref_t<array_ref<pair_ref<string_ref, option_ref<string_ref>>>>(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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue