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