run-command: add clean_on_exit_handler

Some processes might want to perform cleanup tasks before Git kills them
due to the 'clean_on_exit' flag. Let's give them an interface for doing
this. The feature is used in a subsequent patch.

Please note, that the cleanup callback is not executed if Git dies of a
signal. The reason is that only "async-signal-safe" functions would be
allowed to be call in that case. Since we cannot control what functions
the callback will use, we will not support the case. See 507d7804 for
more details.

Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Lars Schneider <larsxschneider@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Lars Schneider 2016-10-16 16:20:28 -07:00 коммит произвёл Junio C Hamano
Родитель b992fe104e
Коммит ac2fbaa674
2 изменённых файлов: 20 добавлений и 4 удалений

Просмотреть файл

@ -21,6 +21,7 @@ void child_process_clear(struct child_process *child)
struct child_to_clean { struct child_to_clean {
pid_t pid; pid_t pid;
struct child_process *process;
struct child_to_clean *next; struct child_to_clean *next;
}; };
static struct child_to_clean *children_to_clean; static struct child_to_clean *children_to_clean;
@ -31,6 +32,18 @@ static void cleanup_children(int sig, int in_signal)
while (children_to_clean) { while (children_to_clean) {
struct child_to_clean *p = children_to_clean; struct child_to_clean *p = children_to_clean;
children_to_clean = p->next; children_to_clean = p->next;
if (p->process && !in_signal) {
struct child_process *process = p->process;
if (process->clean_on_exit_handler) {
trace_printf(
"trace: run_command: running exit handler for pid %"
PRIuMAX, (uintmax_t)p->pid
);
process->clean_on_exit_handler(process);
}
}
kill(p->pid, sig); kill(p->pid, sig);
if (!in_signal) if (!in_signal)
free(p); free(p);
@ -49,10 +62,11 @@ static void cleanup_children_on_exit(void)
cleanup_children(SIGTERM, 0); cleanup_children(SIGTERM, 0);
} }
static void mark_child_for_cleanup(pid_t pid) static void mark_child_for_cleanup(pid_t pid, struct child_process *process)
{ {
struct child_to_clean *p = xmalloc(sizeof(*p)); struct child_to_clean *p = xmalloc(sizeof(*p));
p->pid = pid; p->pid = pid;
p->process = process;
p->next = children_to_clean; p->next = children_to_clean;
children_to_clean = p; children_to_clean = p;
@ -422,7 +436,7 @@ fail_pipe:
if (cmd->pid < 0) if (cmd->pid < 0)
error_errno("cannot fork() for %s", cmd->argv[0]); error_errno("cannot fork() for %s", cmd->argv[0]);
else if (cmd->clean_on_exit) else if (cmd->clean_on_exit)
mark_child_for_cleanup(cmd->pid); mark_child_for_cleanup(cmd->pid, cmd);
/* /*
* Wait for child's execvp. If the execvp succeeds (or if fork() * Wait for child's execvp. If the execvp succeeds (or if fork()
@ -483,7 +497,7 @@ fail_pipe:
if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT)) if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
error_errno("cannot spawn %s", cmd->argv[0]); error_errno("cannot spawn %s", cmd->argv[0]);
if (cmd->clean_on_exit && cmd->pid >= 0) if (cmd->clean_on_exit && cmd->pid >= 0)
mark_child_for_cleanup(cmd->pid); mark_child_for_cleanup(cmd->pid, cmd);
argv_array_clear(&nargv); argv_array_clear(&nargv);
cmd->argv = sargv; cmd->argv = sargv;
@ -765,7 +779,7 @@ int start_async(struct async *async)
exit(!!async->proc(proc_in, proc_out, async->data)); exit(!!async->proc(proc_in, proc_out, async->data));
} }
mark_child_for_cleanup(async->pid); mark_child_for_cleanup(async->pid, NULL);
if (need_in) if (need_in)
close(fdin[0]); close(fdin[0]);

Просмотреть файл

@ -43,6 +43,8 @@ struct child_process {
unsigned stdout_to_stderr:1; unsigned stdout_to_stderr:1;
unsigned use_shell:1; unsigned use_shell:1;
unsigned clean_on_exit:1; unsigned clean_on_exit:1;
void (*clean_on_exit_handler)(struct child_process *process);
void *clean_on_exit_handler_cbdata;
}; };
#define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT } #define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }