1// Copyright 2012 Google Inc. All Rights Reserved.
2//
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
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
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.
14
15#include "subprocess.h"
16
17#include <sys/select.h>
18#include <assert.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <unistd.h>
22#include <stdio.h>
23#include <string.h>
24#include <sys/wait.h>
25#include <spawn.h>
26
27#if defined(USE_PPOLL)
28#include <poll.h>
29#else
30#include <sys/select.h>
31#endif
32
33extern char** environ;
34
35#include "util.h"
36
37using namespace std;
38
39Subprocess::Subprocess(bool use_console) : fd_(-1), pid_(-1),
40 use_console_(use_console) {
41}
42
43Subprocess::~Subprocess() {
44 if (fd_ >= 0)
45 close(fd_);
46 // Reap child if forgotten.
47 if (pid_ != -1)
48 Finish();
49}
50
51bool Subprocess::Start(SubprocessSet* set, const string& command) {
52 int output_pipe[2];
53 if (pipe(output_pipe) < 0)
54 Fatal("pipe: %s", strerror(errno));
55 fd_ = output_pipe[0];
56#if !defined(USE_PPOLL)
57 // If available, we use ppoll in DoWork(); otherwise we use pselect
58 // and so must avoid overly-large FDs.
59 if (fd_ >= static_cast<int>(FD_SETSIZE))
60 Fatal("pipe: %s", strerror(EMFILE));
61#endif // !USE_PPOLL
62 SetCloseOnExec(fd_);
63
64 posix_spawn_file_actions_t action;
65 int err = posix_spawn_file_actions_init(&action);
66 if (err != 0)
67 Fatal("posix_spawn_file_actions_init: %s", strerror(err));
68
69 err = posix_spawn_file_actions_addclose(&action, output_pipe[0]);
70 if (err != 0)
71 Fatal("posix_spawn_file_actions_addclose: %s", strerror(err));
72
73 posix_spawnattr_t attr;
74 err = posix_spawnattr_init(&attr);
75 if (err != 0)
76 Fatal("posix_spawnattr_init: %s", strerror(err));
77
78 short flags = 0;
79
80 flags |= POSIX_SPAWN_SETSIGMASK;
81 err = posix_spawnattr_setsigmask(&attr, &set->old_mask_);
82 if (err != 0)
83 Fatal("posix_spawnattr_setsigmask: %s", strerror(err));
84 // Signals which are set to be caught in the calling process image are set to
85 // default action in the new process image, so no explicit
86 // POSIX_SPAWN_SETSIGDEF parameter is needed.
87
88 if (!use_console_) {
89 // Put the child in its own process group, so ctrl-c won't reach it.
90 flags |= POSIX_SPAWN_SETPGROUP;
91 // No need to posix_spawnattr_setpgroup(&attr, 0), it's the default.
92
93 // Open /dev/null over stdin.
94 err = posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY,
95 0);
96 if (err != 0) {
97 Fatal("posix_spawn_file_actions_addopen: %s", strerror(err));
98 }
99
100 err = posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1);
101 if (err != 0)
102 Fatal("posix_spawn_file_actions_adddup2: %s", strerror(err));
103 err = posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2);
104 if (err != 0)
105 Fatal("posix_spawn_file_actions_adddup2: %s", strerror(err));
106 err = posix_spawn_file_actions_addclose(&action, output_pipe[1]);
107 if (err != 0)
108 Fatal("posix_spawn_file_actions_addclose: %s", strerror(err));
109 // In the console case, output_pipe is still inherited by the child and
110 // closed when the subprocess finishes, which then notifies ninja.
111 }
112#ifdef POSIX_SPAWN_USEVFORK
113 flags |= POSIX_SPAWN_USEVFORK;
114#endif
115
116 err = posix_spawnattr_setflags(&attr, flags);
117 if (err != 0)
118 Fatal("posix_spawnattr_setflags: %s", strerror(err));
119
120 const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL };
121 err = posix_spawn(&pid_, "/bin/sh", &action, &attr,
122 const_cast<char**>(spawned_args), environ);
123 if (err != 0)
124 Fatal("posix_spawn: %s", strerror(err));
125
126 err = posix_spawnattr_destroy(&attr);
127 if (err != 0)
128 Fatal("posix_spawnattr_destroy: %s", strerror(err));
129 err = posix_spawn_file_actions_destroy(&action);
130 if (err != 0)
131 Fatal("posix_spawn_file_actions_destroy: %s", strerror(err));
132
133 close(output_pipe[1]);
134 return true;
135}
136
137void Subprocess::OnPipeReady() {
138 char buf[4 << 10];
139 ssize_t len = read(fd_, buf, sizeof(buf));
140 if (len > 0) {
141 buf_.append(buf, len);
142 } else {
143 if (len < 0)
144 Fatal("read: %s", strerror(errno));
145 close(fd_);
146 fd_ = -1;
147 }
148}
149
150ExitStatus Subprocess::Finish() {
151 assert(pid_ != -1);
152 int status;
153 if (waitpid(pid_, &status, 0) < 0)
154 Fatal("waitpid(%d): %s", pid_, strerror(errno));
155 pid_ = -1;
156
157#ifdef _AIX
158 if (WIFEXITED(status) && WEXITSTATUS(status) & 0x80) {
159 // Map the shell's exit code used for signal failure (128 + signal) to the
160 // status code expected by AIX WIFSIGNALED and WTERMSIG macros which, unlike
161 // other systems, uses a different bit layout.
162 int signal = WEXITSTATUS(status) & 0x7f;
163 status = (signal << 16) | signal;
164 }
165#endif
166
167 if (WIFEXITED(status)) {
168 int exit = WEXITSTATUS(status);
169 if (exit == 0)
170 return ExitSuccess;
171 } else if (WIFSIGNALED(status)) {
172 if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM
173 || WTERMSIG(status) == SIGHUP)
174 return ExitInterrupted;
175 }
176 return ExitFailure;
177}
178
179bool Subprocess::Done() const {
180 return fd_ == -1;
181}
182
183const string& Subprocess::GetOutput() const {
184 return buf_;
185}
186
187int SubprocessSet::interrupted_;
188
189void SubprocessSet::SetInterruptedFlag(int signum) {
190 interrupted_ = signum;
191}
192
193void SubprocessSet::HandlePendingInterruption() {
194 sigset_t pending;
195 sigemptyset(&pending);
196 if (sigpending(&pending) == -1) {
197 perror("ninja: sigpending");
198 return;
199 }
200 if (sigismember(&pending, SIGINT))
201 interrupted_ = SIGINT;
202 else if (sigismember(&pending, SIGTERM))
203 interrupted_ = SIGTERM;
204 else if (sigismember(&pending, SIGHUP))
205 interrupted_ = SIGHUP;
206}
207
208SubprocessSet::SubprocessSet() {
209 sigset_t set;
210 sigemptyset(&set);
211 sigaddset(&set, SIGINT);
212 sigaddset(&set, SIGTERM);
213 sigaddset(&set, SIGHUP);
214 if (sigprocmask(SIG_BLOCK, &set, &old_mask_) < 0)
215 Fatal("sigprocmask: %s", strerror(errno));
216
217 struct sigaction act;
218 memset(&act, 0, sizeof(act));
219 act.sa_handler = SetInterruptedFlag;
220 if (sigaction(SIGINT, &act, &old_int_act_) < 0)
221 Fatal("sigaction: %s", strerror(errno));
222 if (sigaction(SIGTERM, &act, &old_term_act_) < 0)
223 Fatal("sigaction: %s", strerror(errno));
224 if (sigaction(SIGHUP, &act, &old_hup_act_) < 0)
225 Fatal("sigaction: %s", strerror(errno));
226}
227
228SubprocessSet::~SubprocessSet() {
229 Clear();
230
231 if (sigaction(SIGINT, &old_int_act_, 0) < 0)
232 Fatal("sigaction: %s", strerror(errno));
233 if (sigaction(SIGTERM, &old_term_act_, 0) < 0)
234 Fatal("sigaction: %s", strerror(errno));
235 if (sigaction(SIGHUP, &old_hup_act_, 0) < 0)
236 Fatal("sigaction: %s", strerror(errno));
237 if (sigprocmask(SIG_SETMASK, &old_mask_, 0) < 0)
238 Fatal("sigprocmask: %s", strerror(errno));
239}
240
241Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
242 Subprocess *subprocess = new Subprocess(use_console);
243 if (!subprocess->Start(this, command)) {
244 delete subprocess;
245 return 0;
246 }
247 running_.push_back(subprocess);
248 return subprocess;
249}
250
251#ifdef USE_PPOLL
252bool SubprocessSet::DoWork() {
253 vector<pollfd> fds;
254 nfds_t nfds = 0;
255
256 for (vector<Subprocess*>::iterator i = running_.begin();
257 i != running_.end(); ++i) {
258 int fd = (*i)->fd_;
259 if (fd < 0)
260 continue;
261 pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
262 fds.push_back(pfd);
263 ++nfds;
264 }
265
266 interrupted_ = 0;
267 int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_);
268 if (ret == -1) {
269 if (errno != EINTR) {
270 perror("ninja: ppoll");
271 return false;
272 }
273 return IsInterrupted();
274 }
275
276 HandlePendingInterruption();
277 if (IsInterrupted())
278 return true;
279
280 nfds_t cur_nfd = 0;
281 for (vector<Subprocess*>::iterator i = running_.begin();
282 i != running_.end(); ) {
283 int fd = (*i)->fd_;
284 if (fd < 0)
285 continue;
286 assert(fd == fds[cur_nfd].fd);
287 if (fds[cur_nfd++].revents) {
288 (*i)->OnPipeReady();
289 if ((*i)->Done()) {
290 finished_.push(*i);
291 i = running_.erase(i);
292 continue;
293 }
294 }
295 ++i;
296 }
297
298 return IsInterrupted();
299}
300
301#else // !defined(USE_PPOLL)
302bool SubprocessSet::DoWork() {
303 fd_set set;
304 int nfds = 0;
305 FD_ZERO(&set);
306
307 for (vector<Subprocess*>::iterator i = running_.begin();
308 i != running_.end(); ++i) {
309 int fd = (*i)->fd_;
310 if (fd >= 0) {
311 FD_SET(fd, &set);
312 if (nfds < fd+1)
313 nfds = fd+1;
314 }
315 }
316
317 interrupted_ = 0;
318 int ret = pselect(nfds, &set, 0, 0, 0, &old_mask_);
319 if (ret == -1) {
320 if (errno != EINTR) {
321 perror("ninja: pselect");
322 return false;
323 }
324 return IsInterrupted();
325 }
326
327 HandlePendingInterruption();
328 if (IsInterrupted())
329 return true;
330
331 for (vector<Subprocess*>::iterator i = running_.begin();
332 i != running_.end(); ) {
333 int fd = (*i)->fd_;
334 if (fd >= 0 && FD_ISSET(fd, &set)) {
335 (*i)->OnPipeReady();
336 if ((*i)->Done()) {
337 finished_.push(*i);
338 i = running_.erase(i);
339 continue;
340 }
341 }
342 ++i;
343 }
344
345 return IsInterrupted();
346}
347#endif // !defined(USE_PPOLL)
348
349Subprocess* SubprocessSet::NextFinished() {
350 if (finished_.empty())
351 return NULL;
352 Subprocess* subproc = finished_.front();
353 finished_.pop();
354 return subproc;
355}
356
357void SubprocessSet::Clear() {
358 for (vector<Subprocess*>::iterator i = running_.begin();
359 i != running_.end(); ++i)
360 // Since the foreground process is in our process group, it will receive
361 // the interruption signal (i.e. SIGINT or SIGTERM) at the same time as us.
362 if (!(*i)->use_console_)
363 kill(-(*i)->pid_, interrupted_);
364 for (vector<Subprocess*>::iterator i = running_.begin();
365 i != running_.end(); ++i)
366 delete *i;
367 running_.clear();
368}
369