Ninja
subprocess-posix.cc
Go to the documentation of this file.
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 
26 #include "util.h"
27 
28 Subprocess::Subprocess(bool use_console) : fd_(-1), pid_(-1),
29  use_console_(use_console) {
30 }
32  if (fd_ >= 0)
33  close(fd_);
34  // Reap child if forgotten.
35  if (pid_ != -1)
36  Finish();
37 }
38 
39 bool Subprocess::Start(SubprocessSet* set, const string& command) {
40  int output_pipe[2];
41  if (pipe(output_pipe) < 0)
42  Fatal("pipe: %s", strerror(errno));
43  fd_ = output_pipe[0];
44 #if !defined(USE_PPOLL)
45  // If available, we use ppoll in DoWork(); otherwise we use pselect
46  // and so must avoid overly-large FDs.
47  if (fd_ >= static_cast<int>(FD_SETSIZE))
48  Fatal("pipe: %s", strerror(EMFILE));
49 #endif // !USE_PPOLL
51 
52  pid_ = fork();
53  if (pid_ < 0)
54  Fatal("fork: %s", strerror(errno));
55 
56  if (pid_ == 0) {
57  close(output_pipe[0]);
58 
59  // Track which fd we use to report errors on.
60  int error_pipe = output_pipe[1];
61  do {
62  if (sigaction(SIGINT, &set->old_act_, 0) < 0)
63  break;
64  if (sigprocmask(SIG_SETMASK, &set->old_mask_, 0) < 0)
65  break;
66 
67  if (!use_console_) {
68  // Put the child in its own process group, so ctrl-c won't reach it.
69  if (setpgid(0, 0) < 0)
70  break;
71 
72  // Open /dev/null over stdin.
73  int devnull = open("/dev/null", O_RDONLY);
74  if (devnull < 0)
75  break;
76  if (dup2(devnull, 0) < 0)
77  break;
78  close(devnull);
79 
80  if (dup2(output_pipe[1], 1) < 0 ||
81  dup2(output_pipe[1], 2) < 0)
82  break;
83 
84  // Now can use stderr for errors.
85  error_pipe = 2;
86  close(output_pipe[1]);
87  }
88  // In the console case, output_pipe is still inherited by the child and
89  // closed when the subprocess finishes, which then notifies ninja.
90 
91  execl("/bin/sh", "/bin/sh", "-c", command.c_str(), (char *) NULL);
92  } while (false);
93 
94  // If we get here, something went wrong; the execl should have
95  // replaced us.
96  char* err = strerror(errno);
97  if (write(error_pipe, err, strlen(err)) < 0) {
98  // If the write fails, there's nothing we can do.
99  // But this block seems necessary to silence the warning.
100  }
101  _exit(1);
102  }
103 
104  close(output_pipe[1]);
105  return true;
106 }
107 
109  char buf[4 << 10];
110  ssize_t len = read(fd_, buf, sizeof(buf));
111  if (len > 0) {
112  buf_.append(buf, len);
113  } else {
114  if (len < 0)
115  Fatal("read: %s", strerror(errno));
116  close(fd_);
117  fd_ = -1;
118  }
119 }
120 
122  assert(pid_ != -1);
123  int status;
124  if (waitpid(pid_, &status, 0) < 0)
125  Fatal("waitpid(%d): %s", pid_, strerror(errno));
126  pid_ = -1;
127 
128  if (WIFEXITED(status)) {
129  int exit = WEXITSTATUS(status);
130  if (exit == 0)
131  return ExitSuccess;
132  } else if (WIFSIGNALED(status)) {
133  if (WTERMSIG(status) == SIGINT)
134  return ExitInterrupted;
135  }
136  return ExitFailure;
137 }
138 
139 bool Subprocess::Done() const {
140  return fd_ == -1;
141 }
142 
143 const string& Subprocess::GetOutput() const {
144  return buf_;
145 }
146 
148 
150  (void) signum;
151  interrupted_ = true;
152 }
153 
155  sigset_t set;
156  sigemptyset(&set);
157  sigaddset(&set, SIGINT);
158  if (sigprocmask(SIG_BLOCK, &set, &old_mask_) < 0)
159  Fatal("sigprocmask: %s", strerror(errno));
160 
161  struct sigaction act;
162  memset(&act, 0, sizeof(act));
163  act.sa_handler = SetInterruptedFlag;
164  if (sigaction(SIGINT, &act, &old_act_) < 0)
165  Fatal("sigaction: %s", strerror(errno));
166 }
167 
169  Clear();
170 
171  if (sigaction(SIGINT, &old_act_, 0) < 0)
172  Fatal("sigaction: %s", strerror(errno));
173  if (sigprocmask(SIG_SETMASK, &old_mask_, 0) < 0)
174  Fatal("sigprocmask: %s", strerror(errno));
175 }
176 
177 Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
178  Subprocess *subprocess = new Subprocess(use_console);
179  if (!subprocess->Start(this, command)) {
180  delete subprocess;
181  return 0;
182  }
183  running_.push_back(subprocess);
184  return subprocess;
185 }
186 
187 #ifdef USE_PPOLL
188 bool SubprocessSet::DoWork() {
189  vector<pollfd> fds;
190  nfds_t nfds = 0;
191 
192  for (vector<Subprocess*>::iterator i = running_.begin();
193  i != running_.end(); ++i) {
194  int fd = (*i)->fd_;
195  if (fd < 0)
196  continue;
197  pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
198  fds.push_back(pfd);
199  ++nfds;
200  }
201 
202  interrupted_ = false;
203  int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_);
204  if (ret == -1) {
205  if (errno != EINTR) {
206  perror("ninja: ppoll");
207  return false;
208  }
209  return interrupted_;
210  }
211 
212  nfds_t cur_nfd = 0;
213  for (vector<Subprocess*>::iterator i = running_.begin();
214  i != running_.end(); ) {
215  int fd = (*i)->fd_;
216  if (fd < 0)
217  continue;
218  assert(fd == fds[cur_nfd].fd);
219  if (fds[cur_nfd++].revents) {
220  (*i)->OnPipeReady();
221  if ((*i)->Done()) {
222  finished_.push(*i);
223  i = running_.erase(i);
224  continue;
225  }
226  }
227  ++i;
228  }
229 
230  return interrupted_;
231 }
232 
233 #else // !defined(USE_PPOLL)
235  fd_set set;
236  int nfds = 0;
237  FD_ZERO(&set);
238 
239  for (vector<Subprocess*>::iterator i = running_.begin();
240  i != running_.end(); ++i) {
241  int fd = (*i)->fd_;
242  if (fd >= 0) {
243  FD_SET(fd, &set);
244  if (nfds < fd+1)
245  nfds = fd+1;
246  }
247  }
248 
249  interrupted_ = false;
250  int ret = pselect(nfds, &set, 0, 0, 0, &old_mask_);
251  if (ret == -1) {
252  if (errno != EINTR) {
253  perror("ninja: pselect");
254  return false;
255  }
256  return interrupted_;
257  }
258 
259  for (vector<Subprocess*>::iterator i = running_.begin();
260  i != running_.end(); ) {
261  int fd = (*i)->fd_;
262  if (fd >= 0 && FD_ISSET(fd, &set)) {
263  (*i)->OnPipeReady();
264  if ((*i)->Done()) {
265  finished_.push(*i);
266  i = running_.erase(i);
267  continue;
268  }
269  }
270  ++i;
271  }
272 
273  return interrupted_;
274 }
275 #endif // !defined(USE_PPOLL)
276 
278  if (finished_.empty())
279  return NULL;
280  Subprocess* subproc = finished_.front();
281  finished_.pop();
282  return subproc;
283 }
284 
286  for (vector<Subprocess*>::iterator i = running_.begin();
287  i != running_.end(); ++i)
288  // Since the foreground process is in our process group, it will receive a
289  // SIGINT at the same time as us.
290  if (!(*i)->use_console_)
291  kill(-(*i)->pid_, SIGINT);
292  for (vector<Subprocess*>::iterator i = running_.begin();
293  i != running_.end(); ++i)
294  delete *i;
295  running_.clear();
296 }
SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
Definition: subprocess.h:75
bool Done() const
bool use_console_
Definition: subprocess.h:67
static void SetInterruptedFlag(int signum)
Subprocess * NextFinished()
vector< Subprocess * > running_
Definition: subprocess.h:84
void SetCloseOnExec(int fd)
Mark a file descriptor to not be inherited on exec()s.
Definition: util.cc:304
string buf_
Definition: subprocess.h:51
ExitStatus Finish()
Returns ExitSuccess on successful process exit, ExitInterrupted if the process was interrupted...
const string & GetOutput() const
Subprocess * Add(const string &command, bool use_console=false)
Subprocess wraps a single async subprocess.
Definition: subprocess.h:35
ExitStatus
Definition: exit_status.h:18
static bool interrupted_
Definition: subprocess.h:92
Subprocess(bool use_console)
sigset_t old_mask_
Definition: subprocess.h:95
bool Start(struct SubprocessSet *set, const string &command)
void Fatal(const char *msg,...)
Log a fatal message and exit.
Definition: util.cc:52
pid_t pid_
Definition: subprocess.h:65
queue< Subprocess * > finished_
Definition: subprocess.h:85
struct sigaction old_act_
Definition: subprocess.h:94