Merge branch 'jc/maint-request-pull-for-tag'
[gitweb.git] / run-command.c
index a2796c4caecc0fae6ea1a95b2c965b92d98f8a31..1db8abf9843516576f30f8105bbfdd66487db6e1 100644 (file)
@@ -1,6 +1,65 @@
 #include "cache.h"
 #include "run-command.h"
 #include "exec_cmd.h"
+#include "sigchain.h"
+#include "argv-array.h"
+
+struct child_to_clean {
+       pid_t pid;
+       struct child_to_clean *next;
+};
+static struct child_to_clean *children_to_clean;
+static int installed_child_cleanup_handler;
+
+static void cleanup_children(int sig)
+{
+       while (children_to_clean) {
+               struct child_to_clean *p = children_to_clean;
+               children_to_clean = p->next;
+               kill(p->pid, sig);
+               free(p);
+       }
+}
+
+static void cleanup_children_on_signal(int sig)
+{
+       cleanup_children(sig);
+       sigchain_pop(sig);
+       raise(sig);
+}
+
+static void cleanup_children_on_exit(void)
+{
+       cleanup_children(SIGTERM);
+}
+
+static void mark_child_for_cleanup(pid_t pid)
+{
+       struct child_to_clean *p = xmalloc(sizeof(*p));
+       p->pid = pid;
+       p->next = children_to_clean;
+       children_to_clean = p;
+
+       if (!installed_child_cleanup_handler) {
+               atexit(cleanup_children_on_exit);
+               sigchain_push_common(cleanup_children_on_signal);
+               installed_child_cleanup_handler = 1;
+       }
+}
+
+static void clear_child_for_cleanup(pid_t pid)
+{
+       struct child_to_clean **last, *p;
+
+       last = &children_to_clean;
+       for (p = children_to_clean; p; p = p->next) {
+               if (p->pid == pid) {
+                       *last = p->next;
+                       free(p);
+                       return;
+               }
+       }
+}
 
 static inline void close_pair(int fd[2])
 {
@@ -129,6 +188,9 @@ static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
        } else {
                error("waitpid is confused (%s)", argv0);
        }
+
+       clear_child_for_cleanup(pid);
+
        errno = failed_errno;
        return code;
 }
@@ -291,6 +353,8 @@ int start_command(struct child_process *cmd)
        if (cmd->pid < 0)
                error("cannot fork() for %s: %s", cmd->argv[0],
                        strerror(failed_errno = errno));
+       else if (cmd->clean_on_exit)
+               mark_child_for_cleanup(cmd->pid);
 
        /*
         * Wait for child's execvp. If the execvp succeeds (or if fork()
@@ -311,6 +375,7 @@ int start_command(struct child_process *cmd)
                cmd->pid = -1;
        }
        close(notify_pipe[0]);
+
 }
 #else
 {
@@ -355,6 +420,8 @@ int start_command(struct child_process *cmd)
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
                error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
+       if (cmd->clean_on_exit && cmd->pid >= 0)
+               mark_child_for_cleanup(cmd->pid);
 
        if (cmd->env)
                free_environ(env);
@@ -430,6 +497,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd,
        cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
        cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
        cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0;
+       cmd->clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;
 }
 
 int run_command_v_opt(const char **argv, int opt)
@@ -539,6 +607,8 @@ int start_async(struct async *async)
                exit(!!async->proc(proc_in, proc_out, async->data));
        }
 
+       mark_child_for_cleanup(async->pid);
+
        if (need_in)
                close(fdin[0]);
        else if (async->in)
@@ -605,26 +675,23 @@ int finish_async(struct async *async)
 int run_hook(const char *index_file, const char *name, ...)
 {
        struct child_process hook;
-       const char **argv = NULL, *env[2];
+       struct argv_array argv = ARGV_ARRAY_INIT;
+       const char *p, *env[2];
        char index[PATH_MAX];
        va_list args;
        int ret;
-       size_t i = 0, alloc = 0;
 
        if (access(git_path("hooks/%s", name), X_OK) < 0)
                return 0;
 
        va_start(args, name);
-       ALLOC_GROW(argv, i + 1, alloc);
-       argv[i++] = git_path("hooks/%s", name);
-       while (argv[i-1]) {
-               ALLOC_GROW(argv, i + 1, alloc);
-               argv[i++] = va_arg(args, const char *);
-       }
+       argv_array_push(&argv, git_path("hooks/%s", name));
+       while ((p = va_arg(args, const char *)))
+               argv_array_push(&argv, p);
        va_end(args);
 
        memset(&hook, 0, sizeof(hook));
-       hook.argv = argv;
+       hook.argv = argv.argv;
        hook.no_stdin = 1;
        hook.stdout_to_stderr = 1;
        if (index_file) {
@@ -635,6 +702,6 @@ int run_hook(const char *index_file, const char *name, ...)
        }
 
        ret = run_command(&hook);
-       free(argv);
+       argv_array_clear(&argv);
        return ret;
 }