void Subprocess::setAllNonBlocking() { for (auto& p : pipes_) { int fd = p.parentFd; int flags = ::fcntl(fd, F_GETFL); checkUnixError(flags, "fcntl"); int r = ::fcntl(fd, F_SETFL, flags | O_NONBLOCK); checkUnixError(r, "fcntl"); } }
ProcessReturnCode Subprocess::poll() { returnCode_.enforce(ProcessReturnCode::RUNNING); DCHECK_GT(pid_, 0); int status; pid_t found = ::waitpid(pid_, &status, WNOHANG); checkUnixError(found, "waitpid"); if (found != 0) { returnCode_ = ProcessReturnCode(status); pid_ = -1; } return returnCode_; }
AsyncIO::AsyncIO(size_t capacity, PollMode pollMode) : ctx_(0), pending_(0), capacity_(capacity), pollFd_(-1) { CHECK_GT(capacity_, 0); completed_.reserve(capacity_); if (pollMode == POLLABLE) { pollFd_ = eventfd(0, EFD_NONBLOCK); checkUnixError(pollFd_, "AsyncIO: eventfd creation failed"); } }
ProcessReturnCode Subprocess::wait() { returnCode_.enforce(ProcessReturnCode::RUNNING); DCHECK_GT(pid_, 0); int status; pid_t found; do { found = ::waitpid(pid_, &status, 0); } while (found == -1 && errno == EINTR); checkUnixError(found, "waitpid"); DCHECK_EQ(found, pid_); returnCode_ = ProcessReturnCode(status); return returnCode_; }
ProcessReturnCode Subprocess::poll() { returnCode_.enforce(ProcessReturnCode::RUNNING); DCHECK_GT(pid_, 0); int status; pid_t found = ::waitpid(pid_, &status, WNOHANG); checkUnixError(found, "waitpid"); if (found != 0) { // Though the child process had quit, this call does not close the pipes // since its descendants may still be using them. returnCode_ = ProcessReturnCode(status); pid_ = -1; } return returnCode_; }
ProcessReturnCode Subprocess::wait() { returnCode_.enforce(ProcessReturnCode::RUNNING); DCHECK_GT(pid_, 0); int status; pid_t found; do { found = ::waitpid(pid_, &status, 0); } while (found == -1 && errno == EINTR); checkUnixError(found, "waitpid"); // Though the child process had quit, this call does not close the pipes // since its descendants may still be using them. DCHECK_EQ(found, pid_); returnCode_ = ProcessReturnCode(status); pid_ = -1; return returnCode_; }
TemporaryFile::TemporaryFile(StringPiece namePrefix, fs::path dir, Scope scope, bool closeOnDestruction) : scope_(scope), closeOnDestruction_(closeOnDestruction), fd_(-1), path_(generateUniquePath(std::move(dir), namePrefix)) { fd_ = open(path_.string().c_str(), O_RDWR | O_CREAT | O_EXCL, 0666); checkUnixError(fd_, "open failed"); if (scope_ == Scope::UNLINK_IMMEDIATELY) { boost::system::error_code ec; fs::remove(path_, ec); if (ec) { LOG(WARNING) << "unlink on construction failed: " << ec; } else { path_.clear(); } } }
Range<AsyncIO::Op**> AsyncIO::pollCompleted() { CHECK(ctx_); CHECK_NE(pollFd_, -1) << "pollCompleted() only allowed on pollable object"; uint64_t numEvents; // This sets the eventFd counter to 0, see // http://www.kernel.org/doc/man-pages/online/pages/man2/eventfd.2.html ssize_t rc; do { rc = ::read(pollFd_, &numEvents, 8); } while (rc == -1 && errno == EINTR); if (UNLIKELY(rc == -1 && errno == EAGAIN)) { return Range<Op**>(); // nothing completed } checkUnixError(rc, "AsyncIO: read from event fd failed"); DCHECK_EQ(rc, 8); DCHECK_GT(numEvents, 0); DCHECK_LE(numEvents, pending_); // Don't reap more than numEvents, as we've just reset the counter to 0. return doWait(numEvents, numEvents); }
void Subprocess::communicate(FdCallback readCallback, FdCallback writeCallback) { returnCode_.enforce(ProcessReturnCode::RUNNING); setAllNonBlocking(); std::vector<pollfd> fds; fds.reserve(pipes_.size()); std::vector<int> toClose; toClose.reserve(pipes_.size()); while (!pipes_.empty()) { fds.clear(); toClose.clear(); for (auto& p : pipes_) { pollfd pfd; pfd.fd = p.parentFd; // Yes, backwards, PIPE_IN / PIPE_OUT are defined from the // child's point of view. if (!p.enabled) { // Still keeping fd in watched set so we get notified of POLLHUP / // POLLERR pfd.events = 0; } else if (p.direction == PIPE_IN) { pfd.events = POLLOUT; } else { pfd.events = POLLIN; } fds.push_back(pfd); } int r; do { r = ::poll(fds.data(), fds.size(), -1); } while (r == -1 && errno == EINTR); checkUnixError(r, "poll"); for (int i = 0; i < pipes_.size(); ++i) { auto& p = pipes_[i]; DCHECK_EQ(fds[i].fd, p.parentFd); short events = fds[i].revents; bool closed = false; if (events & POLLOUT) { DCHECK(!(events & POLLIN)); if (writeCallback(p.parentFd, p.childFd)) { toClose.push_back(i); closed = true; } } if (events & POLLIN) { DCHECK(!(events & POLLOUT)); if (readCallback(p.parentFd, p.childFd)) { toClose.push_back(i); closed = true; } } if ((events & (POLLHUP | POLLERR)) && !closed) { toClose.push_back(i); closed = true; } } // Close the fds in reverse order so the indexes hold after erase() for (int idx : boost::adaptors::reverse(toClose)) { auto pos = pipes_.begin() + idx; closeChecked(pos->parentFd); pipes_.erase(pos); } } }
void Subprocess::sendSignal(int signal) { returnCode_.enforce(ProcessReturnCode::RUNNING); int r = ::kill(pid_, signal); checkUnixError(r, "kill"); }
void Subprocess::spawnInternal( std::unique_ptr<const char*[]> argv, const char* executable, Options& options, const std::vector<std::string>* env, int errFd) { // Parent work, pre-fork: create pipes std::vector<int> childFds; // Close all of the childFds as we leave this scope SCOPE_EXIT { // These are only pipes, closing them shouldn't fail for (int cfd : childFds) { CHECK_ERR(::close(cfd)); } }; int r; for (auto& p : options.fdActions_) { if (p.second == PIPE_IN || p.second == PIPE_OUT) { int fds[2]; r = ::pipe(fds); checkUnixError(r, "pipe"); PipeInfo pinfo; pinfo.direction = p.second; int cfd; if (p.second == PIPE_IN) { // Child gets reading end pinfo.parentFd = fds[1]; cfd = fds[0]; } else { pinfo.parentFd = fds[0]; cfd = fds[1]; } p.second = cfd; // ensure it gets dup2()ed pinfo.childFd = p.first; childFds.push_back(cfd); pipes_.push_back(pinfo); } } // This should already be sorted, as options.fdActions_ is DCHECK(std::is_sorted(pipes_.begin(), pipes_.end())); // Note that the const casts below are legit, per // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html char** argVec = const_cast<char**>(argv.get()); // Set up environment std::unique_ptr<const char*[]> envHolder; char** envVec; if (env) { envHolder = cloneStrings(*env); envVec = const_cast<char**>(envHolder.get()); } else { envVec = environ; } // Block all signals around vfork; see http://ewontfix.com/7/. // // As the child may run in the same address space as the parent until // the actual execve() system call, any (custom) signal handlers that // the parent has might alter parent's memory if invoked in the child, // with undefined results. So we block all signals in the parent before // vfork(), which will cause them to be blocked in the child as well (we // rely on the fact that Linux, just like all sane implementations, only // clones the calling thread). Then, in the child, we reset all signals // to their default dispositions (while still blocked), and unblock them // (so the exec()ed process inherits the parent's signal mask) // // The parent also unblocks all signals as soon as vfork() returns. sigset_t allBlocked; r = sigfillset(&allBlocked); checkUnixError(r, "sigfillset"); sigset_t oldSignals; r = pthread_sigmask(SIG_SETMASK, &allBlocked, &oldSignals); checkPosixError(r, "pthread_sigmask"); SCOPE_EXIT { // Restore signal mask r = pthread_sigmask(SIG_SETMASK, &oldSignals, nullptr); CHECK_EQ(r, 0) << "pthread_sigmask: " << errnoStr(r); // shouldn't fail }; pid_t pid = vfork(); if (pid == 0) { int errnoValue = prepareChild(options, &oldSignals); if (errnoValue != 0) { childError(errFd, kChildFailure, errnoValue); } errnoValue = runChild(executable, argVec, envVec, options); // If we get here, exec() failed. childError(errFd, kExecFailure, errnoValue); } // In parent. Make sure vfork() succeeded. checkUnixError(pid, errno, "vfork"); // Child is alive. We have to be very careful about throwing after this // point. We are inside the constructor, so if we throw the Subprocess // object will have never existed, and the destructor will never be called. // // We should only throw if we got an error via the errFd, and we know the // child has exited and can be immediately waited for. In all other cases, // we have no way of cleaning up the child. pid_ = pid; returnCode_ = ProcessReturnCode(RV_RUNNING); }
void Subprocess::spawn( std::unique_ptr<const char*[]> argv, const char* executable, const Options& optionsIn, const std::vector<std::string>* env) { if (optionsIn.usePath_ && env) { throw std::invalid_argument( "usePath() not allowed when overriding environment"); } // Make a copy, we'll mutate options Options options(optionsIn); // On error, close all of the pipes_ auto pipesGuard = makeGuard([&] { for (auto& p : this->pipes_) { CHECK_ERR(::close(p.parentFd)); } }); // Create a pipe to use to receive error information from the child, // in case it fails before calling exec() int errFds[2]; int r = ::pipe(errFds); checkUnixError(r, "pipe"); SCOPE_EXIT { CHECK_ERR(::close(errFds[0])); if (errFds[1] >= 0) { CHECK_ERR(::close(errFds[1])); } }; // Ask the child to close the read end of the error pipe. options.fdActions_[errFds[0]] = CLOSE; // Set the close-on-exec flag on the write side of the pipe. // This way the pipe will be closed automatically in the child if execve() // succeeds. If the exec fails the child can write error information to the // pipe. r = fcntl(errFds[1], F_SETFD, FD_CLOEXEC); checkUnixError(r, "set FD_CLOEXEC"); // Perform the actual work of setting up pipes then forking and // executing the child. spawnInternal(std::move(argv), executable, options, env, errFds[1]); // After spawnInternal() returns the child is alive. We have to be very // careful about throwing after this point. We are inside the constructor, // so if we throw the Subprocess object will have never existed, and the // destructor will never be called. // // We should only throw if we got an error via the errFd, and we know the // child has exited and can be immediately waited for. In all other cases, // we have no way of cleaning up the child. // Close writable side of the errFd pipe in the parent process CHECK_ERR(::close(errFds[1])); errFds[1] = -1; // Read from the errFd pipe, to tell if the child ran into any errors before // calling exec() readChildErrorPipe(errFds[0], executable); // We have fully succeeded now, so release the guard on pipes_ pipesGuard.dismiss(); }
void Subprocess::spawn( std::unique_ptr<const char*[]> argv, const char* executable, const Options& optionsIn, const std::vector<std::string>* env) { if (optionsIn.usePath_ && env) { throw std::invalid_argument( "usePath() not allowed when overriding environment"); } // Make a copy, we'll mutate options Options options(optionsIn); // Parent work, pre-fork: create pipes std::vector<int> childFds; for (auto& p : options.fdActions_) { if (p.second == PIPE_IN || p.second == PIPE_OUT) { int fds[2]; int r = ::pipe(fds); checkUnixError(r, "pipe"); PipeInfo pinfo; pinfo.direction = p.second; int cfd; if (p.second == PIPE_IN) { // Child gets reading end pinfo.parentFd = fds[1]; cfd = fds[0]; } else { pinfo.parentFd = fds[0]; cfd = fds[1]; } p.second = cfd; // ensure it gets dup2()ed pinfo.childFd = p.first; childFds.push_back(cfd); pipes_.push_back(pinfo); } } // This should already be sorted, as options.fdActions_ is DCHECK(std::is_sorted(pipes_.begin(), pipes_.end())); // Note that the const casts below are legit, per // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html char** argVec = const_cast<char**>(argv.get()); // Set up environment std::unique_ptr<const char*[]> envHolder; char** envVec; if (env) { envHolder = cloneStrings(*env); envVec = const_cast<char**>(envHolder.get()); } else { envVec = environ; } // Block all signals around vfork; see http://ewontfix.com/7/. // // As the child may run in the same address space as the parent until // the actual execve() system call, any (custom) signal handlers that // the parent has might alter parent's memory if invoked in the child, // with undefined results. So we block all signals in the parent before // vfork(), which will cause them to be blocked in the child as well (we // rely on the fact that Linux, just like all sane implementations, only // clones the calling thread). Then, in the child, we reset all signals // to their default dispositions (while still blocked), and unblock them // (so the exec()ed process inherits the parent's signal mask) // // The parent also unblocks all signals as soon as vfork() returns. sigset_t allBlocked; int r = ::sigfillset(&allBlocked); checkUnixError(r, "sigfillset"); sigset_t oldSignals; r = pthread_sigmask(SIG_SETMASK, &allBlocked, &oldSignals); checkPosixError(r, "pthread_sigmask"); pid_t pid = vfork(); if (pid == 0) { // While all signals are blocked, we must reset their // dispositions to default. for (int sig = 1; sig < NSIG; ++sig) { ::signal(sig, SIG_DFL); } // Unblock signals; restore signal mask. int r = pthread_sigmask(SIG_SETMASK, &oldSignals, nullptr); if (r != 0) abort(); runChild(executable, argVec, envVec, options); // This should never return, but there's nothing else we can do here. abort(); } // In parent. We want to restore the signal mask even if vfork fails, // so we'll save errno here, restore the signal mask, and only then // throw. int savedErrno = errno; // Restore signal mask; do this even if vfork fails! // We only check for errors from pthread_sigmask after we recorded state // that the child is alive, so we know to reap it. r = pthread_sigmask(SIG_SETMASK, &oldSignals, nullptr); checkUnixError(pid, savedErrno, "vfork"); // Child is alive pid_ = pid; returnCode_ = ProcessReturnCode(RV_RUNNING); // Parent work, post-fork: close child's ends of pipes for (int f : childFds) { closeChecked(f); } checkPosixError(r, "pthread_sigmask"); }
void Subprocess::communicate(FdCallback readCallback, FdCallback writeCallback) { // This serves to prevent wait() followed by communicate(), but if you // legitimately need that, send a patch to delete this line. returnCode_.enforce(ProcessReturnCode::RUNNING); setAllNonBlocking(); std::vector<pollfd> fds; fds.reserve(pipes_.size()); std::vector<size_t> toClose; // indexes into pipes_ toClose.reserve(pipes_.size()); while (!pipes_.empty()) { fds.clear(); toClose.clear(); for (auto& p : pipes_) { pollfd pfd; pfd.fd = p.pipe.fd(); // Yes, backwards, PIPE_IN / PIPE_OUT are defined from the // child's point of view. if (!p.enabled) { // Still keeping fd in watched set so we get notified of POLLHUP / // POLLERR pfd.events = 0; } else if (p.direction == PIPE_IN) { pfd.events = POLLOUT; } else { pfd.events = POLLIN; } fds.push_back(pfd); } int r; do { r = ::poll(fds.data(), fds.size(), -1); } while (r == -1 && errno == EINTR); checkUnixError(r, "poll"); for (size_t i = 0; i < pipes_.size(); ++i) { auto& p = pipes_[i]; auto parentFd = p.pipe.fd(); DCHECK_EQ(fds[i].fd, parentFd); short events = fds[i].revents; bool closed = false; if (events & POLLOUT) { DCHECK(!(events & POLLIN)); if (writeCallback(parentFd, p.childFd)) { toClose.push_back(i); closed = true; } } // Call read callback on POLLHUP, to give it a chance to read (and act // on) end of file if (events & (POLLIN | POLLHUP)) { DCHECK(!(events & POLLOUT)); if (readCallback(parentFd, p.childFd)) { toClose.push_back(i); closed = true; } } if ((events & (POLLHUP | POLLERR)) && !closed) { toClose.push_back(i); closed = true; } } // Close the fds in reverse order so the indexes hold after erase() for (int idx : boost::adaptors::reverse(toClose)) { auto pos = pipes_.begin() + idx; pos->pipe.close(); // Throws on error pipes_.erase(pos); } } }