static int execv_shell_cmd(const char **argv) { const char **nargv = prepare_shell_cmd(argv); trace_argv_printf(nargv, "trace: exec:"); sane_execvp(nargv[0], (char **)nargv); free(nargv); return -1; }
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 */ sane_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; }
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 cmd_stash(int argc, const char **argv, const char *prefix) { int i = -1; pid_t pid = getpid(); const char *index_file; struct argv_array args = ARGV_ARRAY_INIT; struct option options[] = { OPT_END() }; if (!use_builtin_stash()) { const char *path = mkpath("%s/git-legacy-stash", git_exec_path()); if (sane_execvp(path, (char **)argv) < 0) die_errno(_("could not exec %s"), path); else BUG("sane_execvp() returned???"); } prefix = setup_git_directory(); trace_repo_setup(prefix); setup_work_tree(); git_config(git_diff_basic_config, NULL); argc = parse_options(argc, argv, prefix, options, git_stash_usage, PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); index_file = get_index_file(); strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file, (uintmax_t)pid); if (!argc) return !!push_stash(0, NULL, prefix); else if (!strcmp(argv[0], "apply")) return !!apply_stash(argc, argv, prefix); else if (!strcmp(argv[0], "clear")) return !!clear_stash(argc, argv, prefix); else if (!strcmp(argv[0], "drop")) return !!drop_stash(argc, argv, prefix); else if (!strcmp(argv[0], "pop")) return !!pop_stash(argc, argv, prefix); else if (!strcmp(argv[0], "branch")) return !!branch_stash(argc, argv, prefix); else if (!strcmp(argv[0], "list")) return !!list_stash(argc, argv, prefix); else if (!strcmp(argv[0], "show")) return !!show_stash(argc, argv, prefix); else if (!strcmp(argv[0], "store")) return !!store_stash(argc, argv, prefix); else if (!strcmp(argv[0], "create")) return !!create_stash(argc, argv, prefix); else if (!strcmp(argv[0], "push")) return !!push_stash(argc, argv, prefix); else if (!strcmp(argv[0], "save")) return !!save_stash(argc, argv, prefix); else if (*argv[0] != '-') usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), git_stash_usage, options); if (strcmp(argv[0], "-p")) { while (++i < argc && strcmp(argv[i], "--")) { /* * `akpqu` is a string which contains all short options, * except `-m` which is verified separately. */ if ((strlen(argv[i]) == 2) && *argv[i] == '-' && strchr("akpqu", argv[i][1])) continue; if (!strcmp(argv[i], "--all") || !strcmp(argv[i], "--keep-index") || !strcmp(argv[i], "--no-keep-index") || !strcmp(argv[i], "--patch") || !strcmp(argv[i], "--quiet") || !strcmp(argv[i], "--include-untracked")) continue; /* * `-m` and `--message=` are verified separately because * they need to be immediately followed by a string * (i.e.`-m"foobar"` or `--message="foobar"`). */ if (starts_with(argv[i], "-m") || starts_with(argv[i], "--message=")) continue; usage_with_options(git_stash_usage, options); } } argv_array_push(&args, "push"); argv_array_pushv(&args, argv); return !!push_stash(args.argc, args.argv, prefix); }
int cmd_difftool(int argc, const char **argv, const char *prefix) { int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0, tool_help = 0; static char *difftool_cmd = NULL, *extcmd = NULL; struct option builtin_difftool_options[] = { OPT_BOOL('g', "gui", &use_gui_tool, N_("use `diff.guitool` instead of `diff.tool`")), OPT_BOOL('d', "dir-diff", &dir_diff, N_("perform a full-directory diff")), { OPTION_SET_INT, 'y', "no-prompt", &prompt, NULL, N_("do not prompt before launching a diff tool"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0 }, { OPTION_SET_INT, 0, "prompt", &prompt, NULL, NULL, PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN, NULL, 1 }, OPT_BOOL(0, "symlinks", &symlinks, N_("use symlinks in dir-diff mode")), OPT_STRING('t', "tool", &difftool_cmd, N_("<tool>"), N_("use the specified diff tool")), OPT_BOOL(0, "tool-help", &tool_help, N_("print a list of diff tools that may be used with " "`--tool`")), OPT_BOOL(0, "trust-exit-code", &trust_exit_code, N_("make 'git-difftool' exit when an invoked diff " "tool returns a non - zero exit code")), OPT_STRING('x', "extcmd", &extcmd, N_("<command>"), N_("specify a custom command for viewing diffs")), OPT_END() }; /* * NEEDSWORK: Once the builtin difftool has been tested enough * and git-legacy-difftool.perl is retired to contrib/, this preamble * can be removed. */ if (!use_builtin_difftool()) { const char *path = mkpath("%s/git-legacy-difftool", git_exec_path()); if (sane_execvp(path, (char **)argv) < 0) die_errno("could not exec %s", path); return 0; } prefix = setup_git_directory(); trace_repo_setup(prefix); setup_work_tree(); /* NEEDSWORK: once we no longer spawn anything, remove this */ setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1); setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1); git_config(difftool_config, NULL); symlinks = has_symlinks; argc = parse_options(argc, argv, prefix, builtin_difftool_options, builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); if (tool_help) return print_tool_help(); if (use_gui_tool && diff_gui_tool && *diff_gui_tool) setenv("GIT_DIFF_TOOL", diff_gui_tool, 1); else if (difftool_cmd) { if (*difftool_cmd) setenv("GIT_DIFF_TOOL", difftool_cmd, 1); else die(_("no <tool> given for --tool=<tool>")); } if (extcmd) { if (*extcmd) setenv("GIT_DIFFTOOL_EXTCMD", extcmd, 1); else die(_("no <cmd> given for --extcmd=<cmd>")); } setenv("GIT_DIFFTOOL_TRUST_EXIT_CODE", trust_exit_code ? "true" : "false", 1); /* * In directory diff mode, 'git-difftool--helper' is called once * to compare the a / b directories. In file diff mode, 'git diff' * will invoke a separate instance of 'git-difftool--helper' for * each file that changed. */ if (dir_diff) return run_dir_diff(extcmd, symlinks, prefix, argc, argv); return run_file_diff(prompt, prefix, argc, argv); }