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