From: Junio C Hamano Date: Tue, 3 Feb 2009 08:26:12 +0000 (-0800) Subject: Merge branch 'jk/maint-cleanup-after-exec-failure' X-Git-Tag: v1.6.2-rc0~37 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/1487eb68f7fd145151caa9a11ee00283629b75ef?ds=inline;hp=-c Merge branch 'jk/maint-cleanup-after-exec-failure' * jk/maint-cleanup-after-exec-failure: git: use run_command() to execute dashed externals run_command(): help callers distinguish errors run_command(): handle missing command errors more gracefully git: s/run_command/run_builtin/ --- 1487eb68f7fd145151caa9a11ee00283629b75ef diff --combined git.c index 320cb43564,af747613f0..c2b181ed78 --- a/git.c +++ b/git.c @@@ -2,6 -2,7 +2,7 @@@ #include "exec_cmd.h" #include "cache.h" #include "quote.h" + #include "run-command.h" const char git_usage_string[] = "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; @@@ -158,7 -159,7 +159,7 @@@ static int handle_alias(int *argcp, con if (ret >= 0 && WIFEXITED(ret) && WEXITSTATUS(ret) != 127) exit(WEXITSTATUS(ret)); - die("Failed to run '%s' when expanding alias '%s'\n", + die("Failed to run '%s' when expanding alias '%s'", alias_string + 1, alias_command); } count = split_cmdline(alias_string, &new_argv); @@@ -219,7 -220,7 +220,7 @@@ struct cmd_struct int option; }; - static int run_command(struct cmd_struct *p, int argc, const char **argv) + static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; @@@ -384,7 -385,7 +385,7 @@@ static void handle_internal_command(in struct cmd_struct *p = commands+i; if (strcmp(p->cmd, cmd)) continue; - exit(run_command(p, argc, argv)); + exit(run_builtin(p, argc, argv)); } } @@@ -392,6 -393,7 +393,7 @@@ static void execv_dashed_external(cons { struct strbuf cmd = STRBUF_INIT; const char *tmp; + int status; strbuf_addf(&cmd, "git-%s", argv[0]); @@@ -406,48 -408,44 +408,55 @@@ trace_argv_printf(argv, "trace: exec:"); - /* execvp() can only ever return if it fails */ - execvp(cmd.buf, (char **)argv); - - trace_printf("trace: exec failed: %s\n", strerror(errno)); + /* + * if we fail because the command is not found, it is + * OK to return. Otherwise, we just pass along the status code. + */ + status = run_command_v_opt(argv, 0); + if (status != -ERR_RUN_COMMAND_EXEC) { + if (IS_RUN_COMMAND_ERR(status)) + die("unable to run '%s'", argv[0]); + exit(-status); + } + errno = ENOENT; /* as if we called execvp */ argv[0] = tmp; strbuf_release(&cmd); } - -int main(int argc, const char **argv) +static int run_argv(int *argcp, const char ***argv) { - const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help"; - char *slash = (char *)cmd + strlen(cmd); int done_alias = 0; - /* - * Take the basename of argv[0] as the command - * name, and the dirname as the default exec_path - * if we don't have anything better. - */ - do - --slash; - while (cmd <= slash && !is_dir_sep(*slash)); - if (cmd <= slash) { - *slash++ = 0; - git_set_argv0_path(cmd); - cmd = slash; + while (1) { + /* See if it's an internal command */ + handle_internal_command(*argcp, *argv); + + /* .. then try the external ones */ + execv_dashed_external(*argv); + + /* It could be an alias -- this works around the insanity + * of overriding "git log" with "git show" by having + * alias.log = show + */ + if (done_alias || !handle_alias(argcp, argv)) + break; + done_alias = 1; } + return done_alias; +} + + +int main(int argc, const char **argv) +{ + const char *cmd; + + cmd = git_extract_argv0_path(argv[0]); + if (!cmd) + cmd = "git-help"; + /* * "git-xxxx" is the same as "git xxxx", but we obviously: * @@@ -491,22 -489,31 +500,22 @@@ setup_path(); while (1) { - /* See if it's an internal command */ - handle_internal_command(argc, argv); - - /* .. then try the external ones */ - execv_dashed_external(argv); - - /* It could be an alias -- this works around the insanity - * of overriding "git log" with "git show" by having - * alias.log = show - */ - if (done_alias || !handle_alias(&argc, &argv)) + static int done_help = 0; + static int was_alias = 0; + was_alias = run_argv(&argc, &argv); + if (errno != ENOENT) break; - done_alias = 1; - } - - if (errno == ENOENT) { - if (done_alias) { + if (was_alias) { fprintf(stderr, "Expansion of alias '%s' failed; " "'%s' is not a git-command\n", cmd, argv[0]); exit(1); } - argv[0] = help_unknown_cmd(cmd); - handle_internal_command(argc, argv); - execv_dashed_external(argv); + if (!done_help) { + cmd = argv[0] = help_unknown_cmd(cmd); + done_help = 1; + } else + break; } fprintf(stderr, "Failed to run command '%s': %s\n", diff --combined run-command.c index db9ce59204,44fccc9d5e..b05c734d05 --- a/run-command.c +++ b/run-command.c @@@ -118,7 -118,9 +118,9 @@@ int start_command(struct child_process } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } - die("exec %s failed.", cmd->argv[0]); + trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0], + strerror(errno)); + exit(127); } #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ @@@ -187,6 -189,7 +189,7 @@@ #endif if (cmd->pid < 0) { + int err = errno; if (need_in) close_pair(fdin); else if (cmd->in) @@@ -197,7 -200,9 +200,9 @@@ close(cmd->out); if (need_err) close_pair(fderr); - return -ERR_RUN_COMMAND_FORK; + return err == ENOENT ? + -ERR_RUN_COMMAND_EXEC : + -ERR_RUN_COMMAND_FORK; } if (need_in) @@@ -236,9 -241,14 +241,14 @@@ static int wait_or_whine(pid_t pid if (!WIFEXITED(status)) return -ERR_RUN_COMMAND_WAITPID_NOEXIT; code = WEXITSTATUS(status); - if (code) + switch (code) { + case 127: + return -ERR_RUN_COMMAND_EXEC; + case 0: + return 0; + default: return -code; - return 0; + } } } @@@ -342,48 -352,3 +352,48 @@@ int finish_async(struct async *async #endif return ret; } + +int run_hook(const char *index_file, const char *name, ...) +{ + struct child_process hook; + const char **argv = NULL, *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 *); + } + va_end(args); + + memset(&hook, 0, sizeof(hook)); + hook.argv = argv; + hook.no_stdin = 1; + hook.stdout_to_stderr = 1; + if (index_file) { + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); + env[0] = index; + env[1] = NULL; + hook.env = env; + } + + ret = start_command(&hook); + free(argv); + if (ret) { + warning("Could not spawn %s", argv[0]); + return ret; + } + ret = finish_command(&hook); + if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL) + warning("%s exited due to uncaught signal", argv[0]); + + return ret; +} diff --combined run-command.h index 0211e1d471,e90d9282ff..15e870a65e --- a/run-command.h +++ b/run-command.h @@@ -10,6 -10,7 +10,7 @@@ enum ERR_RUN_COMMAND_WAITPID_SIGNAL, ERR_RUN_COMMAND_WAITPID_NOEXIT, }; + #define IS_RUN_COMMAND_ERR(x) ((x) <= -ERR_RUN_COMMAND_FORK) struct child_process { const char **argv; @@@ -49,8 -50,6 +50,8 @@@ int start_command(struct child_process int finish_command(struct child_process *); int run_command(struct child_process *); +extern int run_hook(const char *index_file, const char *name, ...); + #define RUN_COMMAND_NO_STDIN 1 #define RUN_GIT_CMD 2 /*If this is to be git sub-command */ #define RUN_COMMAND_STDOUT_TO_STDERR 4