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 
37 using namespace std;
38 
Subprocess(bool use_console)39 Subprocess::Subprocess(bool use_console) : fd_(-1), pid_(-1),
40                                            use_console_(use_console) {
41 }
42 
~Subprocess()43 Subprocess::~Subprocess() {
44   if (fd_ >= 0)
45     close(fd_);
46   // Reap child if forgotten.
47   if (pid_ != -1)
48     Finish();
49 }
50 
Start(SubprocessSet * set,const string & command)51 bool 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 
OnPipeReady()137 void 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 
Finish()150 ExitStatus 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 
Done() const179 bool Subprocess::Done() const {
180   return fd_ == -1;
181 }
182 
GetOutput() const183 const string& Subprocess::GetOutput() const {
184   return buf_;
185 }
186 
187 int SubprocessSet::interrupted_;
188 
SetInterruptedFlag(int signum)189 void SubprocessSet::SetInterruptedFlag(int signum) {
190   interrupted_ = signum;
191 }
192 
HandlePendingInterruption()193 void 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 
SubprocessSet()208 SubprocessSet::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 
~SubprocessSet()228 SubprocessSet::~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 
Add(const string & command,bool use_console)241 Subprocess *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
DoWork()252 bool 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)
DoWork()302 bool 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 
NextFinished()349 Subprocess* SubprocessSet::NextFinished() {
350   if (finished_.empty())
351     return NULL;
352   Subprocess* subproc = finished_.front();
353   finished_.pop();
354   return subproc;
355 }
356 
Clear()357 void 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