proxygen
Subprocess.h
Go to the documentation of this file.
1 /*
2  * Copyright 2012-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
93 #pragma once
94 
95 #include <signal.h>
96 #include <sys/types.h>
97 
98 #if __APPLE__
99 #include <sys/wait.h>
100 #else
101 #include <wait.h>
102 #endif
103 
104 #include <exception>
105 #include <string>
106 #include <vector>
107 
108 #include <boost/container/flat_map.hpp>
109 #include <boost/operators.hpp>
110 
111 #include <folly/Exception.h>
112 #include <folly/File.h>
113 #include <folly/FileUtil.h>
114 #include <folly/Function.h>
115 #include <folly/MapUtil.h>
116 #include <folly/Optional.h>
117 #include <folly/Portability.h>
118 #include <folly/Range.h>
119 #include <folly/gen/String.h>
120 #include <folly/io/IOBufQueue.h>
122 
123 namespace folly {
124 
128 class Subprocess;
130  public:
131  enum State {
132  // Subprocess starts in the constructor, so this state designates only
133  // default-initialized or moved-out ProcessReturnCodes.
138  };
139 
142  }
143 
146  }
147 
148  static ProcessReturnCode make(int status);
149 
150  // Default-initialized for convenience. Subprocess::returnCode() will
151  // never produce this value.
153 
154  // Trivially copyable
155  ProcessReturnCode(const ProcessReturnCode& p) = default;
156  ProcessReturnCode& operator=(const ProcessReturnCode& p) = default;
157  // Non-default move: In order for Subprocess to be movable, the "moved
158  // out" state must not be "running", or ~Subprocess() will abort.
161 
169  State state() const;
170 
174  bool notStarted() const {
175  return state() == NOT_STARTED;
176  }
177  bool running() const {
178  return state() == RUNNING;
179  }
180  bool exited() const {
181  return state() == EXITED;
182  }
183  bool killed() const {
184  return state() == KILLED;
185  }
186 
190  int exitStatus() const;
191 
196  int killSignal() const;
197 
202  bool coreDumped() const;
203 
212  std::string str() const;
213 
218  void enforce(State state) const;
219 
220  private:
221  explicit ProcessReturnCode(int rv) : rawStatus_(rv) {}
222  static constexpr int RV_NOT_STARTED = -2;
223  static constexpr int RV_RUNNING = -1;
224 
226 };
227 
231 class FOLLY_EXPORT SubprocessError : public std::runtime_error {
232  public:
233  using std::runtime_error::runtime_error;
234 };
235 
240  public:
242  ~CalledProcessError() throw() override = default;
244  return returnCode_;
245  }
246 
247  private:
249 };
250 
255  public:
256  SubprocessSpawnError(const char* executable, int errCode, int errnoValue);
257  ~SubprocessSpawnError() throw() override = default;
258  int errnoValue() const {
259  return errnoValue_;
260  }
261 
262  private:
264 };
265 
269 class Subprocess {
270  public:
271  static const int CLOSE = -1;
272  static const int PIPE = -2;
273  static const int PIPE_IN = -3;
274  static const int PIPE_OUT = -4;
275 
289  // This must return 0 on success, or an `errno` error code.
290  virtual int operator()() = 0;
291  };
292 
301  class Options {
302  friend class Subprocess;
303 
304  public:
305  Options() {} // E.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58328
306 
321  Options& fd(int fd, int action);
322 
326  Options& stdinFd(int action) {
327  return fd(STDIN_FILENO, action);
328  }
329 
333  Options& stdoutFd(int action) {
334  return fd(STDOUT_FILENO, action);
335  }
336 
342  Options& stderrFd(int action) {
343  return fd(STDERR_FILENO, action);
344  }
345 
347  return fd(STDIN_FILENO, PIPE_IN);
348  }
350  return fd(STDOUT_FILENO, PIPE_OUT);
351  }
353  return fd(STDERR_FILENO, PIPE_OUT);
354  }
355 
369  closeOtherFds_ = true;
370  return *this;
371  }
372 
377  usePath_ = true;
378  return *this;
379  }
380 
384  Options& chdir(const std::string& dir) {
385  childDir_ = dir;
386  return *this;
387  }
388 
389 #if __linux__
390 
393  Options& parentDeathSignal(int sig) {
394  parentDeathSignal_ = sig;
395  return *this;
396  }
397 #endif
398 
405  processGroupLeader_ = true;
406  return *this;
407  }
408 
422  detach_ = true;
423  return *this;
424  }
425 
459  dangerousPostForkPreExecCallback_ = cob;
460  return *this;
461  }
462 
463 #if __linux__
464 
479  using clone_flags_t = uint64_t;
480  Options& useCloneWithFlags(clone_flags_t cloneFlags) noexcept {
481  cloneFlags_ = cloneFlags;
482  return *this;
483  }
484 #endif
485 
486  private:
487  typedef boost::container::flat_map<int, int> FdMap;
488  FdMap fdActions_;
489  bool closeOtherFds_{false};
490  bool usePath_{false};
491  bool processGroupLeader_{false};
492  bool detach_{false};
493  std::string childDir_; // "" keeps the parent's working directory
494 #if __linux__
495  int parentDeathSignal_{0};
496 #endif
497  DangerousPostForkPreExecCallback* dangerousPostForkPreExecCallback_{
498  nullptr};
499 #if __linux__
500  // none means `vfork()` instead of a custom `clone()`
501  // Optional<> is used because value of '0' means do clone without any flags.
502  Optional<clone_flags_t> cloneFlags_;
503 #endif
504  };
505 
506  // Non-copiable, but movable
507  Subprocess(const Subprocess&) = delete;
508  Subprocess& operator=(const Subprocess&) = delete;
509  Subprocess(Subprocess&&) = default;
510  Subprocess& operator=(Subprocess&&) = default;
511 
518  Subprocess();
519 
529  explicit Subprocess(
530  const std::vector<std::string>& argv,
531  const Options& options = Options(),
532  const char* executable = nullptr,
533  const std::vector<std::string>* env = nullptr);
534  ~Subprocess();
535 
542  // clang-format off
543  [[deprecated(
544  "Prefer not running in a shell or use `shellify`.")]]
545  explicit Subprocess(
546  const std::string& cmd,
547  const Options& options = Options(),
548  const std::vector<std::string>* env = nullptr);
549  // clang-format on
550 
555 
560  pid_t pid() const;
561 
570  return returnCode_;
571  }
572 
581  ProcessReturnCode poll(struct rusage* ru = nullptr);
582 
588  bool pollChecked();
589 
597 
601  void waitChecked();
602 
607  void sendSignal(int signal);
608  void terminate() {
609  sendSignal(SIGTERM);
610  }
611  void kill() {
612  sendSignal(SIGKILL);
613  }
614 
619 
645  std::pair<IOBufQueue, IOBufQueue> communicateIOBuf(
646  IOBufQueue input = IOBufQueue());
647 
648  std::pair<std::string, std::string> communicate(
649  StringPiece input = StringPiece());
650 
719  void communicate(FdCallback readCallback, FdCallback writeCallback);
720 
748  template <class Callback>
750  private:
751  // Binds an FD to the client-provided FD+line callback
753  StreamSplitterCallback(Callback& cb, int fd) : cb_(cb), fd_(fd) {}
754  // The return value semantics are inverted vs StreamSplitter
756  return !cb_(fd_, s);
757  }
758  Callback& cb_;
759  int fd_;
760  };
762 
763  public:
765  Callback&& fdLineCb,
766  uint64_t maxLineLength = 0, // No line length limit by default
767  char delimiter = '\n',
768  uint64_t bufSize = 1024)
769  : fdLineCb_(std::forward<Callback>(fdLineCb)),
770  maxLineLength_(maxLineLength),
771  delimiter_(delimiter),
772  bufSize_(bufSize) {}
773 
774  bool operator()(int pfd, int cfd) {
775  // Make a splitter for this cfd if it doesn't already exist
776  auto it = fdToSplitter_.find(cfd);
777  auto& splitter = (it != fdToSplitter_.end())
778  ? it->second
779  : fdToSplitter_
780  .emplace(
781  cfd,
782  LineSplitter(
783  delimiter_,
784  StreamSplitterCallback(fdLineCb_, cfd),
785  maxLineLength_))
786  .first->second;
787  // Read as much as we can from this FD
788  char buf[bufSize_];
789  while (true) {
790  ssize_t ret = readNoInt(pfd, buf, bufSize_);
791  if (ret == -1 && errno == EAGAIN) { // No more data for now
792  return false;
793  }
794  checkUnixError(ret, "read");
795  if (ret == 0) { // Reached end-of-file
796  splitter.flush(); // Ignore return since the file is over anyway
797  return true;
798  }
799  if (!splitter(StringPiece(buf, ret))) {
800  return true; // The callback told us to stop
801  }
802  }
803  }
804 
805  private:
806  Callback fdLineCb_;
808  const char delimiter_;
810  // We lazily make splitters for all cfds that get used.
811  std::unordered_map<int, LineSplitter> fdToSplitter_;
812  };
813 
814  // Helper to enable template deduction
815  template <class Callback>
816  static auto readLinesCallback(
817  Callback&& fdLineCb,
818  uint64_t maxLineLength = 0, // No line length limit by default
819  char delimiter = '\n',
820  uint64_t bufSize = 1024)
823  std::forward<Callback>(fdLineCb), maxLineLength, delimiter, bufSize);
824  }
825 
837  void enableNotifications(int childFd, bool enabled);
838 
843  bool notificationsEnabled(int childFd) const;
844 
851 
856  void closeParentFd(int childFd);
857 
862  void setAllNonBlocking();
863 
869  int parentFd(int childFd) const {
870  return pipes_[findByChildFd(childFd)].pipe.fd();
871  }
872  int stdinFd() const {
873  return parentFd(0);
874  }
875  int stdoutFd() const {
876  return parentFd(1);
877  }
878  int stderrFd() const {
879  return parentFd(2);
880  }
881 
895  struct ChildPipe {
896  ChildPipe(int fd, folly::File&& ppe) : childFd(fd), pipe(std::move(ppe)) {}
897  int childFd;
898  folly::File pipe; // Owns the parent FD
899  };
900  std::vector<ChildPipe> takeOwnershipOfPipes();
901 
902  private:
903  // spawn() sets up a pipe to read errors from the child,
904  // then calls spawnInternal() to do the bulk of the work. Once
905  // spawnInternal() returns it reads the error pipe to see if the child
906  // encountered any errors.
907  void spawn(
908  std::unique_ptr<const char*[]> argv,
909  const char* executable,
910  const Options& options,
911  const std::vector<std::string>* env);
912  void spawnInternal(
913  std::unique_ptr<const char*[]> argv,
914  const char* executable,
915  Options& options,
916  const std::vector<std::string>* env,
917  int errFd);
918 
919  // Actions to run in child.
920  // Note that this runs after vfork(), so tread lightly.
921  // Returns 0 on success, or an errno value on failure.
922  int prepareChild(
923  const Options& options,
924  const sigset_t* sigmask,
925  const char* childDir) const;
926  int runChild(
927  const char* executable,
928  char** argv,
929  char** env,
930  const Options& options) const;
931 
936  void readChildErrorPipe(int pfd, const char* executable);
937 
938  // Returns an index into pipes_. Throws std::invalid_argument if not found.
939  size_t findByChildFd(const int childFd) const;
940 
941  pid_t pid_{-1};
943 
950  struct Pipe : private boost::totally_ordered<Pipe> {
951  folly::File pipe; // Our end of the pipe, wrapped in a File to auto-close.
952  int childFd = -1; // Identifies the pipe: what FD is this in the child?
953  int direction = PIPE_IN; // one of PIPE_IN / PIPE_OUT
954  bool enabled = true; // Are notifications enabled in communicate()?
955 
956  bool operator<(const Pipe& other) const {
957  return childFd < other.childFd;
958  }
959  bool operator==(const Pipe& other) const {
960  return childFd == other.childFd;
961  }
962  };
963 
964  // Populated at process start according to fdActions, empty after
965  // takeOwnershipOfPipes(). Sorted by childFd. Can only have elements
966  // erased, but not inserted, after being populated.
967  //
968  // The number of pipes between parent and child is assumed to be small,
969  // so we're happy with a vector here, even if it means linear erase.
970  std::vector<Pipe> pipes_;
971 };
972 
973 } // namespace folly
ProcessReturnCode returnCode_
Definition: Subprocess.h:248
static constexpr int RV_RUNNING
Definition: Subprocess.h:223
std::unordered_map< int, LineSplitter > fdToSplitter_
Definition: Subprocess.h:811
ProcessReturnCode & operator=(const ProcessReturnCode &p)=default
bool killed() const
Definition: Subprocess.h:183
static ProcessReturnCode makeNotStarted()
Definition: Subprocess.h:140
static ProcessReturnCode makeRunning()
Definition: Subprocess.h:144
ProcessReturnCode returnCode() const
Definition: Subprocess.h:243
State state() const
Definition: Subprocess.cpp:74
Options & chdir(const std::string &dir)
Definition: Subprocess.h:384
static constexpr int RV_NOT_STARTED
Definition: Subprocess.h:222
ChildPipe(int fd, folly::File &&ppe)
Definition: Subprocess.h:896
bool notStarted() const
Definition: Subprocess.h:174
Options & stdinFd(int action)
Definition: Subprocess.h:326
static ProcessReturnCode make(int status)
Definition: Subprocess.cpp:54
gen::StreamSplitter< StreamSplitterCallback > LineSplitter
Definition: Subprocess.h:761
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
ssize_t readNoInt(int fd, void *buf, size_t count)
Definition: FileUtil.cpp:102
STL namespace.
void runChild(const char *file)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
#define FOLLY_EXPORT
Definition: CPortability.h:133
requires E e noexcept(noexcept(s.error(std::move(e))))
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
int stdinFd() const
Definition: Subprocess.h:872
bool running() const
Definition: Subprocess.h:177
char ** argv
bool operator()(int pfd, int cfd)
Definition: Subprocess.h:774
int errCode
Definition: Subprocess.cpp:224
std::unique_ptr< AsyncFizzServer::HandshakeCallback > cb_
Options & processGroupLeader()
Definition: Subprocess.h:404
bool coreDumped() const
Definition: Subprocess.cpp:108
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
constexpr detail::Sig< Sig > const sig
Definition: Poly.h:1165
bool wait(Waiter *waiter, bool shouldSleep, Waiter *&next)
std::string str() const
Definition: Subprocess.cpp:113
ReadLinesCallback(Callback &&fdLineCb, uint64_t maxLineLength=0, char delimiter= '\n', uint64_t bufSize=1024)
Definition: Subprocess.h:764
void checkUnixError(ssize_t ret, Args &&...args)
Definition: Exception.h:101
Options & stderrFd(int action)
Definition: Subprocess.h:342
cmd
Definition: gtest-cfgcmd.txt:1
int stderrFd() const
Definition: Subprocess.h:878
const char * string
Definition: Conv.cpp:212
bool operator==(const Pipe &other) const
Definition: Subprocess.h:959
int poll(PollDescriptor fds[], nfds_t nfds, int timeout)
Definition: NetOps.cpp:141
void enforce(State state) const
Definition: Subprocess.cpp:90
static set< string > s
ProcessReturnCode returnCode() const
Definition: Subprocess.h:569
bool operator<(const Pipe &other) const
Definition: Subprocess.h:956
Options & stdoutFd(int action)
Definition: Subprocess.h:333
int errnoValue
Definition: Subprocess.cpp:225
Options & dangerousPostForkPreExecCallback(DangerousPostForkPreExecCallback *cob)
Definition: Subprocess.h:457
boost::container::flat_map< int, int > FdMap
Definition: Subprocess.h:487
Range< const char * > StringPiece
int parentFd(int childFd) const
Definition: Subprocess.h:869
int stdoutFd() const
Definition: Subprocess.h:875
bool exited() const
Definition: Subprocess.h:180
void pipe(CPUExecutor cpu, IOExecutor io)
static auto readLinesCallback(Callback &&fdLineCb, uint64_t maxLineLength=0, char delimiter= '\n', uint64_t bufSize=1024) -> ReadLinesCallback< typename std::decay< Callback >::type >
Definition: Subprocess.h:816
action
Definition: upload.py:393