1 // Copyright 2012 Google Inc. All Rights Reserved.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 #include "subprocess.h"
27 extern char** environ;
31 Subprocess::Subprocess(bool use_console) : fd_(-1), pid_(-1),
32 use_console_(use_console) {
35 Subprocess::~Subprocess() {
38 // Reap child if forgotten.
43 bool Subprocess::Start(SubprocessSet* set, const string& command) {
45 if (pipe(output_pipe) < 0)
46 Fatal("pipe: %s", strerror(errno));
48 #if !defined(USE_PPOLL)
49 // If available, we use ppoll in DoWork(); otherwise we use pselect
50 // and so must avoid overly-large FDs.
51 if (fd_ >= static_cast<int>(FD_SETSIZE))
52 Fatal("pipe: %s", strerror(EMFILE));
56 posix_spawn_file_actions_t action;
57 if (posix_spawn_file_actions_init(&action) != 0)
58 Fatal("posix_spawn_file_actions_init: %s", strerror(errno));
60 if (posix_spawn_file_actions_addclose(&action, output_pipe[0]) != 0)
61 Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno));
63 posix_spawnattr_t attr;
64 if (posix_spawnattr_init(&attr) != 0)
65 Fatal("posix_spawnattr_init: %s", strerror(errno));
69 flags |= POSIX_SPAWN_SETSIGMASK;
70 if (posix_spawnattr_setsigmask(&attr, &set->old_mask_) != 0)
71 Fatal("posix_spawnattr_setsigmask: %s", strerror(errno));
72 // Signals which are set to be caught in the calling process image are set to
73 // default action in the new process image, so no explicit
74 // POSIX_SPAWN_SETSIGDEF parameter is needed.
76 // TODO: Consider using POSIX_SPAWN_USEVFORK on Linux with glibc?
79 // Put the child in its own process group, so ctrl-c won't reach it.
80 flags |= POSIX_SPAWN_SETPGROUP;
81 // No need to posix_spawnattr_setpgroup(&attr, 0), it's the default.
83 // Open /dev/null over stdin.
84 if (posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY,
86 Fatal("posix_spawn_file_actions_addopen: %s", strerror(errno));
89 if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1) != 0)
90 Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
91 if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2) != 0)
92 Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
93 // In the console case, output_pipe is still inherited by the child and
94 // closed when the subprocess finishes, which then notifies ninja.
97 if (posix_spawnattr_setflags(&attr, flags) != 0)
98 Fatal("posix_spawnattr_setflags: %s", strerror(errno));
100 const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL };
101 if (posix_spawn(&pid_, "/bin/sh", &action, &attr,
102 const_cast<char**>(spawned_args), environ) != 0)
103 Fatal("posix_spawn: %s", strerror(errno));
105 if (posix_spawnattr_destroy(&attr) != 0)
106 Fatal("posix_spawnattr_destroy: %s", strerror(errno));
107 if (posix_spawn_file_actions_destroy(&action) != 0)
108 Fatal("posix_spawn_file_actions_destroy: %s", strerror(errno));
110 close(output_pipe[1]);
114 void Subprocess::OnPipeReady() {
116 ssize_t len = read(fd_, buf, sizeof(buf));
118 buf_.append(buf, len);
121 Fatal("read: %s", strerror(errno));
127 ExitStatus Subprocess::Finish() {
130 if (waitpid(pid_, &status, 0) < 0)
131 Fatal("waitpid(%d): %s", pid_, strerror(errno));
134 if (WIFEXITED(status)) {
135 int exit = WEXITSTATUS(status);
138 } else if (WIFSIGNALED(status)) {
139 if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM
140 || WTERMSIG(status) == SIGHUP)
141 return ExitInterrupted;
146 bool Subprocess::Done() const {
150 const string& Subprocess::GetOutput() const {
154 int SubprocessSet::interrupted_;
156 void SubprocessSet::SetInterruptedFlag(int signum) {
157 interrupted_ = signum;
160 void SubprocessSet::HandlePendingInterruption() {
162 sigemptyset(&pending);
163 if (sigpending(&pending) == -1) {
164 perror("ninja: sigpending");
167 if (sigismember(&pending, SIGINT))
168 interrupted_ = SIGINT;
169 else if (sigismember(&pending, SIGTERM))
170 interrupted_ = SIGTERM;
171 else if (sigismember(&pending, SIGHUP))
172 interrupted_ = SIGHUP;
175 SubprocessSet::SubprocessSet() {
178 sigaddset(&set, SIGINT);
179 sigaddset(&set, SIGTERM);
180 sigaddset(&set, SIGHUP);
181 if (sigprocmask(SIG_BLOCK, &set, &old_mask_) < 0)
182 Fatal("sigprocmask: %s", strerror(errno));
184 struct sigaction act;
185 memset(&act, 0, sizeof(act));
186 act.sa_handler = SetInterruptedFlag;
187 if (sigaction(SIGINT, &act, &old_int_act_) < 0)
188 Fatal("sigaction: %s", strerror(errno));
189 if (sigaction(SIGTERM, &act, &old_term_act_) < 0)
190 Fatal("sigaction: %s", strerror(errno));
191 if (sigaction(SIGHUP, &act, &old_hup_act_) < 0)
192 Fatal("sigaction: %s", strerror(errno));
195 SubprocessSet::~SubprocessSet() {
198 if (sigaction(SIGINT, &old_int_act_, 0) < 0)
199 Fatal("sigaction: %s", strerror(errno));
200 if (sigaction(SIGTERM, &old_term_act_, 0) < 0)
201 Fatal("sigaction: %s", strerror(errno));
202 if (sigaction(SIGHUP, &old_hup_act_, 0) < 0)
203 Fatal("sigaction: %s", strerror(errno));
204 if (sigprocmask(SIG_SETMASK, &old_mask_, 0) < 0)
205 Fatal("sigprocmask: %s", strerror(errno));
208 Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
209 Subprocess *subprocess = new Subprocess(use_console);
210 if (!subprocess->Start(this, command)) {
214 running_.push_back(subprocess);
219 bool SubprocessSet::DoWork() {
223 for (vector<Subprocess*>::iterator i = running_.begin();
224 i != running_.end(); ++i) {
228 pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
234 int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_);
236 if (errno != EINTR) {
237 perror("ninja: ppoll");
240 return IsInterrupted();
243 HandlePendingInterruption();
248 for (vector<Subprocess*>::iterator i = running_.begin();
249 i != running_.end(); ) {
253 assert(fd == fds[cur_nfd].fd);
254 if (fds[cur_nfd++].revents) {
258 i = running_.erase(i);
265 return IsInterrupted();
268 #else // !defined(USE_PPOLL)
269 bool SubprocessSet::DoWork() {
274 for (vector<Subprocess*>::iterator i = running_.begin();
275 i != running_.end(); ++i) {
285 int ret = pselect(nfds, &set, 0, 0, 0, &old_mask_);
287 if (errno != EINTR) {
288 perror("ninja: pselect");
291 return IsInterrupted();
294 HandlePendingInterruption();
298 for (vector<Subprocess*>::iterator i = running_.begin();
299 i != running_.end(); ) {
301 if (fd >= 0 && FD_ISSET(fd, &set)) {
305 i = running_.erase(i);
312 return IsInterrupted();
314 #endif // !defined(USE_PPOLL)
316 Subprocess* SubprocessSet::NextFinished() {
317 if (finished_.empty())
319 Subprocess* subproc = finished_.front();
324 void SubprocessSet::Clear() {
325 for (vector<Subprocess*>::iterator i = running_.begin();
326 i != running_.end(); ++i)
327 // Since the foreground process is in our process group, it will receive
328 // the interruption signal (i.e. SIGINT or SIGTERM) at the same time as us.
329 if (!(*i)->use_console_)
330 kill(-(*i)->pid_, interrupted_);
331 for (vector<Subprocess*>::iterator i = running_.begin();
332 i != running_.end(); ++i)