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