proxygen
folly::Subprocess Class Reference

#include <Subprocess.h>

Classes

struct  ChildPipe
 
struct  DangerousPostForkPreExecCallback
 
class  Options
 
struct  Pipe
 
class  ReadLinesCallback
 

Public Types

using FdCallback = folly::Function< bool(int, int)>
 

Public Member Functions

 Subprocess (const Subprocess &)=delete
 
Subprocessoperator= (const Subprocess &)=delete
 
 Subprocess (Subprocess &&)=default
 
Subprocessoperator= (Subprocess &&)=default
 
 Subprocess ()
 
 Subprocess (const std::vector< std::string > &argv, const Options &options=Options(), const char *executable=nullptr, const std::vector< std::string > *env=nullptr)
 
 ~Subprocess ()
 
 Subprocess (const std::string &cmd, const Options &options=Options(), const std::vector< std::string > *env=nullptr)
 
pid_t pid () const
 
ProcessReturnCode returnCode () const
 
ProcessReturnCode poll (struct rusage *ru=nullptr)
 
bool pollChecked ()
 
ProcessReturnCode wait ()
 
void waitChecked ()
 
void sendSignal (int signal)
 
void terminate ()
 
void kill ()
 
std::pair< IOBufQueue, IOBufQueuecommunicateIOBuf (IOBufQueue input=IOBufQueue())
 
std::pair< std::string, std::stringcommunicate (StringPiece input=StringPiece())
 
void communicate (FdCallback readCallback, FdCallback writeCallback)
 
void enableNotifications (int childFd, bool enabled)
 
bool notificationsEnabled (int childFd) const
 
void closeParentFd (int childFd)
 
void setAllNonBlocking ()
 
int parentFd (int childFd) const
 
int stdinFd () const
 
int stdoutFd () const
 
int stderrFd () const
 
std::vector< ChildPipetakeOwnershipOfPipes ()
 

Static Public Member Functions

template<class Callback >
static auto readLinesCallback (Callback &&fdLineCb, uint64_t maxLineLength=0, char delimiter= '\n', uint64_t bufSize=1024) -> ReadLinesCallback< typename std::decay< Callback >::type >
 

Static Public Attributes

static const int CLOSE = -1
 
static const int PIPE = -2
 
static const int PIPE_IN = -3
 
static const int PIPE_OUT = -4
 

Private Member Functions

void spawn (std::unique_ptr< const char *[]> argv, const char *executable, const Options &options, const std::vector< std::string > *env)
 
void spawnInternal (std::unique_ptr< const char *[]> argv, const char *executable, Options &options, const std::vector< std::string > *env, int errFd)
 
int prepareChild (const Options &options, const sigset_t *sigmask, const char *childDir) const
 
int runChild (const char *executable, char **argv, char **env, const Options &options) const
 
void readChildErrorPipe (int pfd, const char *executable)
 
size_t findByChildFd (const int childFd) const
 

Private Attributes

pid_t pid_ {-1}
 
ProcessReturnCode returnCode_
 
std::vector< Pipepipes_
 

Detailed Description

Subprocess.

Definition at line 269 of file Subprocess.h.

Member Typedef Documentation

Communicate with the child until all pipes to/from the child are closed.

== Semantics ==

readCallback(pfd, cfd) will be called whenever there's data available on any pipe from the child (PIPE_OUT). pfd is the file descriptor in the parent (that you use to read from); cfd is the file descriptor in the child (used for identifying the stream; 1 = child's standard output, 2 = child's standard error, etc)

writeCallback(pfd, cfd) will be called whenever a pipe to the child is writable (PIPE_IN). pfd is the file descriptor in the parent (that you use to write to); cfd is the file descriptor in the child (used for identifying the stream; 0 = child's standard input, etc)

The read and write callbacks must read from / write to pfd and return false during normal operation. Return true to tell communicate() to close the pipe. For readCallback, this might send SIGPIPE to the child, or make its writes fail with EPIPE, so you should generally avoid returning true unless you've reached end-of-file.

communicate() returns when all pipes to/from the child are closed; the child might stay alive after that, so you must still wait(). Conversely, the child may quit long before its pipes are closed, since its descendants can keep them alive forever.

Most users won't need to use this callback version; the simpler version of communicate (which buffers data in memory) will probably work fine.

== Things you must get correct ==

1) You MUST consume all data passed to readCallback (or return true to close the pipe). Similarly, you MUST write to a writable pipe (or return true to close the pipe). To do otherwise is an error that can result in a deadlock. You must do this even for pipes you are not interested in.

2) pfd is nonblocking, so be prepared for read() / write() to return -1 and set errno to EAGAIN (in which case you should return false). Use readNoInt() from FileUtil.h to handle interrupted reads for you.

3) Your callbacks MUST NOT call any of the Subprocess methods that manipulate the pipe FDs. Check the docblocks, but, for example, neither closeParentFd (return true instead) nor takeOwnershipOfPipes are safe. Stick to reading/writing from pfd, as appropriate.

== Good to know ==

1) See ReadLinesCallback for an easy way to consume the child's output streams line-by-line (or tokenized by another delimiter).

2) "Wait until the descendants close the pipes" is usually the behavior you want, since the descendants may have something to say even if the immediate child is dead. If you need to be able to force-close all parent FDs, communicate() will NOT work for you. Do it your own way by using takeOwnershipOfPipes().

Why not? You can return "true" from your callbacks to sever active pipes, but inactive ones can remain open indefinitely. It is impossible to safely close inactive pipes while another thread is blocked in communicate(). This is BY DESIGN. Racing communicate()'s read/write callbacks can result in wrong I/O and data corruption. This class would need internal synchronization and timeouts, a poor and expensive implementation choice, in order to make closeParentFd() thread-safe.

Definition at line 718 of file Subprocess.h.

Constructor & Destructor Documentation

folly::Subprocess::Subprocess ( const Subprocess )
delete
folly::Subprocess::Subprocess ( Subprocess &&  )
default
folly::Subprocess::Subprocess ( )

Create an uninitialized subprocess.

In this state it can only be destroyed, or assigned to using the move assignment operator.

Definition at line 188 of file Subprocess.cpp.

188 {}
folly::Subprocess::Subprocess ( const std::vector< std::string > &  argv,
const Options options = Options(),
const char *  executable = nullptr,
const std::vector< std::string > *  env = nullptr 
)
explicit

Create a subprocess from the given arguments. argv[0] must be listed. If not-null, executable must be the actual executable being used (otherwise it's the same as argv[0]).

If env is not-null, it must contain name=value strings to be used as the child's environment; otherwise, we inherit the environment from the parent. env must be null if options.usePath is set.

Definition at line 190 of file Subprocess.cpp.

194  {
195  if (argv.empty()) {
196  throw std::invalid_argument("argv must not be empty");
197  }
198  if (!executable) {
199  executable = argv[0].c_str();
200  }
201  spawn(cloneStrings(argv), executable, options, env);
202 }
void spawn(std::unique_ptr< const char *[]> argv, const char *executable, const Options &options, const std::vector< std::string > *env)
Definition: Subprocess.cpp:249
folly::Subprocess::~Subprocess ( )

Definition at line 216 of file Subprocess.cpp.

References folly::ProcessReturnCode::RUNNING.

216  {
218  << "Subprocess destroyed without reaping child";
219 }
State state() const
Definition: Subprocess.cpp:74
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
folly::Subprocess::Subprocess ( const std::string cmd,
const Options options = Options(),
const std::vector< std::string > *  env = nullptr 
)
explicit

Create a subprocess run as a shell command (as shell -c 'command')

The shell to use is taken from the environment variable $SHELL, or /bin/sh if $SHELL is unset.

Definition at line 204 of file Subprocess.cpp.

References argv, and folly::Subprocess::Options::usePath_.

207  {
208  if (options.usePath_) {
209  throw std::invalid_argument("usePath() not allowed when running in shell");
210  }
211 
212  std::vector<std::string> argv = {"/bin/sh", "-c", cmd};
213  spawn(cloneStrings(argv), argv[0].c_str(), options, env);
214 }
void spawn(std::unique_ptr< const char *[]> argv, const char *executable, const Options &options, const std::vector< std::string > *env)
Definition: Subprocess.cpp:249
char ** argv
cmd
Definition: gtest-cfgcmd.txt:1

Member Function Documentation

void folly::Subprocess::closeParentFd ( int  childFd)

Close the parent file descriptor given a file descriptor in the child. DO NOT USE from communicate() callbacks; make them return true instead.

Definition at line 901 of file Subprocess.cpp.

901  {
902  int idx = findByChildFd(childFd);
903  pipes_[idx].pipe.close(); // May throw
904  pipes_.erase(pipes_.begin() + idx);
905 }
size_t findByChildFd(const int childFd) const
Definition: Subprocess.cpp:889
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
std::pair< std::string, std::string > folly::Subprocess::communicate ( StringPiece  input = StringPiece())

Definition at line 740 of file Subprocess.cpp.

References folly::Range< Iter >::data(), folly::gen::move, folly::Range< Iter >::size(), and folly::IOBufQueue::wrapBuffer().

Referenced by TEST().

740  {
741  IOBufQueue inputQueue;
742  inputQueue.wrapBuffer(input.data(), input.size());
743 
744  auto outQueues = communicateIOBuf(std::move(inputQueue));
745  auto outBufs =
746  std::make_pair(outQueues.first.move(), outQueues.second.move());
747  std::pair<std::string, std::string> out;
748  if (outBufs.first) {
749  outBufs.first->coalesce();
750  out.first.assign(
751  reinterpret_cast<const char*>(outBufs.first->data()),
752  outBufs.first->length());
753  }
754  if (outBufs.second) {
755  outBufs.second->coalesce();
756  out.second.assign(
757  reinterpret_cast<const char*>(outBufs.second->data()),
758  outBufs.second->length());
759  }
760  return out;
761 }
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
std::pair< IOBufQueue, IOBufQueue > communicateIOBuf(IOBufQueue input=IOBufQueue())
Definition: Subprocess.cpp:763
void folly::Subprocess::communicate ( FdCallback  readCallback,
FdCallback  writeCallback 
)

Definition at line 801 of file Subprocess.cpp.

References folly::checkUnixError(), i, folly::netops::poll(), and folly::ProcessReturnCode::RUNNING.

803  {
804  // This serves to prevent wait() followed by communicate(), but if you
805  // legitimately need that, send a patch to delete this line.
808 
809  std::vector<pollfd> fds;
810  fds.reserve(pipes_.size());
811  std::vector<size_t> toClose; // indexes into pipes_
812  toClose.reserve(pipes_.size());
813 
814  while (!pipes_.empty()) {
815  fds.clear();
816  toClose.clear();
817 
818  for (auto& p : pipes_) {
819  pollfd pfd;
820  pfd.fd = p.pipe.fd();
821  // Yes, backwards, PIPE_IN / PIPE_OUT are defined from the
822  // child's point of view.
823  if (!p.enabled) {
824  // Still keeping fd in watched set so we get notified of POLLHUP /
825  // POLLERR
826  pfd.events = 0;
827  } else if (p.direction == PIPE_IN) {
828  pfd.events = POLLOUT;
829  } else {
830  pfd.events = POLLIN;
831  }
832  fds.push_back(pfd);
833  }
834 
835  int r;
836  do {
837  r = ::poll(fds.data(), fds.size(), -1);
838  } while (r == -1 && errno == EINTR);
839  checkUnixError(r, "poll");
840 
841  for (size_t i = 0; i < pipes_.size(); ++i) {
842  auto& p = pipes_[i];
843  auto parentFd = p.pipe.fd();
844  DCHECK_EQ(fds[i].fd, parentFd);
845  short events = fds[i].revents;
846 
847  bool closed = false;
848  if (events & POLLOUT) {
849  DCHECK(!(events & POLLIN));
850  if (writeCallback(parentFd, p.childFd)) {
851  toClose.push_back(i);
852  closed = true;
853  }
854  }
855 
856  // Call read callback on POLLHUP, to give it a chance to read (and act
857  // on) end of file
858  if (events & (POLLIN | POLLHUP)) {
859  DCHECK(!(events & POLLOUT));
860  if (readCallback(parentFd, p.childFd)) {
861  toClose.push_back(i);
862  closed = true;
863  }
864  }
865 
866  if ((events & (POLLHUP | POLLERR)) && !closed) {
867  toClose.push_back(i);
868  closed = true;
869  }
870  }
871 
872  // Close the fds in reverse order so the indexes hold after erase()
873  for (int idx : boost::adaptors::reverse(toClose)) {
874  auto pos = pipes_.begin() + idx;
875  pos->pipe.close(); // Throws on error
876  pipes_.erase(pos);
877  }
878  }
879 }
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
static const int PIPE_IN
Definition: Subprocess.h:273
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
void checkUnixError(ssize_t ret, Args &&...args)
Definition: Exception.h:101
void setAllNonBlocking()
Definition: Subprocess.cpp:239
void enforce(State state) const
Definition: Subprocess.cpp:90
int parentFd(int childFd) const
Definition: Subprocess.h:869
ProcessReturnCode poll(struct rusage *ru=nullptr)
Definition: Subprocess.cpp:618
std::pair< IOBufQueue, IOBufQueue > folly::Subprocess::communicateIOBuf ( IOBufQueue  input = IOBufQueue())

Communicate with the child until all pipes to/from the child are closed.

The input buffer is written to the process' stdin pipe, and data is read from the stdout and stderr pipes. Non-blocking I/O is performed on all pipes simultaneously to avoid deadlocks.

The stdin pipe will be closed after the full input buffer has been written. An error will be thrown if a non-empty input buffer is supplied but stdin was not configured as a pipe.

Returns a pair of buffers containing the data read from stdout and stderr. If stdout or stderr is not a pipe, an empty IOBuf queue will be returned for the respective buffer.

Note that communicate() and communicateIOBuf() both return when all pipes to/from the child are closed; the child might stay alive after that, so you must still wait().

communicateIOBuf() uses IOBufQueue for buffering (which has the advantage that it won't try to allocate all data at once), but it does store the subprocess's entire output in memory before returning.

communicate() uses strings for simplicity.

Definition at line 763 of file Subprocess.cpp.

References folly::IOBufQueue::empty(), and folly::gen::move.

Referenced by TEST().

764  {
765  // If the user supplied a non-empty input buffer, make sure
766  // that stdin is a pipe so we can write the data.
767  if (!input.empty()) {
768  // findByChildFd() will throw std::invalid_argument if no pipe for
769  // STDIN_FILENO exists
770  findByChildFd(STDIN_FILENO);
771  }
772 
773  std::pair<IOBufQueue, IOBufQueue> out;
774 
775  auto readCallback = [&](int pfd, int cfd) -> bool {
776  if (cfd == STDOUT_FILENO) {
777  return handleRead(pfd, out.first);
778  } else if (cfd == STDERR_FILENO) {
779  return handleRead(pfd, out.second);
780  } else {
781  // Don't close the file descriptor, the child might not like SIGPIPE,
782  // just read and throw the data away.
783  return discardRead(pfd);
784  }
785  };
786 
787  auto writeCallback = [&](int pfd, int cfd) -> bool {
788  if (cfd == STDIN_FILENO) {
789  return handleWrite(pfd, input);
790  } else {
791  // If we don't want to write to this fd, just close it.
792  return true;
793  }
794  };
795 
796  communicate(std::move(readCallback), std::move(writeCallback));
797 
798  return out;
799 }
size_t findByChildFd(const int childFd) const
Definition: Subprocess.cpp:889
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
std::pair< std::string, std::string > communicate(StringPiece input=StringPiece())
Definition: Subprocess.cpp:740
void folly::Subprocess::enableNotifications ( int  childFd,
bool  enabled 
)

communicate() callbacks can use this to temporarily enable/disable notifications (callbacks) for a pipe to/from the child. By default, all are enabled. Useful for "chatty" communication – you want to disable write callbacks until you receive the expected message.

Disabling a pipe does not free you from the requirement to consume all incoming data. Failing to do so will easily create deadlock bugs.

Throws if the childFd is not known.

Definition at line 881 of file Subprocess.cpp.

Referenced by TEST().

881  {
882  pipes_[findByChildFd(childFd)].enabled = enabled;
883 }
size_t findByChildFd(const int childFd) const
Definition: Subprocess.cpp:889
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
size_t folly::Subprocess::findByChildFd ( const int  childFd) const
private

Definition at line 889 of file Subprocess.cpp.

References pipe().

889  {
890  auto pos = std::lower_bound(
891  pipes_.begin(), pipes_.end(), childFd, [](const Pipe& pipe, int fd) {
892  return pipe.childFd < fd;
893  });
894  if (pos == pipes_.end() || pos->childFd != childFd) {
895  throw std::invalid_argument(
896  folly::to<std::string>("child fd not found ", childFd));
897  }
898  return pos - pipes_.begin();
899 }
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
void pipe(CPUExecutor cpu, IOExecutor io)
void folly::Subprocess::kill ( )
inline

Definition at line 611 of file Subprocess.h.

611  {
612  sendSignal(SIGKILL);
613  }
void sendSignal(int signal)
Definition: Subprocess.cpp:668
bool folly::Subprocess::notificationsEnabled ( int  childFd) const

Are notifications for one pipe to/from child enabled? Throws if the childFd is not known.

Definition at line 885 of file Subprocess.cpp.

885  {
886  return pipes_[findByChildFd(childFd)].enabled;
887 }
size_t findByChildFd(const int childFd) const
Definition: Subprocess.cpp:889
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
Subprocess& folly::Subprocess::operator= ( const Subprocess )
delete
Subprocess& folly::Subprocess::operator= ( Subprocess &&  )
default
int folly::Subprocess::parentFd ( int  childFd) const
inline

Get parent file descriptor corresponding to the given file descriptor in the child. Throws if childFd isn't a pipe (PIPE_IN / PIPE_OUT). Do not close() the returned file descriptor; use closeParentFd, above.

Definition at line 869 of file Subprocess.h.

869  {
870  return pipes_[findByChildFd(childFd)].pipe.fd();
871  }
size_t findByChildFd(const int childFd) const
Definition: Subprocess.cpp:889
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
pid_t folly::Subprocess::pid ( ) const

Return the child's pid, or -1 if the child wasn't successfully spawned or has already been wait()ed upon.

Definition at line 674 of file Subprocess.cpp.

References b, folly::checkUnixError(), folly::IOBufQueue::front(), folly::io::detail::CursorBase< Derived, BufType >::peekBytes(), folly::IOBufQueue::postallocate(), folly::IOBufQueue::preallocate(), folly::readNoInt(), folly::IOBufQueue::trimStart(), and folly::writeNoInt().

674  {
675  return pid_;
676 }
ProcessReturnCode folly::Subprocess::poll ( struct rusage *  ru = nullptr)

Poll the child's status and return it. Return the exit status if the subprocess had quit, or RUNNING otherwise. Throws an std::logic_error if called on a Subprocess whose status is no longer RUNNING. No other exceptions are possible. Aborts on egregious violations of contract, e.g. if you wait for the underlying process without going through this Subprocess instance.

Definition at line 618 of file Subprocess.cpp.

References folly::ProcessReturnCode::enforce(), folly::ProcessReturnCode::make(), and folly::ProcessReturnCode::RUNNING.

Referenced by runParent().

618  {
620  DCHECK_GT(pid_, 0);
621  int status;
622  pid_t found = ::wait4(pid_, &status, WNOHANG, ru);
623  // The spec guarantees that EINTR does not occur with WNOHANG, so the only
624  // two remaining errors are ECHILD (other code reaped the child?), or
625  // EINVAL (cosmic rays?), both of which merit an abort:
626  PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)";
627  if (found != 0) {
628  // Though the child process had quit, this call does not close the pipes
629  // since its descendants may still be using them.
631  pid_ = -1;
632  }
633  return returnCode_;
634 }
static ProcessReturnCode make(int status)
Definition: Subprocess.cpp:54
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
void enforce(State state) const
Definition: Subprocess.cpp:90
bool folly::Subprocess::pollChecked ( )

Poll the child's status. If the process is still running, return false. Otherwise, return true if the process exited with status 0 (success), or throw CalledProcessError if the process exited with a non-zero status.

Definition at line 636 of file Subprocess.cpp.

References folly::netops::poll(), and folly::ProcessReturnCode::RUNNING.

636  {
638  return false;
639  }
640  checkStatus(returnCode_);
641  return true;
642 }
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
ProcessReturnCode poll(struct rusage *ru=nullptr)
Definition: Subprocess.cpp:618
state
Definition: http_parser.c:272
FOLLY_POP_WARNING int folly::Subprocess::prepareChild ( const Options options,
const sigset_t *  sigmask,
const char *  childDir 
) const
private

Definition at line 495 of file Subprocess.cpp.

References folly::netops::close(), folly::Subprocess::Options::closeOtherFds_, folly::Subprocess::Options::dangerousPostForkPreExecCallback_, folly::pushmi::operators::error(), folly::Subprocess::Options::fdActions_, folly::Subprocess::Options::processGroupLeader_, and folly::sig.

498  {
499  // While all signals are blocked, we must reset their
500  // dispositions to default.
501  for (int sig = 1; sig < NSIG; ++sig) {
502  ::signal(sig, SIG_DFL);
503  }
504 
505  {
506  // Unblock signals; restore signal mask.
507  int r = pthread_sigmask(SIG_SETMASK, sigmask, nullptr);
508  if (r != 0) {
509  return r; // pthread_sigmask() returns an errno value
510  }
511  }
512 
513  // Change the working directory, if one is given
514  if (childDir) {
515  if (::chdir(childDir) == -1) {
516  return errno;
517  }
518  }
519 
520  // We don't have to explicitly close the parent's end of all pipes,
521  // as they all have the FD_CLOEXEC flag set and will be closed at
522  // exec time.
523 
524  // Close all fds that we're supposed to close.
525  for (auto& p : options.fdActions_) {
526  if (p.second == CLOSE) {
527  if (::close(p.first) == -1) {
528  return errno;
529  }
530  } else if (p.second != p.first) {
531  if (::dup2(p.second, p.first) == -1) {
532  return errno;
533  }
534  }
535  }
536 
537  // If requested, close all other file descriptors. Don't close
538  // any fds in options.fdActions_, and don't touch stdin, stdout, stderr.
539  // Ignore errors.
540  if (options.closeOtherFds_) {
541  for (int fd = getdtablesize() - 1; fd >= 3; --fd) {
542  if (options.fdActions_.count(fd) == 0) {
543  ::close(fd);
544  }
545  }
546  }
547 
548 #if __linux__
549  // Opt to receive signal on parent death, if requested
550  if (options.parentDeathSignal_ != 0) {
551  const auto parentDeathSignal =
552  static_cast<unsigned long>(options.parentDeathSignal_);
553  if (prctl(PR_SET_PDEATHSIG, parentDeathSignal, 0, 0, 0) == -1) {
554  return errno;
555  }
556  }
557 #endif
558 
559  if (options.processGroupLeader_) {
560  if (setpgrp() == -1) {
561  return errno;
562  }
563  }
564 
565  // The user callback comes last, so that the child is otherwise all set up.
566  if (options.dangerousPostForkPreExecCallback_) {
567  if (int error = (*options.dangerousPostForkPreExecCallback_)()) {
568  return error;
569  }
570  }
571 
572  return 0;
573 }
requires And< SemiMovable< VN >... > &&SemiMovable< E > auto error(E e)
Definition: error.h:48
constexpr detail::Sig< Sig > const sig
Definition: Poly.h:1165
static const int CLOSE
Definition: Subprocess.h:271
int close(NetworkSocket s)
Definition: NetOps.cpp:90
void folly::Subprocess::readChildErrorPipe ( int  pfd,
const char *  executable 
)
private

Read from the error pipe, and throw SubprocessSpawnError if the child failed before calling exec().

Definition at line 589 of file Subprocess.cpp.

References deadlock::info(), folly::readNoInt(), folly::SubprocessSpawnError::SubprocessSpawnError(), and folly::detail::distributed_mutex::wait().

589  {
590  ChildErrorInfo info;
591  auto rc = readNoInt(pfd, &info, sizeof(info));
592  if (rc == 0) {
593  // No data means the child executed successfully, and the pipe
594  // was closed due to the close-on-exec flag being set.
595  return;
596  } else if (rc != sizeof(ChildErrorInfo)) {
597  // An error occurred trying to read from the pipe, or we got a partial read.
598  // Neither of these cases should really occur in practice.
599  //
600  // We can't get any error data from the child in this case, and we don't
601  // know if it is successfully running or not. All we can do is to return
602  // normally, as if the child executed successfully. If something bad
603  // happened the caller should at least get a non-normal exit status from
604  // the child.
605  LOG(ERROR) << "unexpected error trying to read from child error pipe "
606  << "rc=" << rc << ", errno=" << errno;
607  return;
608  }
609 
610  // We got error data from the child. The child should exit immediately in
611  // this case, so wait on it to clean up.
612  wait();
613 
614  // Throw to signal the error
615  throw SubprocessSpawnError(executable, info.errCode, info.errnoValue);
616 }
def info()
Definition: deadlock.py:447
ssize_t readNoInt(int fd, void *buf, size_t count)
Definition: FileUtil.cpp:102
ProcessReturnCode wait()
Definition: Subprocess.cpp:644
template<class Callback >
static auto folly::Subprocess::readLinesCallback ( Callback &&  fdLineCb,
uint64_t  maxLineLength = 0,
char  delimiter = '\n',
uint64_t  bufSize = 1024 
) -> ReadLinesCallback<typename std::decay<Callback>::type>
inlinestatic

Definition at line 816 of file Subprocess.h.

821  {
823  std::forward<Callback>(fdLineCb), maxLineLength, delimiter, bufSize);
824  }
PskType type
ProcessReturnCode folly::Subprocess::returnCode ( ) const
inline

Return the child's status (as per wait()) if the process has already been waited on, -1 if the process is still running, or -2 if the process hasn't been successfully started. NOTE that this does not call waitpid() or Subprocess::poll(), but simply returns the status stored in the Subprocess object.

Definition at line 569 of file Subprocess.h.

References folly::netops::poll(), and folly::detail::distributed_mutex::wait().

Referenced by TEST().

569  {
570  return returnCode_;
571  }
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
int folly::Subprocess::runChild ( const char *  executable,
char **  argv,
char **  env,
const Options options 
) const
private

Definition at line 575 of file Subprocess.cpp.

References folly::Subprocess::Options::usePath_.

579  {
580  // Now, finally, exec.
581  if (options.usePath_) {
582  ::execvp(executable, argv);
583  } else {
584  ::execve(executable, argv, env);
585  }
586  return errno;
587 }
char ** argv
void folly::Subprocess::sendSignal ( int  signal)

Send a signal to the child. Shortcuts for the commonly used Unix signals are below.

Definition at line 668 of file Subprocess.cpp.

References folly::checkUnixError(), and folly::ProcessReturnCode::RUNNING.

668  {
670  int r = ::kill(pid_, signal);
671  checkUnixError(r, "kill");
672 }
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
void checkUnixError(ssize_t ret, Args &&...args)
Definition: Exception.h:101
void enforce(State state) const
Definition: Subprocess.cpp:90
void folly::Subprocess::setAllNonBlocking ( )

Set all pipes from / to child to be non-blocking. communicate() does this for you.

Definition at line 239 of file Subprocess.cpp.

References folly::checkUnixError().

239  {
240  for (auto& p : pipes_) {
241  int fd = p.pipe.fd();
242  int flags = ::fcntl(fd, F_GETFL);
243  checkUnixError(flags, "fcntl");
244  int r = ::fcntl(fd, F_SETFL, flags | O_NONBLOCK);
245  checkUnixError(r, "fcntl");
246  }
247 }
flags
Definition: http_parser.h:127
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
void checkUnixError(ssize_t ret, Args &&...args)
Definition: Exception.h:101
void folly::Subprocess::spawn ( std::unique_ptr< const char *[]>  argv,
const char *  executable,
const Options options,
const std::vector< std::string > *  env 
)
private

Definition at line 249 of file Subprocess.cpp.

References argv, folly::checkUnixError(), folly::netops::close(), folly::Subprocess::Options::detach_, FOLLY_GCC_DISABLE_WARNING, FOLLY_PUSH_WARNING, folly::makeGuard(), folly::gen::move, pipe(), SCOPE_EXIT, folly::Subprocess::Options::usePath_, and folly::detail::distributed_mutex::wait().

253  {
254  if (optionsIn.usePath_ && env) {
255  throw std::invalid_argument(
256  "usePath() not allowed when overriding environment");
257  }
258 
259  // Make a copy, we'll mutate options
260  Options options(optionsIn);
261 
262  // On error, close all pipes_ (ignoring errors, but that seems fine here).
263  auto pipesGuard = makeGuard([this] { pipes_.clear(); });
264 
265  // Create a pipe to use to receive error information from the child,
266  // in case it fails before calling exec()
267  int errFds[2];
268 #if FOLLY_HAVE_PIPE2
269  checkUnixError(::pipe2(errFds, O_CLOEXEC), "pipe2");
270 #else
271  checkUnixError(::pipe(errFds), "pipe");
272 #endif
273  SCOPE_EXIT {
274  CHECK_ERR(::close(errFds[0]));
275  if (errFds[1] >= 0) {
276  CHECK_ERR(::close(errFds[1]));
277  }
278  };
279 
280 #if !FOLLY_HAVE_PIPE2
281  // Ask the child to close the read end of the error pipe.
282  checkUnixError(fcntl(errFds[0], F_SETFD, FD_CLOEXEC), "set FD_CLOEXEC");
283  // Set the close-on-exec flag on the write side of the pipe.
284  // This way the pipe will be closed automatically in the child if execve()
285  // succeeds. If the exec fails the child can write error information to the
286  // pipe.
287  checkUnixError(fcntl(errFds[1], F_SETFD, FD_CLOEXEC), "set FD_CLOEXEC");
288 #endif
289 
290  // Perform the actual work of setting up pipes then forking and
291  // executing the child.
292  spawnInternal(std::move(argv), executable, options, env, errFds[1]);
293 
294  // After spawnInternal() returns the child is alive. We have to be very
295  // careful about throwing after this point. We are inside the constructor,
296  // so if we throw the Subprocess object will have never existed, and the
297  // destructor will never be called.
298  //
299  // We should only throw if we got an error via the errFd, and we know the
300  // child has exited and can be immediately waited for. In all other cases,
301  // we have no way of cleaning up the child.
302 
303  // Close writable side of the errFd pipe in the parent process
304  CHECK_ERR(::close(errFds[1]));
305  errFds[1] = -1;
306 
307  // Read from the errFd pipe, to tell if the child ran into any errors before
308  // calling exec()
309  readChildErrorPipe(errFds[0], executable);
310 
311  // If we spawned a detached child, wait on the intermediate child process.
312  // It always exits immediately.
313  if (options.detach_) {
314  wait();
315  }
316 
317  // We have fully succeeded now, so release the guard on pipes_
318  pipesGuard.dismiss();
319 }
void readChildErrorPipe(int pfd, const char *executable)
Definition: Subprocess.cpp:589
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
void spawnInternal(std::unique_ptr< const char *[]> argv, const char *executable, Options &options, const std::vector< std::string > *env, int errFd)
Definition: Subprocess.cpp:327
void checkUnixError(ssize_t ret, Args &&...args)
Definition: Exception.h:101
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
ProcessReturnCode wait()
Definition: Subprocess.cpp:644
int close(NetworkSocket s)
Definition: NetOps.cpp:90
void pipe(CPUExecutor cpu, IOExecutor io)
FOLLY_PUSH_WARNING void folly::Subprocess::spawnInternal ( std::unique_ptr< const char *[]>  argv,
const char *  executable,
Options options,
const std::vector< std::string > *  env,
int  errFd 
)
private

Definition at line 327 of file Subprocess.cpp.

References folly::checkPosixError(), folly::checkUnixError(), folly::Subprocess::Pipe::childFd, folly::netops::close(), folly::Subprocess::Pipe::direction, gmock_leak_test::environ, folly::errnoStr(), folly::SubprocessSpawnError::errnoValue(), FOLLY_POP_WARNING, kChildFailure, kExecFailure, folly::ProcessReturnCode::makeRunning(), pipe(), folly::Subprocess::Pipe::pipe, runChild(), and SCOPE_EXIT.

332  {
333  // Parent work, pre-fork: create pipes
334  std::vector<int> childFds;
335  // Close all of the childFds as we leave this scope
336  SCOPE_EXIT {
337  // These are only pipes, closing them shouldn't fail
338  for (int cfd : childFds) {
339  CHECK_ERR(::close(cfd));
340  }
341  };
342 
343  int r;
344  for (auto& p : options.fdActions_) {
345  if (p.second == PIPE_IN || p.second == PIPE_OUT) {
346  int fds[2];
347  // We're setting both ends of the pipe as close-on-exec. The child
348  // doesn't need to reset the flag on its end, as we always dup2() the fd,
349  // and dup2() fds don't share the close-on-exec flag.
350 #if FOLLY_HAVE_PIPE2
351  // If possible, set close-on-exec atomically. Otherwise, a concurrent
352  // Subprocess invocation can fork() between "pipe" and "fnctl",
353  // causing FDs to leak.
354  r = ::pipe2(fds, O_CLOEXEC);
355  checkUnixError(r, "pipe2");
356 #else
357  r = ::pipe(fds);
358  checkUnixError(r, "pipe");
359  r = fcntl(fds[0], F_SETFD, FD_CLOEXEC);
360  checkUnixError(r, "set FD_CLOEXEC");
361  r = fcntl(fds[1], F_SETFD, FD_CLOEXEC);
362  checkUnixError(r, "set FD_CLOEXEC");
363 #endif
364  pipes_.emplace_back();
365  Pipe& pipe = pipes_.back();
366  pipe.direction = p.second;
367  int cfd;
368  if (p.second == PIPE_IN) {
369  // Child gets reading end
370  pipe.pipe = folly::File(fds[1], /*ownsFd=*/true);
371  cfd = fds[0];
372  } else {
373  pipe.pipe = folly::File(fds[0], /*ownsFd=*/true);
374  cfd = fds[1];
375  }
376  p.second = cfd; // ensure it gets dup2()ed
377  pipe.childFd = p.first;
378  childFds.push_back(cfd);
379  }
380  }
381 
382  // This should already be sorted, as options.fdActions_ is
383  DCHECK(std::is_sorted(pipes_.begin(), pipes_.end()));
384 
385  // Note that the const casts below are legit, per
386  // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html
387 
388  char** argVec = const_cast<char**>(argv.get());
389 
390  // Set up environment
391  std::unique_ptr<const char*[]> envHolder;
392  char** envVec;
393  if (env) {
394  envHolder = cloneStrings(*env);
395  envVec = const_cast<char**>(envHolder.get());
396  } else {
397  envVec = environ;
398  }
399 
400  // Block all signals around vfork; see http://ewontfix.com/7/.
401  //
402  // As the child may run in the same address space as the parent until
403  // the actual execve() system call, any (custom) signal handlers that
404  // the parent has might alter parent's memory if invoked in the child,
405  // with undefined results. So we block all signals in the parent before
406  // vfork(), which will cause them to be blocked in the child as well (we
407  // rely on the fact that Linux, just like all sane implementations, only
408  // clones the calling thread). Then, in the child, we reset all signals
409  // to their default dispositions (while still blocked), and unblock them
410  // (so the exec()ed process inherits the parent's signal mask)
411  //
412  // The parent also unblocks all signals as soon as vfork() returns.
413  sigset_t allBlocked;
414  r = sigfillset(&allBlocked);
415  checkUnixError(r, "sigfillset");
416  sigset_t oldSignals;
417 
418  r = pthread_sigmask(SIG_SETMASK, &allBlocked, &oldSignals);
419  checkPosixError(r, "pthread_sigmask");
420  SCOPE_EXIT {
421  // Restore signal mask
422  r = pthread_sigmask(SIG_SETMASK, &oldSignals, nullptr);
423  CHECK_EQ(r, 0) << "pthread_sigmask: " << errnoStr(r); // shouldn't fail
424  };
425 
426  // Call c_str() here, as it's not necessarily safe after fork.
427  const char* childDir =
428  options.childDir_.empty() ? nullptr : options.childDir_.c_str();
429 
430  pid_t pid;
431 #ifdef __linux__
432  if (options.cloneFlags_) {
433  pid = syscall(SYS_clone, *options.cloneFlags_, 0, nullptr, nullptr);
434  } else {
435 #endif
436  if (options.detach_) {
437  // If we are detaching we must use fork() instead of vfork() for the first
438  // fork, since we aren't going to simply call exec() in the child.
439  pid = fork();
440  } else {
441  pid = vfork();
442  }
443 #ifdef __linux__
444  }
445 #endif
446  checkUnixError(pid, errno, "failed to fork");
447  if (pid == 0) {
448  // Fork a second time if detach_ was requested.
449  // This must be done before signals are restored in prepareChild()
450  if (options.detach_) {
451 #ifdef __linux__
452  if (options.cloneFlags_) {
453  pid = syscall(SYS_clone, *options.cloneFlags_, 0, nullptr, nullptr);
454  } else {
455 #endif
456  pid = vfork();
457 #ifdef __linux__
458  }
459 #endif
460  if (pid == -1) {
461  // Inform our parent process of the error so it can throw in the parent.
462  childError(errFd, kChildFailure, errno);
463  } else if (pid != 0) {
464  // We are the intermediate process. Exit immediately.
465  // Our child will still inform the original parent of success/failure
466  // through errFd. The pid of the grandchild process never gets
467  // propagated back up to the original parent. In the future we could
468  // potentially send it back using errFd if we needed to.
469  _exit(0);
470  }
471  }
472 
473  int errnoValue = prepareChild(options, &oldSignals, childDir);
474  if (errnoValue != 0) {
475  childError(errFd, kChildFailure, errnoValue);
476  }
477 
478  errnoValue = runChild(executable, argVec, envVec, options);
479  // If we get here, exec() failed.
480  childError(errFd, kExecFailure, errnoValue);
481  }
482 
483  // Child is alive. We have to be very careful about throwing after this
484  // point. We are inside the constructor, so if we throw the Subprocess
485  // object will have never existed, and the destructor will never be called.
486  //
487  // We should only throw if we got an error via the errFd, and we know the
488  // child has exited and can be immediately waited for. In all other cases,
489  // we have no way of cleaning up the child.
490  pid_ = pid;
492 }
static ProcessReturnCode makeRunning()
Definition: Subprocess.h:144
constexpr int kExecFailure
Definition: Subprocess.cpp:49
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
static const int PIPE_OUT
Definition: Subprocess.h:274
void checkPosixError(int err, Args &&...args)
Definition: Exception.h:83
int runChild(const char *executable, char **argv, char **env, const Options &options) const
Definition: Subprocess.cpp:575
static const int PIPE_IN
Definition: Subprocess.h:273
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
fbstring errnoStr(int err)
Definition: String.cpp:463
void checkUnixError(ssize_t ret, Args &&...args)
Definition: Exception.h:101
int prepareChild(const Options &options, const sigset_t *sigmask, const char *childDir) const
Definition: Subprocess.cpp:495
int errnoValue
Definition: Subprocess.cpp:225
int close(NetworkSocket s)
Definition: NetOps.cpp:90
pid_t pid() const
Definition: Subprocess.cpp:674
constexpr int kChildFailure
Definition: Subprocess.cpp:50
void pipe(CPUExecutor cpu, IOExecutor io)
int folly::Subprocess::stderrFd ( ) const
inline

Definition at line 878 of file Subprocess.h.

878  {
879  return parentFd(2);
880  }
int parentFd(int childFd) const
Definition: Subprocess.h:869
int folly::Subprocess::stdinFd ( ) const
inline

Definition at line 872 of file Subprocess.h.

872  {
873  return parentFd(0);
874  }
int parentFd(int childFd) const
Definition: Subprocess.h:869
int folly::Subprocess::stdoutFd ( ) const
inline

Definition at line 875 of file Subprocess.h.

Referenced by TEST().

875  {
876  return parentFd(1);
877  }
int parentFd(int childFd) const
Definition: Subprocess.h:869
std::vector< Subprocess::ChildPipe > folly::Subprocess::takeOwnershipOfPipes ( )

Definition at line 907 of file Subprocess.cpp.

References folly::gen::move, and folly::swap().

Referenced by TEST().

907  {
908  std::vector<Subprocess::ChildPipe> pipes;
909  for (auto& p : pipes_) {
910  pipes.emplace_back(p.childFd, std::move(p.pipe));
911  }
912  // release memory
913  std::vector<Pipe>().swap(pipes_);
914  return pipes;
915 }
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
std::vector< Pipe > pipes_
Definition: Subprocess.h:970
void swap(exception_wrapper &a, exception_wrapper &b) noexcept
void folly::Subprocess::terminate ( )
inline

Definition at line 608 of file Subprocess.h.

608  {
609  sendSignal(SIGTERM);
610  }
void sendSignal(int signal)
Definition: Subprocess.cpp:668
ProcessReturnCode folly::Subprocess::wait ( )

Wait for the process to terminate and return its status. Like poll(), the only exception this can throw is std::logic_error if you call this on a Subprocess whose status is RUNNING. Aborts on egregious violations of contract, like an out-of-band waitpid(p.pid(), 0, 0).

Definition at line 644 of file Subprocess.cpp.

References folly::ProcessReturnCode::enforce(), folly::ProcessReturnCode::make(), and folly::ProcessReturnCode::RUNNING.

Referenced by TEST().

644  {
646  DCHECK_GT(pid_, 0);
647  int status;
648  pid_t found;
649  do {
650  found = ::waitpid(pid_, &status, 0);
651  } while (found == -1 && errno == EINTR);
652  // The only two remaining errors are ECHILD (other code reaped the
653  // child?), or EINVAL (cosmic rays?), and both merit an abort:
654  PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)";
655  // Though the child process had quit, this call does not close the pipes
656  // since its descendants may still be using them.
657  DCHECK_EQ(found, pid_);
659  pid_ = -1;
660  return returnCode_;
661 }
static ProcessReturnCode make(int status)
Definition: Subprocess.cpp:54
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
void enforce(State state) const
Definition: Subprocess.cpp:90
void folly::Subprocess::waitChecked ( )

Wait for the process to terminate, throw if unsuccessful.

Definition at line 663 of file Subprocess.cpp.

References folly::detail::distributed_mutex::wait().

Referenced by TEST().

663  {
664  wait();
665  checkStatus(returnCode_);
666 }
ProcessReturnCode returnCode_
Definition: Subprocess.h:942
ProcessReturnCode wait()
Definition: Subprocess.cpp:644

Member Data Documentation

const int folly::Subprocess::CLOSE = -1
static

Definition at line 271 of file Subprocess.h.

pid_t folly::Subprocess::pid_ {-1}
private

Definition at line 941 of file Subprocess.h.

const int folly::Subprocess::PIPE = -2
static

Definition at line 272 of file Subprocess.h.

Referenced by folly::Subprocess::Options::fd().

const int folly::Subprocess::PIPE_IN = -3
static

Definition at line 273 of file Subprocess.h.

Referenced by folly::Subprocess::Options::fd().

const int folly::Subprocess::PIPE_OUT = -4
static

Definition at line 274 of file Subprocess.h.

Referenced by folly::Subprocess::Options::fd().

std::vector<Pipe> folly::Subprocess::pipes_
private

Definition at line 970 of file Subprocess.h.

ProcessReturnCode folly::Subprocess::returnCode_
private

Definition at line 942 of file Subprocess.h.


The documentation for this class was generated from the following files: