mirror of
https://github.com/git/git.git
synced 2024-11-14 21:23:03 +01:00
c20181e3a3
Callers of start_command() can set the members .in and .out of struct child_process to a value > 0 to specify that this descriptor is used as the stdin or stdout of the child process. Previously, if start_command() was successful, this descriptor was closed upon return. Here we now make sure that the descriptor is also closed in case of failures. All callers are updated not to close the file descriptor themselves after start_command() was called. Note that earlier run_gpg_verify() of git-verify-tag set .out = 1, which worked because start_command() treated this as a special case, but now this is incorrect because it closes the descriptor. The intent here is to inherit stdout to the child, which is achieved by .out = 0. Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at> Signed-off-by: Junio C Hamano <gitster@pobox.com>
251 lines
4.8 KiB
C
251 lines
4.8 KiB
C
#include "cache.h"
|
|
#include "run-command.h"
|
|
#include "exec_cmd.h"
|
|
|
|
static inline void close_pair(int fd[2])
|
|
{
|
|
close(fd[0]);
|
|
close(fd[1]);
|
|
}
|
|
|
|
static inline void dup_devnull(int to)
|
|
{
|
|
int fd = open("/dev/null", O_RDWR);
|
|
dup2(fd, to);
|
|
close(fd);
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
cmd->pid = fork();
|
|
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 (!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_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->no_stderr)
|
|
dup_devnull(2);
|
|
else if (need_err) {
|
|
dup2(fderr[1], 2);
|
|
close_pair(fderr);
|
|
}
|
|
|
|
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]);
|
|
}
|
|
|
|
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 wait_or_whine(pid_t pid)
|
|
{
|
|
for (;;) {
|
|
int status, code;
|
|
pid_t waiting = waitpid(pid, &status, 0);
|
|
|
|
if (waiting < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
error("waitpid failed (%s)", strerror(errno));
|
|
return -ERR_RUN_COMMAND_WAITPID;
|
|
}
|
|
if (waiting != pid)
|
|
return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
|
|
if (WIFSIGNALED(status))
|
|
return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
|
|
|
|
if (!WIFEXITED(status))
|
|
return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
|
|
code = WEXITSTATUS(status);
|
|
if (code)
|
|
return -code;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int finish_command(struct child_process *cmd)
|
|
{
|
|
return wait_or_whine(cmd->pid);
|
|
}
|
|
|
|
int run_command(struct child_process *cmd)
|
|
{
|
|
int code = start_command(cmd);
|
|
if (code)
|
|
return code;
|
|
return finish_command(cmd);
|
|
}
|
|
|
|
static void prepare_run_command_v_opt(struct child_process *cmd,
|
|
const char **argv,
|
|
int opt)
|
|
{
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
cmd->argv = argv;
|
|
cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
|
|
cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
|
|
cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
|
|
}
|
|
|
|
int run_command_v_opt(const char **argv, int opt)
|
|
{
|
|
struct child_process cmd;
|
|
prepare_run_command_v_opt(&cmd, argv, opt);
|
|
return run_command(&cmd);
|
|
}
|
|
|
|
int run_command_v_opt_cd(const char **argv, int opt, const char *dir)
|
|
{
|
|
struct child_process cmd;
|
|
prepare_run_command_v_opt(&cmd, argv, opt);
|
|
cmd.dir = dir;
|
|
return run_command(&cmd);
|
|
}
|
|
|
|
int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
|
|
{
|
|
struct child_process cmd;
|
|
prepare_run_command_v_opt(&cmd, argv, opt);
|
|
cmd.dir = dir;
|
|
cmd.env = env;
|
|
return run_command(&cmd);
|
|
}
|
|
|
|
int start_async(struct async *async)
|
|
{
|
|
int pipe_out[2];
|
|
|
|
if (pipe(pipe_out) < 0)
|
|
return error("cannot create pipe: %s", strerror(errno));
|
|
|
|
async->pid = fork();
|
|
if (async->pid < 0) {
|
|
error("fork (async) failed: %s", strerror(errno));
|
|
close_pair(pipe_out);
|
|
return -1;
|
|
}
|
|
if (!async->pid) {
|
|
close(pipe_out[0]);
|
|
exit(!!async->proc(pipe_out[1], async->data));
|
|
}
|
|
async->out = pipe_out[0];
|
|
close(pipe_out[1]);
|
|
return 0;
|
|
}
|
|
|
|
int finish_async(struct async *async)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (wait_or_whine(async->pid))
|
|
ret = error("waitpid (async) failed");
|
|
return ret;
|
|
}
|