From: Lars Schneider <larsxschnei...@gmail.com>

The flag 'clean_on_exit' kills child processes spawned by Git on exit.
A hard kill like this might not be desired in all cases.

Add 'wait_on_exit' which closes the child's stdin on Git exit and waits
until the child process has terminated.

The flag is used in a subsequent patch.

Signed-off-by: Lars Schneider <larsxschnei...@gmail.com>
---
 run-command.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++--------
 run-command.h |  3 +++
 2 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/run-command.c b/run-command.c
index 3269362..96c54fe 100644
--- a/run-command.c
+++ b/run-command.c
@@ -21,6 +21,9 @@ void child_process_clear(struct child_process *child)

 struct child_to_clean {
        pid_t pid;
+       char *name;
+       int stdin;
+       int wait;
        struct child_to_clean *next;
 };
 static struct child_to_clean *children_to_clean;
@@ -28,12 +31,33 @@ static int installed_child_cleanup_handler;

 static void cleanup_children(int sig, int in_signal)
 {
+       int status;
+       struct child_to_clean *p = children_to_clean;
+
+       /* Close the the child's stdin as indicator that Git will exit soon */
+       while (p) {
+               if (p->wait)
+                       if (p->stdin > 0)
+                               close(p->stdin);
+               p = p->next;
+       }
+
        while (children_to_clean) {
-               struct child_to_clean *p = children_to_clean;
+               p = children_to_clean;
                children_to_clean = p->next;
+
+               if (p->wait) {
+                       fprintf(stderr, _("Waiting for '%s' to finish..."), 
p->name);
+                       while ((waitpid(p->pid, &status, 0)) < 0 && errno == 
EINTR)
+                               ;       /* nothing */
+                       fprintf(stderr, _("done!\n"));
+               }
+
                kill(p->pid, sig);
-               if (!in_signal)
+               if (!in_signal) {
+                       free(p->name);
                        free(p);
+               }
        }
 }

@@ -49,10 +73,16 @@ static void cleanup_children_on_exit(void)
        cleanup_children(SIGTERM, 0);
 }

-static void mark_child_for_cleanup(pid_t pid)
+static void mark_child_for_cleanup(pid_t pid, const char *name, int stdin, int 
wait)
 {
        struct child_to_clean *p = xmalloc(sizeof(*p));
        p->pid = pid;
+       p->wait = wait;
+       p->stdin = stdin;
+       if (name)
+               p->name = xstrdup(name);
+       else
+               p->name = "process";
        p->next = children_to_clean;
        children_to_clean = p;

@@ -63,6 +93,13 @@ static void mark_child_for_cleanup(pid_t pid)
        }
 }

+#ifdef NO_PTHREADS
+static void mark_child_for_cleanup_no_wait(pid_t pid, const char *name, int 
timeout, int stdin)
+{
+       mark_child_for_cleanup(pid, NULL, 0, 0);
+}
+#endif
+
 static void clear_child_for_cleanup(pid_t pid)
 {
        struct child_to_clean **pp;
@@ -421,8 +458,9 @@ int start_command(struct child_process *cmd)
        }
        if (cmd->pid < 0)
                error_errno("cannot fork() for %s", cmd->argv[0]);
-       else if (cmd->clean_on_exit)
-               mark_child_for_cleanup(cmd->pid);
+       else if (cmd->clean_on_exit || cmd->wait_on_exit)
+               mark_child_for_cleanup(
+                       cmd->pid, cmd->argv[0], cmd->in, cmd->wait_on_exit);

        /*
         * Wait for child's execvp. If the execvp succeeds (or if fork()
@@ -482,8 +520,9 @@ int start_command(struct child_process *cmd)
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
                error_errno("cannot spawn %s", cmd->argv[0]);
-       if (cmd->clean_on_exit && cmd->pid >= 0)
-               mark_child_for_cleanup(cmd->pid);
+       if ((cmd->clean_on_exit || cmd->wait_on_exit) && cmd->pid >= 0)
+               mark_child_for_cleanup(
+                       cmd->pid, cmd->argv[0], cmd->in, 
cmd->clean_on_exit_timeout);

        argv_array_clear(&nargv);
        cmd->argv = sargv;
@@ -765,7 +804,7 @@ int start_async(struct async *async)
                exit(!!async->proc(proc_in, proc_out, async->data));
        }

-       mark_child_for_cleanup(async->pid);
+       mark_child_for_cleanup_no_wait(async->pid);

        if (need_in)
                close(fdin[0]);
diff --git a/run-command.h b/run-command.h
index cf29a31..f7b9907 100644
--- a/run-command.h
+++ b/run-command.h
@@ -42,7 +42,10 @@ struct child_process {
        unsigned silent_exec_failure:1;
        unsigned stdout_to_stderr:1;
        unsigned use_shell:1;
+        /* kill the child on Git exit */
        unsigned clean_on_exit:1;
+       /* close the child's stdin on Git exit and wait until it terminates */
+       unsigned wait_on_exit:1;
 };

 #define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }
--
2.10.0

Reply via email to