X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=run-command.c;h=d679cc267c39b9fb34f57078db5db12ed591b7e2;hb=3fc8522e68c00c899567c2877bb71aff75d9df3e;hp=31fc5ea86eb6a3fe3650b8ac50f19f8621d90b9d;hpb=424aac653acce98f1bfb7f8b7bf36361f558472d;p=thirdparty%2Fgit.git diff --git a/run-command.c b/run-command.c index 31fc5ea86e..d679cc267c 100644 --- a/run-command.c +++ b/run-command.c @@ -1,11 +1,12 @@ #include "cache.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "sigchain.h" #include "argv-array.h" #include "thread-utils.h" #include "strbuf.h" #include "string-list.h" +#include "quote.h" void child_process_init(struct child_process *child) { @@ -244,7 +245,7 @@ int sane_execvp(const char *file, char * const argv[]) static const char **prepare_shell_cmd(struct argv_array *out, const char **argv) { if (!argv[0]) - die("BUG: shell command is empty"); + BUG("shell command is empty"); if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) { #ifndef GIT_WINDOWS_NATIVE @@ -379,10 +380,10 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr) set_error_routine(old_errfn); } -static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) +static int prepare_cmd(struct argv_array *out, const struct child_process *cmd) { if (!cmd->argv[0]) - die("BUG: command is empty"); + BUG("command is empty"); /* * Add SHELL_PATH so in the event exec fails with ENOEXEC we can @@ -402,16 +403,22 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) /* * If there are no '/' characters in the command then perform a path * lookup and use the resolved path as the command to exec. If there - * are no '/' characters or if the command wasn't found in the path, - * have exec attempt to invoke the command directly. + * are '/' characters, we have exec attempt to invoke the command + * directly. */ if (!strchr(out->argv[1], '/')) { char *program = locate_in_PATH(out->argv[1]); if (program) { free((char *)out->argv[1]); out->argv[1] = program; + } else { + argv_array_clear(out); + errno = ENOENT; + return -1; } } + + return 0; } static char **prep_childenv(const char *const *deltaenv) @@ -470,15 +477,12 @@ struct atfork_state { sigset_t old; }; -#ifndef NO_PTHREADS -static void bug_die(int err, const char *msg) -{ - if (err) { - errno = err; - die_errno("BUG: %s", msg); - } -} -#endif +#define CHECK_BUG(err, msg) \ + do { \ + int e = (err); \ + if (e) \ + BUG("%s: %s", msg, strerror(e)); \ + } while(0) static void atfork_prepare(struct atfork_state *as) { @@ -490,9 +494,9 @@ static void atfork_prepare(struct atfork_state *as) if (sigprocmask(SIG_SETMASK, &all, &as->old)) die_errno("sigprocmask"); #else - bug_die(pthread_sigmask(SIG_SETMASK, &all, &as->old), + CHECK_BUG(pthread_sigmask(SIG_SETMASK, &all, &as->old), "blocking all signals"); - bug_die(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs), + CHECK_BUG(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs), "disabling cancellation"); #endif } @@ -503,9 +507,9 @@ static void atfork_parent(struct atfork_state *as) if (sigprocmask(SIG_SETMASK, &as->old, NULL)) die_errno("sigprocmask"); #else - bug_die(pthread_setcancelstate(as->cs, NULL), + CHECK_BUG(pthread_setcancelstate(as->cs, NULL), "re-enabling cancellation"); - bug_die(pthread_sigmask(SIG_SETMASK, &as->old, NULL), + CHECK_BUG(pthread_sigmask(SIG_SETMASK, &as->old, NULL), "restoring signal mask"); #endif } @@ -556,6 +560,90 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal) return code; } +static void trace_add_env(struct strbuf *dst, const char *const *deltaenv) +{ + struct string_list envs = STRING_LIST_INIT_DUP; + const char *const *e; + int i; + int printed_unset = 0; + + /* Last one wins, see run-command.c:prep_childenv() for context */ + for (e = deltaenv; e && *e; e++) { + struct strbuf key = STRBUF_INIT; + char *equals = strchr(*e, '='); + + if (equals) { + strbuf_add(&key, *e, equals - *e); + string_list_insert(&envs, key.buf)->util = equals + 1; + } else { + string_list_insert(&envs, *e)->util = NULL; + } + strbuf_release(&key); + } + + /* "unset X Y...;" */ + for (i = 0; i < envs.nr; i++) { + const char *var = envs.items[i].string; + const char *val = envs.items[i].util; + + if (val || !getenv(var)) + continue; + + if (!printed_unset) { + strbuf_addstr(dst, " unset"); + printed_unset = 1; + } + strbuf_addf(dst, " %s", var); + } + if (printed_unset) + strbuf_addch(dst, ';'); + + /* ... followed by "A=B C=D ..." */ + for (i = 0; i < envs.nr; i++) { + const char *var = envs.items[i].string; + const char *val = envs.items[i].util; + const char *oldval; + + if (!val) + continue; + + oldval = getenv(var); + if (oldval && !strcmp(val, oldval)) + continue; + + strbuf_addf(dst, " %s=", var); + sq_quote_buf_pretty(dst, val); + } + string_list_clear(&envs, 0); +} + +static void trace_run_command(const struct child_process *cp) +{ + struct strbuf buf = STRBUF_INIT; + + if (!trace_want(&trace_default_key)) + return; + + strbuf_addstr(&buf, "trace: run_command:"); + if (cp->dir) { + strbuf_addstr(&buf, " cd "); + sq_quote_buf_pretty(&buf, cp->dir); + strbuf_addch(&buf, ';'); + } + /* + * The caller is responsible for initializing cp->env from + * cp->env_array if needed. We only check one place. + */ + if (cp->env) + trace_add_env(&buf, cp->env); + if (cp->git_cmd) + strbuf_addstr(&buf, " git"); + sq_quote_argv_pretty(&buf, cp->argv); + + trace_printf("%s", buf.buf); + strbuf_release(&buf); +} + int start_command(struct child_process *cmd) { int need_in, need_out, need_err; @@ -624,7 +712,8 @@ fail_pipe: cmd->err = fderr[0]; } - trace_argv_printf(cmd->argv, "trace: run_command:"); + trace_run_command(cmd); + fflush(NULL); #ifndef GIT_WINDOWS_NATIVE @@ -636,6 +725,12 @@ fail_pipe: struct child_err cerr; struct atfork_state as; + if (prepare_cmd(&argv, cmd) < 0) { + failed_errno = errno; + cmd->pid = -1; + goto end_of_spawn; + } + if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; @@ -646,7 +741,6 @@ fail_pipe: set_cloexec(null_fd); } - prepare_cmd(&argv, cmd); childenv = prep_childenv(cmd->env); atfork_prepare(&as); @@ -774,6 +868,8 @@ fail_pipe: argv_array_clear(&argv); free(childenv); } +end_of_spawn: + #else { int fhin = 0, fhout = 1, fherr = 2; @@ -881,7 +977,7 @@ int run_command(struct child_process *cmd) int code; if (cmd->out < 0 || cmd->err < 0) - die("BUG: run_command with a pipe can cause deadlock"); + BUG("run_command with a pipe can cause deadlock"); code = start_command(cmd); if (code) @@ -1471,7 +1567,7 @@ static void pp_init(struct parallel_processes *pp, pp->data = data; if (!get_next_task) - die("BUG: you need to specify a get_next_task function"); + BUG("you need to specify a get_next_task function"); pp->get_next_task = get_next_task; pp->start_failure = start_failure ? start_failure : default_start_failure; @@ -1533,7 +1629,7 @@ static int pp_start_one(struct parallel_processes *pp) if (pp->children[i].state == GIT_CP_FREE) break; if (i == pp->max_processes) - die("BUG: bookkeeping is hard"); + BUG("bookkeeping is hard"); code = pp->get_next_task(&pp->children[i].process, &pp->children[i].err,