From: Lars Schneider <larsxschneider@xxxxxxxxx> 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 <larsxschneider@xxxxxxxxx> --- 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