proxygen
|
#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 | |
Subprocess & | operator= (const Subprocess &)=delete |
Subprocess (Subprocess &&)=default | |
Subprocess & | operator= (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, IOBufQueue > | communicateIOBuf (IOBufQueue input=IOBufQueue()) |
std::pair< std::string, std::string > | communicate (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< ChildPipe > | takeOwnershipOfPipes () |
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< Pipe > | pipes_ |
Definition at line 269 of file Subprocess.h.
using folly::Subprocess::FdCallback = folly::Function<bool(int, int)> |
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.
|
delete |
|
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.
|
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.
folly::Subprocess::~Subprocess | ( | ) |
|
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_.
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.
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().
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.
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().
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().
|
private |
|
inline |
Definition at line 611 of file Subprocess.h.
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.
|
delete |
|
default |
|
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.
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().
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().
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.
|
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.
|
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().
|
inlinestatic |
Definition at line 816 of file Subprocess.h.
|
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().
|
private |
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.
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().
|
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().
|
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.
|
inline |
Definition at line 878 of file Subprocess.h.
|
inline |
Definition at line 872 of file Subprocess.h.
|
inline |
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().
|
inline |
Definition at line 608 of file Subprocess.h.
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().
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().
|
static |
Definition at line 271 of file Subprocess.h.
|
private |
Definition at line 941 of file Subprocess.h.
|
static |
Definition at line 272 of file Subprocess.h.
Referenced by folly::Subprocess::Options::fd().
|
static |
Definition at line 273 of file Subprocess.h.
Referenced by folly::Subprocess::Options::fd().
|
static |
Definition at line 274 of file Subprocess.h.
Referenced by folly::Subprocess::Options::fd().
|
private |
Definition at line 970 of file Subprocess.h.
|
private |
Definition at line 942 of file Subprocess.h.