@ -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
#endif
static inline void set_cloexec(int fd)
static inline void set_cloexec(int fd)
@ -395,12 +444,14 @@ fail_pipe:
#ifndef GIT_WINDOWS_NATIVE
#ifndef GIT_WINDOWS_NATIVE
{
{
int notify_pipe[2];
int notify_pipe[2];
char **childenv;
struct argv_array argv = ARGV_ARRAY_INIT;
struct argv_array argv = ARGV_ARRAY_INIT;
if (pipe(notify_pipe))
if (pipe(notify_pipe))
notify_pipe[0] = notify_pipe[1] = -1;
notify_pipe[0] = notify_pipe[1] = -1;
prepare_cmd(&argv, cmd);
prepare_cmd(&argv, cmd);
childenv = prep_childenv(cmd->env);
cmd->pid = fork();
cmd->pid = fork();
failed_errno = errno;
failed_errno = errno;
@ -456,14 +507,6 @@ fail_pipe:
if (cmd->dir && chdir(cmd->dir))
if (cmd->dir && chdir(cmd->dir))
die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
cmd->dir);
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
* 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
* be used in the event exec failed with ENOEXEC at which point
* we will try to interpret the command using 'sh'.
* 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)
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 (errno == ENOENT) {
if (!cmd->silent_exec_failure)
if (!cmd->silent_exec_failure)
@ -509,6 +554,7 @@ fail_pipe:
close(notify_pipe[0]);
close(notify_pipe[0]);
argv_array_clear(&argv);
argv_array_clear(&argv);
free(childenv);
}
}
#else
#else
{
{