diff options
author | Johannes Sixt <j6t@kdbg.org> | 2010-01-10 14:11:22 +0100 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2010-01-10 10:15:03 -0800 |
commit | 2b541bf8be2bbd6cc8daf8e3d5d4a8ee30b2ce4e (patch) | |
tree | a96beedf645609deb877c8b6180bae4df1fa658e /run-command.c | |
parent | ab0b41daf62ec3076e980fcad492b1997b35f22b (diff) | |
download | git-2b541bf8be2bbd6cc8daf8e3d5d4a8ee30b2ce4e.tar.gz |
start_command: detect execvp failures early
Previously, failures during execvp could be detected only by
finish_command. However, in some situations it is beneficial for the
parent process to know earlier that the child process will not run.
The idea to use a pipe to signal failures to the parent process and
the test case were lifted from patches by Ilari Liusvaara.
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'run-command.c')
-rw-r--r-- | run-command.c | 47 |
1 files changed, 46 insertions, 1 deletions
diff --git a/run-command.c b/run-command.c index dccac37c4b..efe9fe4138 100644 --- a/run-command.c +++ b/run-command.c @@ -17,6 +17,12 @@ static inline void dup_devnull(int to) #ifndef WIN32 static int child_err = 2; +static int child_notifier = -1; + +static void notify_parent(void) +{ + write(child_notifier, "", 1); +} static NORETURN void die_child(const char *err, va_list params) { @@ -142,6 +148,11 @@ fail_pipe: trace_argv_printf(cmd->argv, "trace: run_command:"); #ifndef WIN32 +{ + int notify_pipe[2]; + if (pipe(notify_pipe)) + notify_pipe[0] = notify_pipe[1] = -1; + fflush(NULL); cmd->pid = fork(); if (!cmd->pid) { @@ -156,6 +167,11 @@ fail_pipe: } set_die_routine(die_child); + close(notify_pipe[0]); + set_cloexec(notify_pipe[1]); + child_notifier = notify_pipe[1]; + atexit(notify_parent); + if (cmd->no_stdin) dup_devnull(0); else if (need_in) { @@ -196,8 +212,16 @@ fail_pipe: unsetenv(*cmd->env); } } - if (cmd->preexec_cb) + if (cmd->preexec_cb) { + /* + * We cannot predict what the pre-exec callback does. + * Forgo parent notification. + */ + close(child_notifier); + child_notifier = -1; + cmd->preexec_cb(); + } if (cmd->git_cmd) { execv_git_cmd(cmd->argv); } else { @@ -215,6 +239,27 @@ fail_pipe: if (cmd->pid < 0) error("cannot fork() for %s: %s", cmd->argv[0], strerror(failed_errno = errno)); + + /* + * Wait for child's execvp. If the execvp succeeds (or if fork() + * failed), EOF is seen immediately by the parent. Otherwise, the + * child process sends a single byte. + * Note that use of this infrastructure is completely advisory, + * therefore, we keep error checks minimal. + */ + close(notify_pipe[1]); + if (read(notify_pipe[0], ¬ify_pipe[1], 1) == 1) { + /* + * 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], + cmd->silent_exec_failure); + failed_errno = errno; + cmd->pid = -1; + } + close(notify_pipe[0]); +} #else { int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ |