From 59f149628933ac61929beabb686d95031839f554 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Fri, 2 Oct 2020 16:50:06 +0000 Subject: [PATCH] subprocess: don't initialize environment at startup Currently, the subprocess package reads from the environment as it's created at startup when the init function is called. However, we'll soon want to modify the environment in this case before it gets processed, so let's change the code to use a mutex to initialize the environment once before using it and simply call that before using the environment we've set up. We'll want to reset the environment as well in a future commit, so let's be sure to add a function for that. We reuse the same internal function and just ignore the return value to make our code paths simpler. --- subprocess/subprocess.go | 29 ++++++++++++++++++++++++++++- subprocess/subprocess_nix.go | 2 +- subprocess/subprocess_windows.go | 2 +- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/subprocess/subprocess.go b/subprocess/subprocess.go index e430eccd..a70551cd 100644 --- a/subprocess/subprocess.go +++ b/subprocess/subprocess.go @@ -9,6 +9,7 @@ import ( "os/exec" "regexp" "strings" + "sync" "github.com/rubyist/tracerx" ) @@ -128,6 +129,7 @@ func quotedArgs(args []string) string { // An env for an exec.Command without GIT_TRACE and GIT_INTERNAL_SUPER_PREFIX var env []string +var envMu sync.Mutex var traceEnv = "GIT_TRACE=" // Don't pass GIT_INTERNAL_SUPER_PREFIX back to Git. Git passes this environment @@ -138,7 +140,20 @@ var traceEnv = "GIT_TRACE=" // support --super-prefix and would immediately exit with an error as a result. var superPrefixEnv = "GIT_INTERNAL_SUPER_PREFIX=" -func init() { +func fetchEnvironment() []string { + envMu.Lock() + defer envMu.Unlock() + + return fetchEnvironmentInternal() +} + +// fetchEnvironmentInternal should only be called from fetchEnvironment or +// ResetEnvironment, who will hold the required lock. +func fetchEnvironmentInternal() []string { + if env != nil { + return env + } + realEnv := os.Environ() env = make([]string, 0, len(realEnv)) @@ -148,4 +163,16 @@ func init() { } env = append(env, kv) } + return env +} + +// ResetEnvironment resets the cached environment that's used in subprocess +// calls. +func ResetEnvironment() { + envMu.Lock() + defer envMu.Unlock() + + env = nil + // Reinitialize the environment settings. + fetchEnvironmentInternal() } diff --git a/subprocess/subprocess_nix.go b/subprocess/subprocess_nix.go index 6e96a0af..35f0ad89 100644 --- a/subprocess/subprocess_nix.go +++ b/subprocess/subprocess_nix.go @@ -9,6 +9,6 @@ import ( // ExecCommand is a small platform specific wrapper around os/exec.Command func ExecCommand(name string, arg ...string) *Cmd { cmd := exec.Command(name, arg...) - cmd.Env = env + cmd.Env = fetchEnvironment() return newCmd(cmd) } diff --git a/subprocess/subprocess_windows.go b/subprocess/subprocess_windows.go index 9a104d1b..75dc3bb5 100644 --- a/subprocess/subprocess_windows.go +++ b/subprocess/subprocess_windows.go @@ -11,6 +11,6 @@ import ( func ExecCommand(name string, arg ...string) *Cmd { cmd := exec.Command(name, arg...) cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} - cmd.Env = env + cmd.Env = fetchEnvironment() return newCmd(cmd) }