X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FSubprocess.h;h=ea2a062afd16dd12acbc97ebfefa708c52d6070c;hp=0300fd8efdeac65f711e2fb5c503ccf83535671d;hb=297d72d1ee6b5b8e70a6f61224f44b275e155bc3;hpb=8f27d856f0e5287b54533efe44d0797d5e50bc1e diff --git a/folly/Subprocess.h b/folly/Subprocess.h index 0300fd8e..ea2a062a 100644 --- a/folly/Subprocess.h +++ b/folly/Subprocess.h @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,13 +33,13 @@ * to complete, returning the exit status. * * A thread-safe [1] version of popen() (type="r", to read from the child): - * Subprocess proc(cmd, Subprocess::pipeStdout()); - * // read from proc.stdout() + * Subprocess proc(cmd, Subprocess::Options().pipeStdout()); + * // read from proc.stdoutFd() * proc.wait(); * * A thread-safe [1] version of popen() (type="w", to write to the child): - * Subprocess proc(cmd, Subprocess::pipeStdin()); - * // write to proc.stdin() + * Subprocess proc(cmd, Subprocess::Options().pipeStdin()); + * // write to proc.stdinFd() * proc.wait(); * * If you want to redirect both stdin and stdout to pipes, you can, but note @@ -106,17 +106,18 @@ #include #include -#include #include #include #include #include -#include -#include #include +#include #include #include +#include +#include +#include namespace folly { @@ -208,7 +209,10 @@ class ProcessReturnCode { /** * Base exception thrown by the Subprocess methods. */ -class SubprocessError : public std::exception {}; +class SubprocessError : public std::runtime_error { + public: + using std::runtime_error::runtime_error; +}; /** * Exception thrown by *Checked methods of Subprocess. @@ -216,12 +220,10 @@ class SubprocessError : public std::exception {}; class CalledProcessError : public SubprocessError { public: explicit CalledProcessError(ProcessReturnCode rc); - ~CalledProcessError() throw() = default; - const char* what() const throw() override { return what_.c_str(); } + ~CalledProcessError() throw() override = default; ProcessReturnCode returnCode() const { return returnCode_; } private: ProcessReturnCode returnCode_; - std::string what_; }; /** @@ -230,13 +232,11 @@ class CalledProcessError : public SubprocessError { class SubprocessSpawnError : public SubprocessError { public: SubprocessSpawnError(const char* executable, int errCode, int errnoValue); - ~SubprocessSpawnError() throw() = default; - const char* what() const throw() override { return what_.c_str(); } + ~SubprocessSpawnError() throw() override = default; int errnoValue() const { return errnoValue_; } private: int errnoValue_; - std::string what_; }; /** @@ -274,7 +274,7 @@ class Subprocess { * the close-on-exec flag is set (fcntl FD_CLOEXEC) and inherited * otherwise. */ - class Options : private boost::orable { + class Options { friend class Subprocess; public: Options() {} // E.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58328 @@ -298,19 +298,19 @@ class Subprocess { /** * Shortcut to change the action for standard input. */ - Options& stdin(int action) { return fd(STDIN_FILENO, action); } + Options& stdinFd(int action) { return fd(STDIN_FILENO, action); } /** * Shortcut to change the action for standard output. */ - Options& stdout(int action) { return fd(STDOUT_FILENO, action); } + Options& stdoutFd(int action) { return fd(STDOUT_FILENO, action); } /** * Shortcut to change the action for standard error. * Note that stderr(1) will redirect the standard error to the same * file descriptor as standard output; the equivalent of bash's "2>&1" */ - Options& stderr(int action) { return fd(STDERR_FILENO, action); } + Options& stderrFd(int action) { return fd(STDERR_FILENO, action); } Options& pipeStdin() { return fd(STDIN_FILENO, PIPE_IN); } Options& pipeStdout() { return fd(STDOUT_FILENO, PIPE_OUT); } @@ -397,10 +397,28 @@ class Subprocess { return *this; } +#if __linux__ /** - * Helpful way to combine Options. + * This is an experimental feature, it is best you don't use it at this + * point of time. + * Although folly would support cloning with custom flags in some form, this + * API might change in the near future. So use the following assuming it is + * experimental. (Apr 11, 2017) + * + * This unlocks Subprocess to support clone flags, many of them need + * CAP_SYS_ADMIN permissions. It might also require you to go through the + * implementation to understand what happens before, between and after the + * fork-and-exec. + * + * `man 2 clone` would be a starting point for knowing about the available + * flags. */ - Options& operator|=(const Options& other); + using clone_flags_t = uint64_t; + Options& useCloneWithFlags(clone_flags_t cloneFlags) noexcept { + cloneFlags_ = cloneFlags; + return *this; + } +#endif private: typedef boost::container::flat_map FdMap; @@ -414,12 +432,13 @@ class Subprocess { bool processGroupLeader_{false}; DangerousPostForkPreExecCallback* dangerousPostForkPreExecCallback_{nullptr}; +#if __linux__ + // none means `vfork()` instead of a custom `clone()` + // Optional<> is used because value of '0' means do clone without any flags. + Optional cloneFlags_; +#endif }; - static Options pipeStdin() { return Options().stdin(PIPE); } - static Options pipeStdout() { return Options().stdout(PIPE); } - static Options pipeStderr() { return Options().stderr(PIPE); } - // Non-copiable, but movable Subprocess(const Subprocess&) = delete; Subprocess& operator=(const Subprocess&) = delete; @@ -462,8 +481,6 @@ class Subprocess { const Options& options = Options(), const std::vector* env = nullptr); - static std::vector shellify(const std::string& cmd); - //// //// The methods below only manipulate the process state, and do not //// affect its communication pipes. @@ -492,7 +509,7 @@ class Subprocess { * e.g. if you wait for the underlying process without going through this * Subprocess instance. */ - ProcessReturnCode poll(); + ProcessReturnCode poll(struct rusage* ru = nullptr); /** * Poll the child's status. If the process is still running, return false. @@ -770,9 +787,9 @@ class Subprocess { int parentFd(int childFd) const { return pipes_[findByChildFd(childFd)].pipe.fd(); } - int stdin() const { return parentFd(0); } - int stdout() const { return parentFd(1); } - int stderr() const { return parentFd(2); } + int stdinFd() const { return parentFd(0); } + int stdoutFd() const { return parentFd(1); } + int stderrFd() const { return parentFd(2); } /** * The child's pipes are logically separate from the process metadata @@ -864,17 +881,4 @@ class Subprocess { std::vector pipes_; }; -inline Subprocess::Options& Subprocess::Options::operator|=( - const Subprocess::Options& other) { - if (this == &other) return *this; - // Replace - for (auto& p : other.fdActions_) { - fdActions_[p.first] = p.second; - } - closeOtherFds_ |= other.closeOtherFds_; - usePath_ |= other.usePath_; - processGroupLeader_ |= other.processGroupLeader_; - return *this; -} - } // namespace folly