new-command.txt: correct the command description file
[gitweb.git] / run-command.c
index 0b432cc9713251b4d116bedf52ae1f51bd9d80cf..84e4ce66e9d3da22be318813d75cbb3a94b614e4 100644 (file)
@@ -4,10 +4,6 @@
 #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));
@@ -15,6 +11,12 @@ void child_process_init(struct child_process *child)
        argv_array_init(&child->env_array);
 }
 
+void child_process_clear(struct child_process *child)
+{
+       argv_array_clear(&child->args);
+       argv_array_clear(&child->env_array);
+}
+
 struct child_to_clean {
        pid_t pid;
        struct child_to_clean *next;
@@ -22,26 +24,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)
@@ -204,7 +207,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)
@@ -216,17 +218,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)
@@ -236,7 +227,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;
@@ -244,6 +235,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;
@@ -340,8 +333,7 @@ int start_command(struct child_process *cmd)
 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);
+                       child_process_clear(cmd);
                        errno = failed_errno;
                        return -1;
                }
@@ -366,11 +358,10 @@ int start_command(struct child_process *cmd)
                 * 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]);
@@ -454,7 +445,7 @@ int start_command(struct child_process *cmd)
                 * 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;
        }
@@ -527,8 +518,7 @@ int start_command(struct child_process *cmd)
                        close_pair(fderr);
                else if (cmd->err)
                        close(cmd->err);
-               argv_array_clear(&cmd->args);
-               argv_array_clear(&cmd->env_array);
+               child_process_clear(cmd);
                errno = failed_errno;
                return -1;
        }
@@ -553,15 +543,25 @@ int start_command(struct child_process *cmd)
 
 int finish_command(struct child_process *cmd)
 {
-       int ret = wait_or_whine(cmd->pid, cmd->argv[0]);
-       argv_array_clear(&cmd->args);
-       argv_array_clear(&cmd->env_array);
+       int ret = wait_or_whine(cmd->pid, cmd->argv[0], 0);
+       child_process_clear(cmd);
        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);
@@ -784,7 +784,7 @@ int start_async(struct async *async)
 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);
 
@@ -794,13 +794,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)
@@ -833,3 +835,19 @@ int run_hook_le(const char *const *env, const char *name, ...)
 
        return ret;
 }
+
+int capture_command(struct child_process *cmd, struct strbuf *buf, size_t hint)
+{
+       cmd->out = -1;
+       if (start_command(cmd) < 0)
+               return -1;
+
+       if (strbuf_read(buf, cmd->out, hint) < 0) {
+               close(cmd->out);
+               finish_command(cmd); /* throw away exit code */
+               return -1;
+       }
+
+       close(cmd->out);
+       return finish_command(cmd);
+}