/*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2012-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#endif
#include <fcntl.h>
-#include <array>
#include <algorithm>
+#include <array>
#include <system_error>
#include <boost/container/flat_set.hpp>
#include <glog/logging.h>
-#include <folly/Assume.h>
#include <folly/Conv.h>
#include <folly/Exception.h>
#include <folly/ScopeGuard.h>
-#include <folly/Shell.h>
#include <folly/String.h>
#include <folly/io/Cursor.h>
-#include <folly/portability/Environment.h>
+#include <folly/lang/Assume.h>
#include <folly/portability/Sockets.h>
+#include <folly/portability/Stdlib.h>
+#include <folly/portability/SysSyscall.h>
#include <folly/portability/Unistd.h>
+#include <folly/system/Shell.h>
constexpr int kExecFailure = 127;
constexpr int kChildFailure = 126;
namespace folly {
+ProcessReturnCode ProcessReturnCode::make(int status) {
+ if (!WIFEXITED(status) && !WIFSIGNALED(status)) {
+ throw std::runtime_error(
+ to<std::string>("Invalid ProcessReturnCode: ", status));
+ }
+ return ProcessReturnCode(status);
+}
+
ProcessReturnCode::ProcessReturnCode(ProcessReturnCode&& p) noexcept
: rawStatus_(p.rawStatus_) {
p.rawStatus_ = ProcessReturnCode::RV_NOT_STARTED;
}
ProcessReturnCode::State ProcessReturnCode::state() const {
- if (rawStatus_ == RV_NOT_STARTED) return NOT_STARTED;
- if (rawStatus_ == RV_RUNNING) return RUNNING;
- if (WIFEXITED(rawStatus_)) return EXITED;
- if (WIFSIGNALED(rawStatus_)) return KILLED;
- throw std::runtime_error(to<std::string>(
- "Invalid ProcessReturnCode: ", rawStatus_));
+ if (rawStatus_ == RV_NOT_STARTED) {
+ return NOT_STARTED;
+ }
+ if (rawStatus_ == RV_RUNNING) {
+ return RUNNING;
+ }
+ if (WIFEXITED(rawStatus_)) {
+ return EXITED;
+ }
+ if (WIFSIGNALED(rawStatus_)) {
+ return KILLED;
+ }
+ assume_unreachable();
}
void ProcessReturnCode::enforce(State expected) const {
}
CalledProcessError::CalledProcessError(ProcessReturnCode rc)
- : returnCode_(rc),
- what_(returnCode_.str()) {
-}
+ : SubprocessError(rc.str()), returnCode_(rc) {}
-SubprocessSpawnError::SubprocessSpawnError(const char* executable,
- int errCode,
- int errnoValue)
- : errnoValue_(errnoValue),
- what_(to<std::string>(errCode == kExecFailure ?
- "failed to execute " :
- "error preparing to execute ",
- executable, ": ", errnoStr(errnoValue))) {
+static inline std::string toSubprocessSpawnErrorMessage(
+ char const* executable,
+ int errCode,
+ int errnoValue) {
+ auto prefix = errCode == kExecFailure ? "failed to execute "
+ : "error preparing to execute ";
+ return to<std::string>(prefix, executable, ": ", errnoStr(errnoValue));
}
+SubprocessSpawnError::SubprocessSpawnError(
+ const char* executable,
+ int errCode,
+ int errnoValue)
+ : SubprocessError(
+ toSubprocessSpawnErrorMessage(executable, errCode, errnoValue)),
+ errnoValue_(errnoValue) {}
+
namespace {
// Copy pointers to the given strings in a format suitable for posix_spawn
}
}
-} // namespace
+} // namespace
Subprocess::Options& Subprocess::Options::fd(int fd, int action) {
if (action == Subprocess::PIPE) {
if (argv.empty()) {
throw std::invalid_argument("argv must not be empty");
}
- if (!executable) executable = argv[0].c_str();
+ if (!executable) {
+ executable = argv[0].c_str();
+ }
spawn(cloneStrings(argv), executable, options, env);
}
_exit(errCode);
}
-} // namespace
+} // namespace
void Subprocess::setAllNonBlocking() {
for (auto& p : pipes_) {
int cfd;
if (p.second == PIPE_IN) {
// Child gets reading end
- pipe.pipe = folly::File(fds[1], /*owns_fd=*/ true);
+ pipe.pipe = folly::File(fds[1], /*ownsFd=*/true);
cfd = fds[0];
} else {
- pipe.pipe = folly::File(fds[0], /*owns_fd=*/ true);
+ pipe.pipe = folly::File(fds[0], /*ownsFd=*/true);
cfd = fds[1];
}
p.second = cfd; // ensure it gets dup2()ed
// Call c_str() here, as it's not necessarily safe after fork.
const char* childDir =
options.childDir_.empty() ? nullptr : options.childDir_.c_str();
- pid_t pid = vfork();
+
+ pid_t pid;
+#ifdef __linux__
+ if (options.cloneFlags_) {
+ pid = syscall(SYS_clone, *options.cloneFlags_, 0, nullptr, nullptr);
+ checkUnixError(pid, errno, "clone");
+ } else {
+#endif
+ pid = vfork();
+ checkUnixError(pid, errno, "vfork");
+#ifdef __linux__
+ }
+#endif
if (pid == 0) {
int errnoValue = prepareChild(options, &oldSignals, childDir);
if (errnoValue != 0) {
// 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
// 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);
+ returnCode_ = ProcessReturnCode::makeRunning();
}
int Subprocess::prepareChild(const Options& options,
throw SubprocessSpawnError(executable, info.errCode, info.errnoValue);
}
-ProcessReturnCode Subprocess::poll() {
+ProcessReturnCode Subprocess::poll(struct rusage* ru) {
returnCode_.enforce(ProcessReturnCode::RUNNING);
DCHECK_GT(pid_, 0);
int status;
- pid_t found = ::waitpid(pid_, &status, WNOHANG);
+ pid_t found = ::wait4(pid_, &status, WNOHANG, ru);
// The spec guarantees that EINTR does not occur with WNOHANG, so the only
// two remaining errors are ECHILD (other code reaped the child?), or
// EINVAL (cosmic rays?), both of which merit an abort:
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);
+ returnCode_ = ProcessReturnCode::make(status);
pid_ = -1;
}
return returnCode_;
// 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);
+ returnCode_ = ProcessReturnCode::make(status);
pid_ = -1;
return returnCode_;
}
}
}
-} // namespace
+} // namespace
std::pair<std::string, std::string> Subprocess::communicate(
StringPiece input) {
Initializer initializer;
-} // namespace
+} // namespace
-} // namespace folly
+} // namespace folly