struct child_to_clean {
pid_t pid;
+ struct child_process *process;
struct child_to_clean *next;
};
static struct child_to_clean *children_to_clean;
while (children_to_clean) {
struct child_to_clean *p = children_to_clean;
children_to_clean = p->next;
+
+ if (p->process && !in_signal) {
+ struct child_process *process = p->process;
+ if (process->clean_on_exit_handler) {
+ trace_printf(
+ "trace: run_command: running exit handler for pid %"
+ PRIuMAX, (uintmax_t)p->pid
+ );
+ process->clean_on_exit_handler(process);
+ }
+ }
+
kill(p->pid, sig);
if (!in_signal)
free(p);
cleanup_children(SIGTERM, 0);
}
-static void mark_child_for_cleanup(pid_t pid)
+static void mark_child_for_cleanup(pid_t pid, struct child_process *process)
{
struct child_to_clean *p = xmalloc(sizeof(*p));
p->pid = pid;
+ p->process = process;
p->next = children_to_clean;
children_to_clean = p;
return -1;
}
-static const char **prepare_shell_cmd(const char **argv)
+static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
{
- int argc, nargc = 0;
- const char **nargv;
-
- for (argc = 0; argv[argc]; argc++)
- ; /* just counting */
- /* +1 for NULL, +3 for "sh -c" plus extra $0 */
- nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
-
- if (argc < 1)
+ if (!argv[0])
die("BUG: shell command is empty");
if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
#ifndef GIT_WINDOWS_NATIVE
- nargv[nargc++] = SHELL_PATH;
+ argv_array_push(out, SHELL_PATH);
#else
- nargv[nargc++] = "sh";
+ argv_array_push(out, "sh");
#endif
- nargv[nargc++] = "-c";
-
- if (argc < 2)
- nargv[nargc++] = argv[0];
- else {
- struct strbuf arg0 = STRBUF_INIT;
- strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
- nargv[nargc++] = strbuf_detach(&arg0, NULL);
- }
- }
+ argv_array_push(out, "-c");
- for (argc = 0; argv[argc]; argc++)
- nargv[nargc++] = argv[argc];
- nargv[nargc] = NULL;
+ /*
+ * If we have no extra arguments, we do not even need to
+ * bother with the "$@" magic.
+ */
+ if (!argv[1])
+ argv_array_push(out, argv[0]);
+ else
+ argv_array_pushf(out, "%s \"$@\"", argv[0]);
+ }
- return nargv;
+ argv_array_pushv(out, argv);
+ return out->argv;
}
#ifndef GIT_WINDOWS_NATIVE
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);
+ 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;
}
#endif
if (waiting < 0) {
failed_errno = errno;
- error("waitpid for %s failed: %s", argv0, strerror(errno));
+ error_errno("waitpid for %s failed", argv0);
} else if (waiting != pid) {
error("waitpid is confused (%s)", argv0);
} else if (WIFSIGNALED(status)) {
code = WTERMSIG(status);
- if (code != SIGINT && code != SIGQUIT)
+ if (code != SIGINT && code != SIGQUIT && code != SIGPIPE)
error("%s died of signal %d", argv0, code);
/*
* This return value is chosen so that code & 0xff
}
}
if (cmd->pid < 0)
- error("cannot fork() for %s: %s", cmd->argv[0],
- strerror(errno));
+ error_errno("cannot fork() for %s", cmd->argv[0]);
else if (cmd->clean_on_exit)
- mark_child_for_cleanup(cmd->pid);
+ mark_child_for_cleanup(cmd->pid, cmd);
/*
* Wait for child's execvp. If the execvp succeeds (or if fork()
{
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);
fhout = dup(cmd->out);
if (cmd->git_cmd)
- cmd->argv = prepare_git_cmd(cmd->argv);
+ cmd->argv = prepare_git_cmd(&nargv, cmd->argv);
else if (cmd->use_shell)
- cmd->argv = prepare_shell_cmd(cmd->argv);
+ 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));
+ error_errno("cannot spawn %s", cmd->argv[0]);
if (cmd->clean_on_exit && cmd->pid >= 0)
- mark_child_for_cleanup(cmd->pid);
-
- if (cmd->git_cmd)
- free(cmd->argv);
+ mark_child_for_cleanup(cmd->pid, cmd);
+ argv_array_clear(&nargv);
cmd->argv = sargv;
if (fhin != 0)
close(fhin);
struct async *async = data;
intptr_t ret;
+ if (async->isolate_sigpipe) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGPIPE);
+ if (pthread_sigmask(SIG_BLOCK, &mask, NULL) < 0) {
+ ret = error("unable to block SIGPIPE in async thread");
+ return (void *)ret;
+ }
+ }
+
pthread_setspecific(async_key, async);
ret = async->proc(async->proc_in, async->proc_out, async->data);
return (void *)ret;
return !pthread_equal(main_thread, pthread_self());
}
+static void NORETURN async_exit(int code)
+{
+ pthread_exit((void *)(intptr_t)code);
+}
+
#else
static struct {
return process_is_async;
}
+static void NORETURN async_exit(int code)
+{
+ exit(code);
+}
+
#endif
+void check_pipe(int err)
+{
+ if (err == EPIPE) {
+ if (in_async())
+ async_exit(141);
+
+ signal(SIGPIPE, SIG_DFL);
+ raise(SIGPIPE);
+ /* Should never happen, but just in case... */
+ exit(141);
+ }
+}
+
int start_async(struct async *async)
{
int need_in, need_out;
if (pipe(fdin) < 0) {
if (async->out > 0)
close(async->out);
- return error("cannot create pipe: %s", strerror(errno));
+ return error_errno("cannot create pipe");
}
async->in = fdin[1];
}
close_pair(fdin);
else if (async->in)
close(async->in);
- return error("cannot create pipe: %s", strerror(errno));
+ return error_errno("cannot create pipe");
}
async->out = fdout[0];
}
async->pid = fork();
if (async->pid < 0) {
- error("fork (async) failed: %s", strerror(errno));
+ error_errno("fork (async) failed");
goto error;
}
if (!async->pid) {
exit(!!async->proc(proc_in, proc_out, async->data));
}
- mark_child_for_cleanup(async->pid);
+ mark_child_for_cleanup(async->pid, NULL);
if (need_in)
close(fdin[0]);
{
int err = pthread_create(&async->tid, NULL, run_thread, async);
if (err) {
- error("cannot create thread: %s", strerror(err));
+ error_errno("cannot create thread");
goto error;
}
}
return ret;
}
-int capture_command(struct child_process *cmd, struct strbuf *buf, size_t hint)
+struct io_pump {
+ /* initialized by caller */
+ int fd;
+ int type; /* POLLOUT or POLLIN */
+ union {
+ struct {
+ const char *buf;
+ size_t len;
+ } out;
+ struct {
+ struct strbuf *buf;
+ size_t hint;
+ } in;
+ } u;
+
+ /* returned by pump_io */
+ int error; /* 0 for success, otherwise errno */
+
+ /* internal use */
+ struct pollfd *pfd;
+};
+
+static int pump_io_round(struct io_pump *slots, int nr, struct pollfd *pfd)
{
- cmd->out = -1;
+ int pollsize = 0;
+ int i;
+
+ for (i = 0; i < nr; i++) {
+ struct io_pump *io = &slots[i];
+ if (io->fd < 0)
+ continue;
+ pfd[pollsize].fd = io->fd;
+ pfd[pollsize].events = io->type;
+ io->pfd = &pfd[pollsize++];
+ }
+
+ if (!pollsize)
+ return 0;
+
+ if (poll(pfd, pollsize, -1) < 0) {
+ if (errno == EINTR)
+ return 1;
+ die_errno("poll failed");
+ }
+
+ for (i = 0; i < nr; i++) {
+ struct io_pump *io = &slots[i];
+
+ if (io->fd < 0)
+ continue;
+
+ if (!(io->pfd->revents & (POLLOUT|POLLIN|POLLHUP|POLLERR|POLLNVAL)))
+ continue;
+
+ if (io->type == POLLOUT) {
+ ssize_t len = xwrite(io->fd,
+ io->u.out.buf, io->u.out.len);
+ if (len < 0) {
+ io->error = errno;
+ close(io->fd);
+ io->fd = -1;
+ } else {
+ io->u.out.buf += len;
+ io->u.out.len -= len;
+ if (!io->u.out.len) {
+ close(io->fd);
+ io->fd = -1;
+ }
+ }
+ }
+
+ if (io->type == POLLIN) {
+ ssize_t len = strbuf_read_once(io->u.in.buf,
+ io->fd, io->u.in.hint);
+ if (len < 0)
+ io->error = errno;
+ if (len <= 0) {
+ close(io->fd);
+ io->fd = -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int pump_io(struct io_pump *slots, int nr)
+{
+ struct pollfd *pfd;
+ int i;
+
+ for (i = 0; i < nr; i++)
+ slots[i].error = 0;
+
+ ALLOC_ARRAY(pfd, nr);
+ while (pump_io_round(slots, nr, pfd))
+ ; /* nothing */
+ free(pfd);
+
+ /* There may be multiple errno values, so just pick the first. */
+ for (i = 0; i < nr; i++) {
+ if (slots[i].error) {
+ errno = slots[i].error;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+int pipe_command(struct child_process *cmd,
+ const char *in, size_t in_len,
+ struct strbuf *out, size_t out_hint,
+ struct strbuf *err, size_t err_hint)
+{
+ struct io_pump io[3];
+ int nr = 0;
+
+ if (in)
+ cmd->in = -1;
+ if (out)
+ cmd->out = -1;
+ if (err)
+ cmd->err = -1;
+
if (start_command(cmd) < 0)
return -1;
- if (strbuf_read(buf, cmd->out, hint) < 0) {
- close(cmd->out);
+ if (in) {
+ io[nr].fd = cmd->in;
+ io[nr].type = POLLOUT;
+ io[nr].u.out.buf = in;
+ io[nr].u.out.len = in_len;
+ nr++;
+ }
+ if (out) {
+ io[nr].fd = cmd->out;
+ io[nr].type = POLLIN;
+ io[nr].u.in.buf = out;
+ io[nr].u.in.hint = out_hint;
+ nr++;
+ }
+ if (err) {
+ io[nr].fd = cmd->err;
+ io[nr].type = POLLIN;
+ io[nr].u.in.buf = err;
+ io[nr].u.in.hint = err_hint;
+ nr++;
+ }
+
+ if (pump_io(io, nr) < 0) {
finish_command(cmd); /* throw away exit code */
return -1;
}
- close(cmd->out);
return finish_command(cmd);
}