static void execv_dashed_external(const char **argv) { struct child_process cmd = CHILD_PROCESS_INIT; int status; if (get_super_prefix()) die("%s doesn't support --super-prefix", argv[0]); if (use_pager == -1 && !is_builtin(argv[0])) use_pager = check_pager_config(argv[0]); commit_pager_choice(); argv_array_pushf(&cmd.args, "git-%s", argv[0]); argv_array_pushv(&cmd.args, argv + 1); cmd.clean_on_exit = 1; cmd.wait_after_clean = 1; cmd.silent_exec_failure = 1; trace_argv_printf(cmd.args.argv, "trace: exec:"); /* * If we fail because the command is not found, it is * OK to return. Otherwise, we just pass along the status code, * or our usual generic code if we were not even able to exec * the program. */ status = run_command(&cmd); if (status >= 0) exit(status); else if (errno != ENOENT) exit(128); }
static void execv_dashed_external(const char **argv) { struct strbuf cmd = STRBUF_INIT; const char *tmp; int status; commit_pager_choice(); strbuf_addf(&cmd, "git-%s", argv[0]); /* * argv[0] must be the git command, but the argv array * belongs to the caller, and may be reused in * subsequent loop iterations. Save argv[0] and * restore it on error. */ tmp = argv[0]; argv[0] = cmd.buf; trace_argv_printf(argv, "trace: exec:"); /* * 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, RUN_SILENT_EXEC_FAILURE); if (status >= 0 || errno != ENOENT) exit(status); argv[0] = tmp; strbuf_release(&cmd); }
static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status, help; struct stat st; const char *prefix; prefix = NULL; help = argc == 2 && !strcmp(argv[1], "-h"); if (!help) { if (p->option & RUN_SETUP) prefix = setup_git_directory(); else if (p->option & RUN_SETUP_GENTLY) { int nongit_ok; prefix = setup_git_directory_gently(&nongit_ok); } if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) use_pager = check_pager_config(p->cmd); if (use_pager == -1 && p->option & USE_PAGER) use_pager = 1; if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) && startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */ trace_repo_setup(prefix); } commit_pager_choice(); if (!help && get_super_prefix()) { if (!(p->option & SUPPORT_SUPER_PREFIX)) die("%s doesn't support --super-prefix", p->cmd); if (prefix) die("can't use --super-prefix from a subdirectory"); } if (!help && p->option & NEED_WORK_TREE) setup_work_tree(); trace_argv_printf(argv, "trace: built-in: git"); status = p->fn(argc, argv, prefix); if (status) return status; /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) return 0; /* Ignore write errors for pipes and sockets.. */ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) return 0; /* Check for ENOSPC and EIO errors.. */ if (fflush(stdout)) die_errno("write failure on standard output"); if (ferror(stdout)) die("unknown write failure on standard output"); if (fclose(stdout)) die_errno("close failed on standard output"); return 0; }
static int execv_shell_cmd(const char **argv) { const char **nargv = prepare_shell_cmd(argv); trace_argv_printf(nargv, "trace: exec:"); execvp(nargv[0], (char **)nargv); free(nargv); return -1; }
static int run_argv(int *argcp, const char ***argv) { int done_alias = 0; while (1) { /* * If we tried alias and futzed with our environment, * it no longer is safe to invoke builtins directly in * general. We have to spawn them as dashed externals. * * NEEDSWORK: if we can figure out cases * where it is safe to do, we can avoid spawning a new * process. */ if (!done_alias) handle_builtin(*argcp, *argv); else if (get_builtin(**argv)) { struct argv_array args = ARGV_ARRAY_INIT; int i; if (get_super_prefix()) die("%s doesn't support --super-prefix", **argv); commit_pager_choice(); argv_array_push(&args, "git"); for (i = 0; i < *argcp; i++) argv_array_push(&args, (*argv)[i]); trace_argv_printf(args.argv, "trace: exec:"); /* * if we fail because the command is not found, it is * OK to return. Otherwise, we just pass along the status code. */ i = run_command_v_opt(args.argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT); if (i >= 0 || errno != ENOENT) exit(i); die("could not execute builtin %s", **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) break; if (!handle_alias(argcp, argv)) break; done_alias = 1; } return done_alias; }
static int execv_shell_cmd(const char **argv) { struct argv_array nargv = ARGV_ARRAY_INIT; prepare_shell_cmd(&nargv, argv); trace_argv_printf(nargv.argv, "trace: exec:"); sane_execvp(nargv.argv[0], (char **)nargv.argv); argv_array_clear(&nargv); return -1; }
int execv_git_cmd(const char **argv) { const char **nargv = prepare_git_cmd(argv); trace_argv_printf(nargv, "trace: exec:"); /* execvp() can only ever return if it fails */ execvp("git", (char **)nargv); trace_printf("trace: exec failed: %s\n", strerror(errno)); free(nargv); return -1; }
int execv_git_cmd(const char **argv) { struct argv_array nargv = ARGV_ARRAY_INIT; prepare_git_cmd(&nargv, argv); trace_argv_printf(nargv.argv, "trace: exec:"); /* execvp() can only ever return if it fails */ sane_execvp("git", (char **)nargv.argv); trace_printf("trace: exec failed: %s\n", strerror(errno)); argv_array_clear(&nargv); return -1; }
static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status, help; struct stat st; const char *prefix; prefix = NULL; help = argc == 2 && !strcmp(argv[1], "-h"); if (!help) { if (p->option & RUN_SETUP) prefix = setup_git_directory(); if (p->option & RUN_SETUP_GENTLY) { int nongit_ok; prefix = setup_git_directory_gently(&nongit_ok); } if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) use_pager = check_pager_config(p->cmd); if (use_pager == -1 && p->option & USE_PAGER) use_pager = 1; } commit_pager_choice(); if (!help && p->option & NEED_WORK_TREE) setup_work_tree(); trace_argv_printf(argv, "trace: built-in: git"); status = p->fn(argc, argv, prefix); if (status) return status; /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) return 0; /* Ignore write errors for pipes and sockets.. */ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) return 0; /* Check for ENOSPC and EIO errors.. */ if (fflush(stdout)) die_errno("write failure on standard output"); if (ferror(stdout)) die("unknown write failure on standard output"); if (fclose(stdout)) die_errno("close failed on standard output"); return 0; }
int start_command(struct child_process *cmd) { int need_in, need_out, need_err; int fdin[2], fdout[2], fderr[2]; /* * In case of errors we must keep the promise to close FDs * that have been passed in via ->in and ->out. */ need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { if (pipe(fdin) < 0) { if (cmd->out > 0) close(cmd->out); return -ERR_RUN_COMMAND_PIPE; } cmd->in = fdin[1]; } need_out = !cmd->no_stdout && !cmd->stdout_to_stderr && cmd->out < 0; if (need_out) { if (pipe(fdout) < 0) { if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); return -ERR_RUN_COMMAND_PIPE; } cmd->out = fdout[0]; } need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); return -ERR_RUN_COMMAND_PIPE; } cmd->err = fderr[0]; } trace_argv_printf(cmd->argv, "trace: run_command:"); #ifndef __MINGW32__ cmd->pid = fork(); if (!cmd->pid) { if (cmd->no_stdin) dup_devnull(0); else if (need_in) { dup2(fdin[0], 0); close_pair(fdin); } else if (cmd->in) { dup2(cmd->in, 0); close(cmd->in); } if (cmd->no_stderr) dup_devnull(2); else if (need_err) { dup2(fderr[1], 2); close_pair(fderr); } if (cmd->no_stdout) dup_devnull(1); else if (cmd->stdout_to_stderr) dup2(2, 1); else if (need_out) { dup2(fdout[1], 1); close_pair(fdout); } else if (cmd->out > 1) { dup2(cmd->out, 1); close(cmd->out); } if (cmd->dir && chdir(cmd->dir)) die("exec %s: cd to %s failed (%s)", cmd->argv[0], cmd->dir, strerror(errno)); if (cmd->env) { for (; *cmd->env; cmd->env++) { if (strchr(*cmd->env, '=')) putenv((char*)*cmd->env); else unsetenv(*cmd->env); } } if (cmd->git_cmd) { execv_git_cmd(cmd->argv); } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } die("exec %s failed.", cmd->argv[0]); } #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ const char *sargv0 = cmd->argv[0]; char **env = environ; struct strbuf git_cmd; if (cmd->no_stdin) { s0 = dup(0); dup_devnull(0); } else if (need_in) { s0 = dup(0); dup2(fdin[0], 0); } else if (cmd->in) { s0 = dup(0); dup2(cmd->in, 0); } if (cmd->no_stderr) { s2 = dup(2); dup_devnull(2); } else if (need_err) { s2 = dup(2); dup2(fderr[1], 2); } if (cmd->no_stdout) { s1 = dup(1); dup_devnull(1); } else if (cmd->stdout_to_stderr) { s1 = dup(1); dup2(2, 1); } else if (need_out) { s1 = dup(1); dup2(fdout[1], 1); } else if (cmd->out > 1) { s1 = dup(1); dup2(cmd->out, 1); } if (cmd->dir) die("chdir in start_command() not implemented"); if (cmd->env) { env = copy_environ(); for (; *cmd->env; cmd->env++) env = env_setenv(env, *cmd->env); } if (cmd->git_cmd) { strbuf_init(&git_cmd, 0); strbuf_addf(&git_cmd, "git-%s", cmd->argv[0]); cmd->argv[0] = git_cmd.buf; } cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env); if (cmd->env) free_environ(env); if (cmd->git_cmd) strbuf_release(&git_cmd); cmd->argv[0] = sargv0; if (s0 >= 0) dup2(s0, 0), close(s0); if (s1 >= 0) dup2(s1, 1), close(s1); if (s2 >= 0) dup2(s2, 2), close(s2); #endif if (cmd->pid < 0) { if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); if (need_err) close_pair(fderr); return -ERR_RUN_COMMAND_FORK; } if (need_in) close(fdin[0]); else if (cmd->in) close(cmd->in); if (need_out) close(fdout[1]); else if (cmd->out) close(cmd->out); if (need_err) close(fderr[1]); return 0; }
static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status, help; struct stat st; const char *prefix; prefix = NULL; help = argc == 2 && !strcmp(argv[1], "-h"); if (!help) { if (p->option & RUN_SETUP) prefix = setup_git_directory(); else if (p->option & RUN_SETUP_GENTLY) { int nongit_ok; prefix = setup_git_directory_gently(&nongit_ok); } if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) && !(p->option & DELAY_PAGER_CONFIG)) use_pager = check_pager_config(p->cmd); if (use_pager == -1 && p->option & USE_PAGER) use_pager = 1; if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) && startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */ trace_repo_setup(prefix); } commit_pager_choice(); if (!help && get_super_prefix()) { if (!(p->option & SUPPORT_SUPER_PREFIX)) die("%s doesn't support --super-prefix", p->cmd); } if (!help && p->option & NEED_WORK_TREE) setup_work_tree(); if (run_pre_command_hook(argv)) die("pre-command hook aborted command"); trace_argv_printf(argv, "trace: built-in: git"); /* * Validate the state of the cache entries in the index before and * after running the command. Validation is only performed if the * appropriate environment variable is set. */ validate_cache_entries(&the_index); exit_code = status = p->fn(argc, argv, prefix); validate_cache_entries(&the_index); if (status) return status; run_post_command_hook(); /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) return 0; /* Ignore write errors for pipes and sockets.. */ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) return 0; /* Check for ENOSPC and EIO errors.. */ if (fflush(stdout)) die_errno("write failure on standard output"); if (ferror(stdout)) die("unknown write failure on standard output"); if (fclose(stdout)) die_errno("close failed on standard output"); return 0; }
static int handle_alias(int *argcp, const char ***argv) { int envchanged = 0, ret = 0, saved_errno = errno; int count, option_count; const char **new_argv; const char *alias_command; char *alias_string; alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); if (alias_string) { if (alias_string[0] == '!') { struct child_process child = CHILD_PROCESS_INIT; int nongit_ok; /* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */ setup_git_directory_gently(&nongit_ok); commit_pager_choice(); child.use_shell = 1; argv_array_push(&child.args, alias_string + 1); argv_array_pushv(&child.args, (*argv) + 1); ret = run_command(&child); if (ret >= 0) /* normal exit */ exit(ret); die_errno("while expanding alias '%s': '%s'", alias_command, alias_string + 1); } count = split_cmdline(alias_string, &new_argv); if (count < 0) die("Bad alias.%s string: %s", alias_command, split_cmdline_strerror(count)); option_count = handle_options(&new_argv, &count, &envchanged); if (envchanged) die("alias '%s' changes environment variables.\n" "You can use '!git' in the alias to do this", alias_command); memmove(new_argv - option_count, new_argv, count * sizeof(char *)); new_argv -= option_count; if (count < 1) die("empty alias for %s", alias_command); if (!strcmp(alias_command, new_argv[0])) die("recursive alias: %s", alias_command); trace_argv_printf(new_argv, "trace: alias expansion: %s =>", alias_command); REALLOC_ARRAY(new_argv, count + *argcp); /* insert after command name */ memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp); *argv = new_argv; *argcp += count - 1; ret = 1; } errno = saved_errno; return ret; }
int start_command(struct child_process *cmd) { int need_in, need_out, need_err; int fdin[2], fdout[2], fderr[2]; int failed_errno; char *str; if (!cmd->argv) cmd->argv = cmd->args.argv; if (!cmd->env) cmd->env = cmd->env_array.argv; /* * In case of errors we must keep the promise to close FDs * that have been passed in via ->in and ->out. */ need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { if (pipe(fdin) < 0) { failed_errno = errno; if (cmd->out > 0) close(cmd->out); str = "standard input"; goto fail_pipe; } cmd->in = fdin[1]; } need_out = !cmd->no_stdout && !cmd->stdout_to_stderr && cmd->out < 0; if (need_out) { if (pipe(fdout) < 0) { failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); str = "standard output"; goto fail_pipe; } cmd->out = fdout[0]; } need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); str = "standard error"; fail_pipe: error("cannot create %s pipe for %s: %s", str, cmd->argv[0], strerror(failed_errno)); child_process_clear(cmd); errno = failed_errno; return -1; } cmd->err = fderr[0]; } trace_argv_printf(cmd->argv, "trace: run_command:"); fflush(NULL); #ifndef GIT_WINDOWS_NATIVE { int notify_pipe[2]; if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; cmd->pid = fork(); failed_errno = errno; if (!cmd->pid) { /* * Redirect the channel to write syscall error messages to * before redirecting the process's stderr so that all die() * in subsequent call paths use the parent's stderr. */ if (cmd->no_stderr || need_err) { int child_err = dup(2); set_cloexec(child_err); set_error_handle(fdopen(child_err, "w")); } 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) { dup2(fdin[0], 0); close_pair(fdin); } else if (cmd->in) { dup2(cmd->in, 0); close(cmd->in); } if (cmd->no_stderr) dup_devnull(2); else if (need_err) { dup2(fderr[1], 2); close_pair(fderr); } else if (cmd->err > 1) { dup2(cmd->err, 2); close(cmd->err); } if (cmd->no_stdout) dup_devnull(1); else if (cmd->stdout_to_stderr) dup2(2, 1); else if (need_out) { dup2(fdout[1], 1); close_pair(fdout); } else if (cmd->out > 1) { dup2(cmd->out, 1); close(cmd->out); } if (cmd->dir && chdir(cmd->dir)) die_errno("exec '%s': cd to '%s' failed", cmd->argv[0], cmd->dir); if (cmd->env) { for (; *cmd->env; cmd->env++) { if (strchr(*cmd->env, '=')) putenv((char *)*cmd->env); else unsetenv(*cmd->env); } } if (cmd->git_cmd) execv_git_cmd(cmd->argv); else if (cmd->use_shell) execv_shell_cmd(cmd->argv); else sane_execvp(cmd->argv[0], (char *const*) cmd->argv); if (errno == ENOENT) { if (!cmd->silent_exec_failure) error("cannot run %s: %s", cmd->argv[0], strerror(ENOENT)); exit(127); } else { die_errno("cannot exec '%s'", cmd->argv[0]); } } if (cmd->pid < 0) error("cannot fork() for %s: %s", cmd->argv[0], strerror(errno)); else if (cmd->clean_on_exit) mark_child_for_cleanup(cmd->pid); /* * 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], 0); failed_errno = errno; cmd->pid = -1; } close(notify_pipe[0]); } #else { int fhin = 0, fhout = 1, fherr = 2; const char **sargv = cmd->argv; struct argv_array nargv = ARGV_ARRAY_INIT; if (cmd->no_stdin) fhin = open("/dev/null", O_RDWR); else if (need_in) fhin = dup(fdin[0]); else if (cmd->in) fhin = dup(cmd->in); if (cmd->no_stderr) fherr = open("/dev/null", O_RDWR); else if (need_err) fherr = dup(fderr[1]); else if (cmd->err > 2) fherr = dup(cmd->err); if (cmd->no_stdout) fhout = open("/dev/null", O_RDWR); else if (cmd->stdout_to_stderr) fhout = dup(fherr); else if (need_out) fhout = dup(fdout[1]); else if (cmd->out > 1) fhout = dup(cmd->out); if (cmd->git_cmd) cmd->argv = prepare_git_cmd(&nargv, cmd->argv); else if (cmd->use_shell) cmd->argv = prepare_shell_cmd(&nargv, cmd->argv); cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env, cmd->dir, fhin, fhout, fherr); failed_errno = errno; if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT)) error("cannot spawn %s: %s", cmd->argv[0], strerror(errno)); if (cmd->clean_on_exit && cmd->pid >= 0) mark_child_for_cleanup(cmd->pid); argv_array_clear(&nargv); cmd->argv = sargv; if (fhin != 0) close(fhin); if (fhout != 1) close(fhout); if (fherr != 2) close(fherr); } #endif if (cmd->pid < 0) { if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); if (need_err) close_pair(fderr); else if (cmd->err) close(cmd->err); child_process_clear(cmd); errno = failed_errno; return -1; } if (need_in) close(fdin[0]); else if (cmd->in) close(cmd->in); if (need_out) close(fdout[1]); else if (cmd->out) close(cmd->out); if (need_err) close(fderr[1]); else if (cmd->err) close(cmd->err); return 0; }
int start_command(struct child_process *cmd) { int need_in, need_out, need_err; int fdin[2], fdout[2], fderr[2]; int failed_errno; char *str; if (!cmd->argv) cmd->argv = cmd->args.argv; if (!cmd->env) cmd->env = cmd->env_array.argv; /* * In case of errors we must keep the promise to close FDs * that have been passed in via ->in and ->out. */ need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { if (pipe(fdin) < 0) { failed_errno = errno; if (cmd->out > 0) close(cmd->out); str = "standard input"; goto fail_pipe; } cmd->in = fdin[1]; } need_out = !cmd->no_stdout && !cmd->stdout_to_stderr && cmd->out < 0; if (need_out) { if (pipe(fdout) < 0) { failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); str = "standard output"; goto fail_pipe; } cmd->out = fdout[0]; } need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); str = "standard error"; fail_pipe: error("cannot create %s pipe for %s: %s", str, cmd->argv[0], strerror(failed_errno)); child_process_clear(cmd); errno = failed_errno; return -1; } cmd->err = fderr[0]; } trace_argv_printf(cmd->argv, "trace: run_command:"); fflush(NULL); #ifndef GIT_WINDOWS_NATIVE { int notify_pipe[2]; int null_fd = -1; char **childenv; struct argv_array argv = ARGV_ARRAY_INIT; struct child_err cerr; struct atfork_state as; if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; if (cmd->no_stdin || cmd->no_stdout || cmd->no_stderr) { null_fd = open("/dev/null", O_RDWR | O_CLOEXEC); if (null_fd < 0) die_errno(_("open /dev/null failed")); set_cloexec(null_fd); } prepare_cmd(&argv, cmd); childenv = prep_childenv(cmd->env); atfork_prepare(&as); /* * NOTE: In order to prevent deadlocking when using threads special * care should be taken with the function calls made in between the * fork() and exec() calls. No calls should be made to functions which * require acquiring a lock (e.g. malloc) as the lock could have been * held by another thread at the time of forking, causing the lock to * never be released in the child process. This means only * Async-Signal-Safe functions are permitted in the child. */ cmd->pid = fork(); failed_errno = errno; if (!cmd->pid) { int sig; /* * Ensure the default die/error/warn routines do not get * called, they can take stdio locks and malloc. */ set_die_routine(child_die_fn); set_error_routine(child_error_fn); set_warn_routine(child_warn_fn); close(notify_pipe[0]); set_cloexec(notify_pipe[1]); child_notifier = notify_pipe[1]; if (cmd->no_stdin) child_dup2(null_fd, 0); else if (need_in) { child_dup2(fdin[0], 0); child_close_pair(fdin); } else if (cmd->in) { child_dup2(cmd->in, 0); child_close(cmd->in); } if (cmd->no_stderr) child_dup2(null_fd, 2); else if (need_err) { child_dup2(fderr[1], 2); child_close_pair(fderr); } else if (cmd->err > 1) { child_dup2(cmd->err, 2); child_close(cmd->err); } if (cmd->no_stdout) child_dup2(null_fd, 1); else if (cmd->stdout_to_stderr) child_dup2(2, 1); else if (need_out) { child_dup2(fdout[1], 1); child_close_pair(fdout); } else if (cmd->out > 1) { child_dup2(cmd->out, 1); child_close(cmd->out); } if (cmd->dir && chdir(cmd->dir)) child_die(CHILD_ERR_CHDIR); /* * restore default signal handlers here, in case * we catch a signal right before execve below */ for (sig = 1; sig < NSIG; sig++) { /* ignored signals get reset to SIG_DFL on execve */ if (signal(sig, SIG_DFL) == SIG_IGN) signal(sig, SIG_IGN); } if (sigprocmask(SIG_SETMASK, &as.old, NULL) != 0) child_die(CHILD_ERR_SIGPROCMASK); /* * Attempt to exec using the command and arguments starting at * argv.argv[1]. argv.argv[0] contains SHELL_PATH which will * be used in the event exec failed with ENOEXEC at which point * we will try to interpret the command using 'sh'. */ execve(argv.argv[1], (char *const *) argv.argv + 1, (char *const *) childenv); if (errno == ENOEXEC) execve(argv.argv[0], (char *const *) argv.argv, (char *const *) childenv); if (errno == ENOENT) { if (cmd->silent_exec_failure) child_die(CHILD_ERR_SILENT); child_die(CHILD_ERR_ENOENT); } else { child_die(CHILD_ERR_ERRNO); } } atfork_parent(&as); 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, cmd); /* * Wait for child's exec. If the exec succeeds (or if fork() * failed), EOF is seen immediately by the parent. Otherwise, the * child process sends a child_err struct. * Note that use of this infrastructure is completely advisory, * therefore, we keep error checks minimal. */ close(notify_pipe[1]); if (xread(notify_pipe[0], &cerr, sizeof(cerr)) == sizeof(cerr)) { /* * At this point we know that fork() succeeded, but exec() * failed. Errors have been reported to our stderr. */ wait_or_whine(cmd->pid, cmd->argv[0], 0); child_err_spew(cmd, &cerr); failed_errno = errno; cmd->pid = -1; } close(notify_pipe[0]); if (null_fd >= 0) close(null_fd); argv_array_clear(&argv); free(childenv); } #else { int fhin = 0, fhout = 1, fherr = 2; const char **sargv = cmd->argv; struct argv_array nargv = ARGV_ARRAY_INIT; if (cmd->no_stdin) fhin = open("/dev/null", O_RDWR); else if (need_in) fhin = dup(fdin[0]); else if (cmd->in) fhin = dup(cmd->in); if (cmd->no_stderr) fherr = open("/dev/null", O_RDWR); else if (need_err) fherr = dup(fderr[1]); else if (cmd->err > 2) fherr = dup(cmd->err); if (cmd->no_stdout) fhout = open("/dev/null", O_RDWR); else if (cmd->stdout_to_stderr) fhout = dup(fherr); else if (need_out) fhout = dup(fdout[1]); else if (cmd->out > 1) fhout = dup(cmd->out); if (cmd->git_cmd) cmd->argv = prepare_git_cmd(&nargv, cmd->argv); else if (cmd->use_shell) cmd->argv = prepare_shell_cmd(&nargv, cmd->argv); cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env, cmd->dir, fhin, fhout, fherr); 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, cmd); argv_array_clear(&nargv); cmd->argv = sargv; if (fhin != 0) close(fhin); if (fhout != 1) close(fhout); if (fherr != 2) close(fherr); } #endif if (cmd->pid < 0) { if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); if (need_err) close_pair(fderr); else if (cmd->err) close(cmd->err); child_process_clear(cmd); errno = failed_errno; return -1; } if (need_in) close(fdin[0]); else if (cmd->in) close(cmd->in); if (need_out) close(fdout[1]); else if (cmd->out) close(cmd->out); if (need_err) close(fderr[1]); else if (cmd->err) close(cmd->err); return 0; }
int start_command(struct child_process *cmd) { int need_in, need_out, need_err; int fdin[2], fdout[2], fderr[2]; int failed_errno = failed_errno; /* * In case of errors we must keep the promise to close FDs * that have been passed in via ->in and ->out. */ need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { if (pipe(fdin) < 0) { failed_errno = errno; if (cmd->out > 0) close(cmd->out); goto fail_pipe; } cmd->in = fdin[1]; } need_out = !cmd->no_stdout && !cmd->stdout_to_stderr && cmd->out < 0; if (need_out) { if (pipe(fdout) < 0) { failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); goto fail_pipe; } cmd->out = fdout[0]; } need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); fail_pipe: error("cannot create pipe for %s: %s", cmd->argv[0], strerror(failed_errno)); errno = failed_errno; return -1; } cmd->err = fderr[0]; } 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) { /* * Redirect the channel to write syscall error messages to * before redirecting the process's stderr so that all die() * in subsequent call paths use the parent's stderr. */ if (cmd->no_stderr || need_err) { child_err = dup(2); set_cloexec(child_err); } 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) { dup2(fdin[0], 0); close_pair(fdin); } else if (cmd->in) { dup2(cmd->in, 0); close(cmd->in); } if (cmd->no_stderr) dup_devnull(2); else if (need_err) { dup2(fderr[1], 2); close_pair(fderr); } else if (cmd->err > 1) { dup2(cmd->err, 2); close(cmd->err); } if (cmd->no_stdout) dup_devnull(1); else if (cmd->stdout_to_stderr) dup2(2, 1); else if (need_out) { dup2(fdout[1], 1); close_pair(fdout); } else if (cmd->out > 1) { dup2(cmd->out, 1); close(cmd->out); } if (cmd->dir && chdir(cmd->dir)) die_errno("exec '%s': cd to '%s' failed", cmd->argv[0], cmd->dir); if (cmd->env) { for (; *cmd->env; cmd->env++) { if (strchr(*cmd->env, '=')) putenv((char *)*cmd->env); else unsetenv(*cmd->env); } } 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 if (cmd->use_shell) { execv_shell_cmd(cmd->argv); } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } /* * Do not check for cmd->silent_exec_failure; the parent * process will check it when it sees this exit code. */ if (errno == ENOENT) exit(127); else die_errno("cannot exec '%s'", cmd->argv[0]); } 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 fhin = 0, fhout = 1, fherr = 2; const char **sargv = cmd->argv; char **env = environ; if (cmd->no_stdin) fhin = open("/dev/null", O_RDWR); else if (need_in) fhin = dup(fdin[0]); else if (cmd->in) fhin = dup(cmd->in); if (cmd->no_stderr) fherr = open("/dev/null", O_RDWR); else if (need_err) fherr = dup(fderr[1]); else if (cmd->err > 2) fherr = dup(cmd->err); if (cmd->no_stdout) fhout = open("/dev/null", O_RDWR); else if (cmd->stdout_to_stderr) fhout = dup(fherr); else if (need_out) fhout = dup(fdout[1]); else if (cmd->out > 1) fhout = dup(cmd->out); if (cmd->env) env = make_augmented_environ(cmd->env); if (cmd->git_cmd) { cmd->argv = prepare_git_cmd(cmd->argv); } else if (cmd->use_shell) { cmd->argv = prepare_shell_cmd(cmd->argv); } cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env, cmd->dir, fhin, fhout, fherr); failed_errno = errno; if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT)) error("cannot spawn %s: %s", cmd->argv[0], strerror(errno)); if (cmd->env) free_environ(env); if (cmd->git_cmd) free(cmd->argv); cmd->argv = sargv; if (fhin != 0) close(fhin); if (fhout != 1) close(fhout); if (fherr != 2) close(fherr); } #endif if (cmd->pid < 0) { if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); if (need_err) close_pair(fderr); else if (cmd->err) close(cmd->err); errno = failed_errno; return -1; } if (need_in) close(fdin[0]); else if (cmd->in) close(cmd->in); if (need_out) close(fdout[1]); else if (cmd->out) close(cmd->out); if (need_err) close(fderr[1]); else if (cmd->err) close(cmd->err); return 0; }
static int handle_alias(int *argcp, const char ***argv) { int envchanged = 0, ret = 0, saved_errno = errno; const char *subdir; int count, option_count; const char **new_argv; const char *alias_command; char *alias_string; int unused_nongit; subdir = setup_git_directory_gently(&unused_nongit); alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); if (alias_string) { if (alias_string[0] == '!') { const char **alias_argv; int argc = *argcp, i; commit_pager_choice(); /* build alias_argv */ alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1)); alias_argv[0] = alias_string + 1; for (i = 1; i < argc; ++i) alias_argv[i] = (*argv)[i]; alias_argv[argc] = NULL; ret = run_command_v_opt(alias_argv, RUN_USING_SHELL); if (ret >= 0) /* normal exit */ exit(ret); die_errno("While expanding alias '%s': '%s'", alias_command, alias_string + 1); } count = split_cmdline(alias_string, &new_argv); if (count < 0) die("Bad alias.%s string: %s", alias_command, split_cmdline_strerror(count)); option_count = handle_options(&new_argv, &count, &envchanged); if (envchanged) die("alias '%s' changes environment variables\n" "You can use '!git' in the alias to do this.", alias_command); memmove(new_argv - option_count, new_argv, count * sizeof(char *)); new_argv -= option_count; if (count < 1) die("empty alias for %s", alias_command); if (!strcmp(alias_command, new_argv[0])) die("recursive alias: %s", alias_command); trace_argv_printf(new_argv, "trace: alias expansion: %s =>", alias_command); new_argv = xrealloc(new_argv, sizeof(char *) * (count + *argcp)); /* insert after command name */ memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp); *argv = new_argv; *argcp += count - 1; ret = 1; } if (subdir && chdir(subdir)) die_errno("Cannot change to '%s'", subdir); errno = saved_errno; return ret; }
static int handle_alias(int *argcp, const char ***argv) { int envchanged = 0, ret = 0, saved_errno = errno; const char *subdir; int count, option_count; const char **new_argv; const char *alias_command; char *alias_string; int unused_nongit; subdir = setup_git_directory_gently(&unused_nongit); alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); if (alias_string) { if (alias_string[0] == '!') { commit_pager_choice(); if (*argcp > 1) { struct strbuf buf; strbuf_init(&buf, PATH_MAX); strbuf_addstr(&buf, alias_string); sq_quote_argv(&buf, (*argv) + 1, PATH_MAX); free(alias_string); alias_string = buf.buf; } trace_printf("trace: alias to shell cmd: %s => %s\n", alias_command, alias_string + 1); ret = system(alias_string + 1); if (ret >= 0 && WIFEXITED(ret) && WEXITSTATUS(ret) != 127) exit(WEXITSTATUS(ret)); die("Failed to run '%s' when expanding alias '%s'", alias_string + 1, alias_command); } count = split_cmdline(alias_string, &new_argv); if (count < 0) die("Bad alias.%s string: %s", alias_command, split_cmdline_strerror(count)); option_count = handle_options(&new_argv, &count, &envchanged); if (envchanged) die("alias '%s' changes environment variables\n" "You can use '!git' in the alias to do this.", alias_command); memmove(new_argv - option_count, new_argv, count * sizeof(char *)); new_argv -= option_count; if (count < 1) die("empty alias for %s", alias_command); if (!strcmp(alias_command, new_argv[0])) die("recursive alias: %s", alias_command); trace_argv_printf(new_argv, "trace: alias expansion: %s =>", alias_command); new_argv = xrealloc(new_argv, sizeof(char *) * (count + *argcp)); /* insert after command name */ memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp); *argv = new_argv; *argcp += count - 1; ret = 1; } if (subdir && chdir(subdir)) die_errno("Cannot change to '%s'", subdir); errno = saved_errno; return ret; }