18 #include <sys/select.h>
28 #if defined(USE_PPOLL)
31 #include <sys/select.h>
45 use_console_(use_console) {
57 int subproc_stdout_fd = -1;
62 if (pipe(output_pipe) < 0)
63 Fatal(
"pipe: %s", strerror(errno));
65 subproc_stdout_fd = output_pipe[1];
66 #if !defined(USE_PPOLL)
69 if (
fd_ >=
static_cast<int>(FD_SETSIZE))
70 Fatal(
"pipe: %s", strerror(EMFILE));
75 posix_spawn_file_actions_t action;
76 int err = posix_spawn_file_actions_init(&action);
78 Fatal(
"posix_spawn_file_actions_init: %s", strerror(err));
81 err = posix_spawn_file_actions_addclose(&action,
fd_);
83 Fatal(
"posix_spawn_file_actions_addclose: %s", strerror(err));
86 posix_spawnattr_t attr;
87 err = posix_spawnattr_init(&attr);
89 Fatal(
"posix_spawnattr_init: %s", strerror(err));
93 flags |= POSIX_SPAWN_SETSIGMASK;
94 err = posix_spawnattr_setsigmask(&attr, &set->
old_mask_);
96 Fatal(
"posix_spawnattr_setsigmask: %s", strerror(err));
103 flags |= POSIX_SPAWN_SETPGROUP;
107 err = posix_spawn_file_actions_addopen(&action, 0,
"/dev/null", O_RDONLY,
110 Fatal(
"posix_spawn_file_actions_addopen: %s", strerror(err));
113 err = posix_spawn_file_actions_adddup2(&action, subproc_stdout_fd, 1);
115 Fatal(
"posix_spawn_file_actions_adddup2: %s", strerror(err));
116 err = posix_spawn_file_actions_adddup2(&action, subproc_stdout_fd, 2);
118 Fatal(
"posix_spawn_file_actions_adddup2: %s", strerror(err));
119 err = posix_spawn_file_actions_addclose(&action, subproc_stdout_fd);
121 Fatal(
"posix_spawn_file_actions_addclose: %s", strerror(err));
124 #ifdef POSIX_SPAWN_USEVFORK
125 flags |= POSIX_SPAWN_USEVFORK;
128 err = posix_spawnattr_setflags(&attr, flags);
130 Fatal(
"posix_spawnattr_setflags: %s", strerror(err));
132 const char* spawned_args[] = {
"/bin/sh",
"-c", command.c_str(), NULL };
133 err = posix_spawn(&
pid_,
"/bin/sh", &action, &attr,
134 const_cast<char**
>(spawned_args),
environ);
136 Fatal(
"posix_spawn: %s", strerror(err));
138 err = posix_spawnattr_destroy(&attr);
140 Fatal(
"posix_spawnattr_destroy: %s", strerror(err));
141 err = posix_spawn_file_actions_destroy(&action);
143 Fatal(
"posix_spawn_file_actions_destroy: %s", strerror(err));
146 close(subproc_stdout_fd);
152 ssize_t len = read(
fd_, buf,
sizeof(buf));
154 buf_.append(buf, len);
157 Fatal(
"read: %s", strerror(errno));
167 while ((ret = waitpid(
pid_, &status, waitpid_options)) < 0) {
169 Fatal(
"waitpid(%d): %s",
pid_, strerror(errno));
190 if (WIFEXITED(status) && WEXITSTATUS(status) & 0x80) {
194 int signal = WEXITSTATUS(status) & 0x7f;
195 status = (signal << 16) | signal;
199 if (WIFEXITED(status)) {
201 return static_cast<ExitStatus>(WEXITSTATUS(status));
203 if (WIFSIGNALED(status)) {
204 if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM
205 || WTERMSIG(status) == SIGHUP)
239 sigemptyset(&pending);
240 if (sigpending(&pending) == -1) {
241 perror(
"ninja: sigpending");
244 if (sigismember(&pending, SIGINT))
246 else if (sigismember(&pending, SIGTERM))
248 else if (sigismember(&pending, SIGHUP))
257 sigaddset(&set, SIGINT);
258 sigaddset(&set, SIGTERM);
259 sigaddset(&set, SIGHUP);
260 sigaddset(&set, SIGCHLD);
261 if (sigprocmask(SIG_BLOCK, &set, &
old_mask_) < 0)
262 Fatal(
"sigprocmask: %s", strerror(errno));
264 struct sigaction act;
265 memset(&act, 0,
sizeof(act));
268 Fatal(
"sigaction: %s", strerror(errno));
270 Fatal(
"sigaction: %s", strerror(errno));
272 Fatal(
"sigaction: %s", strerror(errno));
274 memset(&act, 0,
sizeof(act));
275 act.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;
278 Fatal(
"sigaction: %s", strerror(errno));
287 if ((*i)->use_console_ && (*i)->TryFinish(WNOHANG)) {
300 Fatal(
"sigaction: %s", strerror(errno));
302 Fatal(
"sigaction: %s", strerror(errno));
304 Fatal(
"sigaction: %s", strerror(errno));
306 Fatal(
"sigaction: %s", strerror(errno));
307 if (sigprocmask(SIG_SETMASK, &
old_mask_, 0) < 0)
308 Fatal(
"sigprocmask: %s", strerror(errno));
313 if (!subprocess->
Start(
this, command)) {
326 for (vector<Subprocess*>::iterator i =
running_.begin();
331 pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
338 pollfd pfd = { -1, 0, 0 };
345 int ret = ppoll(&fds.front(), nfds, NULL, &
old_mask_);
351 if (errno != EINTR) {
352 perror(
"ninja: ppoll");
368 for (vector<Subprocess*>::iterator i =
running_.begin();
375 assert(fd == fds[cur_nfd].fd);
376 if (fds[cur_nfd++].revents) {
396 for (vector<Subprocess*>::iterator i =
running_.begin();
408 int ret = pselect(nfds, (nfds > 0 ? &set :
nullptr), 0, 0, 0, &
old_mask_);
411 if (errno != EINTR) {
412 perror(
"ninja: pselect");
425 for (vector<Subprocess*>::iterator i =
running_.begin();
428 if (fd >= 0 && FD_ISSET(fd, &set)) {
452 for (vector<Subprocess*>::iterator i =
running_.begin();
456 if (!(*i)->use_console_)
458 for (vector<Subprocess*>::iterator i =
running_.begin();
SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
void CheckConsoleProcessTerminated()
std::queue< Subprocess * > finished_
static void HandlePendingInterruption()
Subprocess * NextFinished()
static volatile sig_atomic_t interrupted_
Store the signal number that causes the interruption.
static volatile sig_atomic_t s_sigchld_received
Initialized to 0 before ppoll/pselect().
struct sigaction old_hup_act_
static void SetInterruptedFlag(int signum)
struct sigaction old_chld_act_
std::vector< Subprocess * > running_
struct sigaction old_int_act_
struct sigaction old_term_act_
static void SigChldHandler(int signo, siginfo_t *info, void *context)
static bool IsInterrupted()
Whether ninja should quit. Set on SIGINT, SIGTERM or SIGHUP reception.
Subprocess * Add(const std::string &command, bool use_console=false)
Subprocess wraps a single async subprocess.
bool Start(struct SubprocessSet *set, const std::string &command)
bool TryFinish(int waitpid_options)
Call waitpid() on the subprocess with the provided options and update the pid_ and exit_status_ field...
ExitStatus exit_status_
In POSIX platforms it is necessary to use waitpid(WNOHANG) to know whether a certain subprocess has f...
Subprocess(bool use_console)
int fd_
The file descriptor that will be used in ppoll/pselect() for this process, if any.
ExitStatus Finish()
Returns ExitSuccess on successful process exit, ExitInterrupted if the process was interrupted,...
const std::string & GetOutput() const
pid_t pid_
PID of the subprocess. Set to -1 when the subprocess is reaped.
void SetCloseOnExec(int fd)
Mark a file descriptor to not be inherited on exec()s.
void Fatal(const char *msg,...)
Log a fatal message and exit.