diff options
Diffstat (limited to 'run-command.c')
-rw-r--r-- | run-command.c | 150 |
1 files changed, 104 insertions, 46 deletions
diff --git a/run-command.c b/run-command.c index 761f0fde40..e17e456cda 100644 --- a/run-command.c +++ b/run-command.c @@ -4,14 +4,11 @@ #include "sigchain.h" #include "argv-array.h" -#ifndef SHELL_PATH -# define SHELL_PATH "/bin/sh" -#endif - void child_process_init(struct child_process *child) { memset(child, 0, sizeof(*child)); argv_array_init(&child->args); + argv_array_init(&child->env_array); } struct child_to_clean { @@ -21,26 +18,27 @@ struct child_to_clean { static struct child_to_clean *children_to_clean; static int installed_child_cleanup_handler; -static void cleanup_children(int sig) +static void cleanup_children(int sig, int in_signal) { while (children_to_clean) { struct child_to_clean *p = children_to_clean; children_to_clean = p->next; kill(p->pid, sig); - free(p); + if (!in_signal) + free(p); } } static void cleanup_children_on_signal(int sig) { - cleanup_children(sig); + cleanup_children(sig, 1); sigchain_pop(sig); raise(sig); } static void cleanup_children_on_exit(void) { - cleanup_children(SIGTERM); + cleanup_children(SIGTERM, 0); } static void mark_child_for_cleanup(pid_t pid) @@ -203,7 +201,6 @@ static int execv_shell_cmd(const char **argv) #endif #ifndef GIT_WINDOWS_NATIVE -static int child_err = 2; static int child_notifier = -1; static void notify_parent(void) @@ -215,17 +212,6 @@ static void notify_parent(void) */ xwrite(child_notifier, "", 1); } - -static NORETURN void die_child(const char *err, va_list params) -{ - vwritef(child_err, "fatal: ", err, params); - exit(128); -} - -static void error_child(const char *err, va_list params) -{ - vwritef(child_err, "error: ", err, params); -} #endif static inline void set_cloexec(int fd) @@ -235,7 +221,7 @@ static inline void set_cloexec(int fd) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); } -static int wait_or_whine(pid_t pid, const char *argv0) +static int wait_or_whine(pid_t pid, const char *argv0, int in_signal) { int status, code = -1; pid_t waiting; @@ -243,6 +229,8 @@ static int wait_or_whine(pid_t pid, const char *argv0) while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR) ; /* nothing */ + if (in_signal) + return 0; if (waiting < 0) { failed_errno = errno; @@ -287,6 +275,8 @@ int start_command(struct child_process *cmd) if (!cmd->argv) cmd->argv = cmd->args.argv; + if (!cmd->env) + cmd->env = cmd->env_array.argv; /* * In case of errors we must keep the promise to close FDs @@ -338,6 +328,7 @@ fail_pipe: error("cannot create %s pipe for %s: %s", str, cmd->argv[0], strerror(failed_errno)); argv_array_clear(&cmd->args); + argv_array_clear(&cmd->env_array); errno = failed_errno; return -1; } @@ -362,11 +353,10 @@ fail_pipe: * in subsequent call paths use the parent's stderr. */ if (cmd->no_stderr || need_err) { - child_err = dup(2); + int child_err = dup(2); set_cloexec(child_err); + set_error_handle(fdopen(child_err, "w")); } - set_die_routine(die_child); - set_error_routine(error_child); close(notify_pipe[0]); set_cloexec(notify_pipe[1]); @@ -450,7 +440,7 @@ fail_pipe: * At this point we know that fork() succeeded, but execvp() * failed. Errors have been reported to our stderr. */ - wait_or_whine(cmd->pid, cmd->argv[0]); + wait_or_whine(cmd->pid, cmd->argv[0], 0); failed_errno = errno; cmd->pid = -1; } @@ -524,6 +514,7 @@ fail_pipe: else if (cmd->err) close(cmd->err); argv_array_clear(&cmd->args); + argv_array_clear(&cmd->env_array); errno = failed_errno; return -1; } @@ -548,14 +539,26 @@ fail_pipe: int finish_command(struct child_process *cmd) { - int ret = wait_or_whine(cmd->pid, cmd->argv[0]); + int ret = wait_or_whine(cmd->pid, cmd->argv[0], 0); argv_array_clear(&cmd->args); + argv_array_clear(&cmd->env_array); return ret; } +int finish_command_in_signal(struct child_process *cmd) +{ + return wait_or_whine(cmd->pid, cmd->argv[0], 1); +} + + int run_command(struct child_process *cmd) { - int code = start_command(cmd); + int code; + + if (cmd->out < 0 || cmd->err < 0) + die("BUG: run_command with a pipe can cause deadlock"); + + code = start_command(cmd); if (code) return code; return finish_command(cmd); @@ -601,7 +604,7 @@ static NORETURN void die_async(const char *err, va_list params) { vreportf("fatal: ", err, params); - if (!pthread_equal(main_thread, pthread_self())) { + if (in_async()) { struct async *async = pthread_getspecific(async_key); if (async->proc_in >= 0) close(async->proc_in); @@ -620,6 +623,58 @@ static int async_die_is_recursing(void) return ret != NULL; } +int in_async(void) +{ + if (!main_thread_set) + return 0; /* no asyncs started yet */ + return !pthread_equal(main_thread, pthread_self()); +} + +#else + +static struct { + void (**handlers)(void); + size_t nr; + size_t alloc; +} git_atexit_hdlrs; + +static int git_atexit_installed; + +static void git_atexit_dispatch(void) +{ + size_t i; + + for (i=git_atexit_hdlrs.nr ; i ; i--) + git_atexit_hdlrs.handlers[i-1](); +} + +static void git_atexit_clear(void) +{ + free(git_atexit_hdlrs.handlers); + memset(&git_atexit_hdlrs, 0, sizeof(git_atexit_hdlrs)); + git_atexit_installed = 0; +} + +#undef atexit +int git_atexit(void (*handler)(void)) +{ + ALLOC_GROW(git_atexit_hdlrs.handlers, git_atexit_hdlrs.nr + 1, git_atexit_hdlrs.alloc); + git_atexit_hdlrs.handlers[git_atexit_hdlrs.nr++] = handler; + if (!git_atexit_installed) { + if (atexit(&git_atexit_dispatch)) + return -1; + git_atexit_installed = 1; + } + return 0; +} +#define atexit git_atexit + +static int process_is_async; +int in_async(void) +{ + return process_is_async; +} + #endif int start_async(struct async *async) @@ -678,6 +733,8 @@ int start_async(struct async *async) close(fdin[1]); if (need_out) close(fdout[0]); + git_atexit_clear(); + process_is_async = 1; exit(!!async->proc(proc_in, proc_out, async->data)); } @@ -738,7 +795,7 @@ error: int finish_async(struct async *async) { #ifdef NO_PTHREADS - return wait_or_whine(async->pid, "child process"); + return wait_or_whine(async->pid, "child process", 0); #else void *ret = (void *)(intptr_t)(-1); @@ -748,13 +805,15 @@ int finish_async(struct async *async) #endif } -char *find_hook(const char *name) +const char *find_hook(const char *name) { - char *path = git_path("hooks/%s", name); - if (access(path, X_OK) < 0) - path = NULL; + static struct strbuf path = STRBUF_INIT; - return path; + strbuf_reset(&path); + strbuf_git_path(&path, "hooks/%s", name); + if (access(path.buf, X_OK) < 0) + return NULL; + return path.buf; } int run_hook_ve(const char *const *env, const char *name, va_list args) @@ -788,19 +847,18 @@ int run_hook_le(const char *const *env, const char *name, ...) return ret; } -int run_hook_with_custom_index(const char *index_file, const char *name, ...) +int capture_command(struct child_process *cmd, struct strbuf *buf, size_t hint) { - const char *hook_env[3] = { NULL }; - char index[PATH_MAX]; - va_list args; - int ret; - - snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); - hook_env[0] = index; + cmd->out = -1; + if (start_command(cmd) < 0) + return -1; - va_start(args, name); - ret = run_hook_ve(hook_env, name, args); - va_end(args); + if (strbuf_read(buf, cmd->out, hint) < 0) { + close(cmd->out); + finish_command(cmd); /* throw away exit code */ + return -1; + } - return ret; + close(cmd->out); + return finish_command(cmd); } |