summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Williams <bmwill@google.com>2017-04-19 16:13:22 -0700
committerJunio C Hamano <gitster@pobox.com>2017-04-20 17:55:32 -0700
commitae25394b4c21f072f176ccbaf7047abe7fa5f04d (patch)
treec04504f47cb2a5e3657e4e4d9aaeb4f240c5300c
parent3a30033327337323e91dcbcb87396d3245260585 (diff)
downloadgit-ae25394b4c21f072f176ccbaf7047abe7fa5f04d.tar.gz
run-command: prepare child environment before forking
In order to avoid allocation between 'fork()' and 'exec()' prepare the environment to be used in the child process prior to forking. Switch to using 'execve()' so that the construct child environment can used in the exec'd process. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--run-command.c66
1 files changed, 56 insertions, 10 deletions
diff --git a/run-command.c b/run-command.c
index 1c7a3b6110..15e2e74a7e 100644
--- a/run-command.c
+++ b/run-command.c
@@ -267,6 +267,55 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
}
}
}
+
+static char **prep_childenv(const char *const *deltaenv)
+{
+ extern char **environ;
+ char **childenv;
+ struct string_list env = STRING_LIST_INIT_DUP;
+ struct strbuf key = STRBUF_INIT;
+ const char *const *p;
+ int i;
+
+ /* Construct a sorted string list consisting of the current environ */
+ for (p = (const char *const *) environ; p && *p; p++) {
+ const char *equals = strchr(*p, '=');
+
+ if (equals) {
+ strbuf_reset(&key);
+ strbuf_add(&key, *p, equals - *p);
+ string_list_append(&env, key.buf)->util = (void *) *p;
+ } else {
+ string_list_append(&env, *p)->util = (void *) *p;
+ }
+ }
+ string_list_sort(&env);
+
+ /* Merge in 'deltaenv' with the current environ */
+ for (p = deltaenv; p && *p; p++) {
+ const char *equals = strchr(*p, '=');
+
+ if (equals) {
+ /* ('key=value'), insert or replace entry */
+ strbuf_reset(&key);
+ strbuf_add(&key, *p, equals - *p);
+ string_list_insert(&env, key.buf)->util = (void *) *p;
+ } else {
+ /* otherwise ('key') remove existing entry */
+ string_list_remove(&env, *p, 0);
+ }
+ }
+
+ /* Create an array of 'char *' to be used as the childenv */
+ childenv = xmalloc((env.nr + 1) * sizeof(char *));
+ for (i = 0; i < env.nr; i++)
+ childenv[i] = env.items[i].util;
+ childenv[env.nr] = NULL;
+
+ string_list_clear(&env, 0);
+ strbuf_release(&key);
+ return childenv;
+}
#endif
static inline void set_cloexec(int fd)
@@ -395,12 +444,14 @@ fail_pipe:
#ifndef GIT_WINDOWS_NATIVE
{
int notify_pipe[2];
+ char **childenv;
struct argv_array argv = ARGV_ARRAY_INIT;
if (pipe(notify_pipe))
notify_pipe[0] = notify_pipe[1] = -1;
prepare_cmd(&argv, cmd);
+ childenv = prep_childenv(cmd->env);
cmd->pid = fork();
failed_errno = errno;
@@ -456,14 +507,6 @@ fail_pipe:
if (cmd->dir && chdir(cmd->dir))
die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
cmd->dir);
- if (cmd->env) {
- for (; *cmd->env; cmd->env++) {
- if (strchr(*cmd->env, '='))
- putenv((char *)*cmd->env);
- else
- unsetenv(*cmd->env);
- }
- }
/*
* Attempt to exec using the command and arguments starting at
@@ -471,9 +514,11 @@ fail_pipe:
* be used in the event exec failed with ENOEXEC at which point
* we will try to interpret the command using 'sh'.
*/
- execv(argv.argv[1], (char *const *) argv.argv + 1);
+ execve(argv.argv[1], (char *const *) argv.argv + 1,
+ (char *const *) childenv);
if (errno == ENOEXEC)
- execv(argv.argv[0], (char *const *) argv.argv);
+ execve(argv.argv[0], (char *const *) argv.argv,
+ (char *const *) childenv);
if (errno == ENOENT) {
if (!cmd->silent_exec_failure)
@@ -509,6 +554,7 @@ fail_pipe:
close(notify_pipe[0]);
argv_array_clear(&argv);
+ free(childenv);
}
#else
{