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