summaryrefslogtreecommitdiffstats
path: root/src/subprocess.cc
diff options
context:
space:
mode:
authorEvan Martin <martine@danga.com>2010-12-05 00:09:50 (GMT)
committerEvan Martin <martine@danga.com>2010-12-05 00:09:50 (GMT)
commit42d237e7841679b27cd5dcae82d143d86a87a807 (patch)
tree253b0cf88ecf3b0404146a7c7945dd4c660afa04 /src/subprocess.cc
parentd0025e234cf3fc0a13f45d179880578285609b59 (diff)
downloadNinja-42d237e7841679b27cd5dcae82d143d86a87a807.zip
Ninja-42d237e7841679b27cd5dcae82d143d86a87a807.tar.gz
Ninja-42d237e7841679b27cd5dcae82d143d86a87a807.tar.bz2
move src into subdir
Diffstat (limited to 'src/subprocess.cc')
-rw-r--r--src/subprocess.cc170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/subprocess.cc b/src/subprocess.cc
new file mode 100644
index 0000000..7bf4fc3
--- /dev/null
+++ b/src/subprocess.cc
@@ -0,0 +1,170 @@
+#include "subprocess.h"
+
+#include <algorithm>
+#include <map>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "util.h"
+
+Subprocess::Stream::Stream() : fd_(-1) {}
+Subprocess::Stream::~Stream() {
+ if (fd_ >= 0)
+ close(fd_);
+}
+
+Subprocess::Subprocess() : pid_(-1) {}
+Subprocess::~Subprocess() {
+ // Reap child if forgotten.
+ if (pid_ != -1)
+ Finish();
+}
+
+bool Subprocess::Start(const string& command) {
+ int stdout_pipe[2];
+ if (pipe(stdout_pipe) < 0)
+ Fatal("pipe: %s", strerror(errno));
+ stdout_.fd_ = stdout_pipe[0];
+
+ int stderr_pipe[2];
+ if (pipe(stderr_pipe) < 0)
+ Fatal("pipe: %s", strerror(errno));
+ stderr_.fd_ = stderr_pipe[0];
+
+ pid_ = fork();
+ if (pid_ < 0)
+ Fatal("fork: %s", strerror(errno));
+
+ if (pid_ == 0) {
+ close(stdout_pipe[0]);
+ close(stderr_pipe[0]);
+
+ // Track which fd we use to report errors on.
+ int error_pipe = stderr_pipe[1];
+ do {
+ // Open /dev/null over stdin.
+ int devnull = open("/dev/null", O_WRONLY);
+ if (devnull < 0)
+ break;
+ if (dup2(devnull, 0) < 0)
+ break;
+ close(devnull);
+
+ if (dup2(stdout_pipe[1], 1) < 0 ||
+ dup2(stderr_pipe[1], 2) < 0)
+ break;
+
+ // Now can use stderr for errors.
+ error_pipe = 2;
+ close(stdout_pipe[1]);
+ close(stderr_pipe[1]);
+
+ execl("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL);
+ } while (false);
+
+ // If we get here, something went wrong; the execl should have
+ // replaced us.
+ char* err = strerror(errno);
+ write(error_pipe, err, strlen(err));
+ _exit(1);
+ }
+
+ close(stdout_pipe[1]);
+ close(stderr_pipe[1]);
+ return true;
+}
+
+void Subprocess::OnFDReady(int fd) {
+ char buf[4 << 10];
+ ssize_t len = read(fd, buf, sizeof(buf));
+ Stream* stream = fd == stdout_.fd_ ? &stdout_ : &stderr_;
+ if (len > 0) {
+ stream->buf_.append(buf, len);
+ } else {
+ if (len < 0)
+ Fatal("read: %s", strerror(errno));
+ close(stream->fd_);
+ stream->fd_ = -1;
+ }
+}
+
+bool Subprocess::Finish() {
+ assert(pid_ != -1);
+ int status;
+ if (waitpid(pid_, &status, 0) < 0)
+ Fatal("waitpid(%d): %s", pid_, strerror(errno));
+ pid_ = -1;
+
+ if (WIFEXITED(status)) {
+ int exit = WEXITSTATUS(status);
+ if (exit == 0)
+ return true;
+ }
+ return false;
+}
+
+void SubprocessSet::Add(Subprocess* subprocess) {
+ running_.push_back(subprocess);
+}
+
+void SubprocessSet::DoWork() {
+ vector<pollfd> fds;
+
+ map<int, Subprocess*> fd_to_subprocess;
+ for (vector<Subprocess*>::iterator i = running_.begin();
+ i != running_.end(); ++i) {
+ int fd = (*i)->stdout_.fd_;
+ if (fd >= 0) {
+ fd_to_subprocess[fd] = *i;
+ fds.resize(fds.size() + 1);
+ pollfd* newfd = &fds.back();
+ newfd->fd = fd;
+ newfd->events = POLLIN;
+ newfd->revents = 0;
+ }
+ fd = (*i)->stderr_.fd_;
+ if (fd >= 0) {
+ fd_to_subprocess[fd] = *i;
+ fds.resize(fds.size() + 1);
+ pollfd* newfd = &fds.back();
+ newfd->fd = fd;
+ newfd->events = POLLIN;
+ newfd->revents = 0;
+ }
+ }
+
+ int ret = poll(fds.data(), fds.size(), -1);
+ if (ret == -1) {
+ if (errno != EINTR)
+ perror("poll");
+ return;
+ }
+
+ for (size_t i = 0; i < fds.size(); ++i) {
+ if (fds[i].revents) {
+ Subprocess* subproc = fd_to_subprocess[fds[i].fd];
+ if (fds[i].revents) {
+ subproc->OnFDReady(fds[i].fd);
+ if (subproc->done()) {
+ finished_.push(subproc);
+ std::remove(running_.begin(), running_.end(), subproc);
+ running_.resize(running_.size() - 1);
+ }
+ }
+ }
+ }
+}
+
+Subprocess* SubprocessSet::NextFinished() {
+ if (finished_.empty())
+ return NULL;
+ Subprocess* subproc = finished_.front();
+ finished_.pop();
+ return subproc;
+}