CHILD_ERR_CHDIR,
CHILD_ERR_DUP2,
CHILD_ERR_CLOSE,
+ CHILD_ERR_SIGPROCMASK,
CHILD_ERR_ENOENT,
CHILD_ERR_SILENT,
CHILD_ERR_ERRNO
case CHILD_ERR_CLOSE:
error_errno("close() in child failed");
break;
+ case CHILD_ERR_SIGPROCMASK:
+ error_errno("sigprocmask failed restoring signals");
+ break;
case CHILD_ERR_ENOENT:
error_errno("cannot run %s", cmd->argv[0]);
break;
strbuf_release(&key);
return childenv;
}
+
+struct atfork_state {
+#ifndef NO_PTHREADS
+ int cs;
#endif
+ sigset_t old;
+};
+
+#ifndef NO_PTHREADS
+static void bug_die(int err, const char *msg)
+{
+ if (err) {
+ errno = err;
+ die_errno("BUG: %s", msg);
+ }
+}
+#endif
+
+static void atfork_prepare(struct atfork_state *as)
+{
+ sigset_t all;
+
+ if (sigfillset(&all))
+ die_errno("sigfillset");
+#ifdef NO_PTHREADS
+ if (sigprocmask(SIG_SETMASK, &all, &as->old))
+ die_errno("sigprocmask");
+#else
+ bug_die(pthread_sigmask(SIG_SETMASK, &all, &as->old),
+ "blocking all signals");
+ bug_die(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs),
+ "disabling cancellation");
+#endif
+}
+
+static void atfork_parent(struct atfork_state *as)
+{
+#ifdef NO_PTHREADS
+ if (sigprocmask(SIG_SETMASK, &as->old, NULL))
+ die_errno("sigprocmask");
+#else
+ bug_die(pthread_setcancelstate(as->cs, NULL),
+ "re-enabling cancellation");
+ bug_die(pthread_sigmask(SIG_SETMASK, &as->old, NULL),
+ "restoring signal mask");
+#endif
+}
+#endif /* GIT_WINDOWS_NATIVE */
static inline void set_cloexec(int fd)
{
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;
prepare_cmd(&argv, cmd);
childenv = prep_childenv(cmd->env);
+ atfork_prepare(&as);
/*
* NOTE: In order to prevent deadlocking when using threads special
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.
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
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)