/** * popen2 - Open a bidirectional pipe to a process * @path: full path of executable to run * @to_child: return file descriptor for child stdin * @from_child: return file descriptor for child stdout * * Return -1 on error, pid of child process on success. */ pid_t popen2(const char *path, int *to_child, int *from_child, bool no_stderr) { int child_in[2], child_out[2]; pid_t pid; if (access(path, X_OK) < 0) return -1; if (pipe(child_in) < 0 || pipe(child_out) < 0) return -1; pid = fork(); if (pid < 0) return -1; if (pid == 0) { /* * child */ close(child_in[1]); close(child_out[0]); /* * Rationale: by making sure that fd > 2, dup(fd) <= 2, * a new copy is created, allowing to close the pipe end, * and with FD_CLOEXEC to off (on linux). */ if (fd_no_clobber_stdio(&child_in[0]) < 0 || dup2(child_in[0], STDIN_FILENO) != STDIN_FILENO || close(child_in[0]) < 0) _exit(127); if (fd_no_clobber_stdio(&child_out[1]) < 0 || dup2(child_out[1], STDOUT_FILENO) != STDOUT_FILENO || close(child_out[1]) < 0) _exit(127); if (no_stderr && dup_devnull(STDERR_FILENO) < 0) _exit(127); closeall(STDERR_FILENO + 1); if (execl(path, path, NULL) < 0) _exit(1); } /* * parent */ close(child_in[0]); close(child_out[1]); *to_child = child_in[1]; *from_child = child_out[0]; return pid; }
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 = 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; }
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]; } fflush(NULL); 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->preexec_cb) cmd->preexec_cb(); if (cmd->perf_cmd) { execv_perf_cmd(cmd->argv); } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } exit(127); } if (cmd->pid < 0) { int err = 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); if (need_err) close_pair(fderr); return err == ENOENT ? -ERR_RUN_COMMAND_EXEC : -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; }
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; }