diff options
Diffstat (limited to 'libbutl/process.hxx')
-rw-r--r-- | libbutl/process.hxx | 164 |
1 files changed, 142 insertions, 22 deletions
diff --git a/libbutl/process.hxx b/libbutl/process.hxx index 47cc507..bbb7c89 100644 --- a/libbutl/process.hxx +++ b/libbutl/process.hxx @@ -117,8 +117,8 @@ namespace butl // Moveable-only type. // - process_path (process_path&&); - process_path& operator= (process_path&&); + process_path (process_path&&) noexcept; + process_path& operator= (process_path&&) noexcept; process_path (const process_path&) = delete; process_path& operator= (const process_path&) = delete; @@ -269,7 +269,30 @@ namespace butl // the parent. So you should do this yourself, if required. For example, // to redirect the child process stdout to stderr, you can do: // - // process p (..., 0, 2); + // process pr (..., 0, 2); + // + // Note also that the somewhat roundabout setup with -1 as a redirect + // "instruction" and out_fd/in_ofd/in_efd data members for the result + // helps to make sure the stream instances are destroyed before the + // process instance. For example: + // + // process pr (..., 0, -1, 2); + // ifdstream is (move (pr.in_ofd)); + // + // This is important in case an exception is thrown where we want to make + // sure all our pipe ends are closed before we wait for the process exit + // (which happens in the process destructor). + // + // And speaking of the destruction order, another thing to keep in mind is + // that only one stream can use the skip mode (fdstream_mode::skip; + // because skipping is performed in the blocking mode) and the stream that + // skips should come first so that all other streams are destroyed/closed + // before it (failed that, we may end up in a deadlock). For example: + // + // process pr (..., -1, -1, -1); + // ifdstream is (move (pr.in_ofd), fdstream_mode::skip); // Must be first. + // ifdstream es (move (pr.in_efd)); + // ofdstream os (move (pr.out_fd)); // // The cwd argument allows to change the current working directory of the // child process. NULL and empty arguments are ignored. @@ -287,39 +310,104 @@ namespace butl // Note that the versions without the the process_path argument may // temporarily change args[0] (see path_search() for details). // - process (const char* [], + process (const char**, + int in = 0, int out = 1, int err = 2, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const process_path&, const char* const*, int in = 0, int out = 1, int err = 2, const char* cwd = nullptr, const char* const* envvars = nullptr); - process (const process_path&, const char* [], + process (std::vector<const char*>&, + int in = 0, int out = 1, int err = 2, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const process_path&, const std::vector<const char*>&, int in = 0, int out = 1, int err = 2, const char* cwd = nullptr, const char* const* envvars = nullptr); // If the descriptors are pipes that you have created, then you should use - // this constructor instead to communicate this information. + // this constructor instead to communicate this information (the parent + // end may need to be "probed" on Windows). // // For generality, if the "other" end of the pipe is -1, then assume this // is not a pipe. // struct pipe { - int in = -1; - int out = -1; - pipe () = default; pipe (int i, int o): in (i), out (o) {} explicit pipe (const fdpipe& p): in (p.in.get ()), out (p.out.get ()) {} + + // Transfer ownership to one end of the pipe. + // + pipe (auto_fd i, int o): in (i.release ()), out (o), own_in (true) {} + pipe (int i, auto_fd o): in (i), out (o.release ()), own_out (true) {} + + // Moveable-only type. + // + pipe (pipe&&) noexcept; + pipe& operator= (pipe&&) noexcept; + + pipe (const pipe&) = delete; + pipe& operator= (const pipe&) = delete; + + ~pipe (); + + public: + int in = -1; + int out = -1; + + bool own_in = false; + bool own_out = false; }; - process (const process_path&, const char* [], + process (const char**, pipe in, pipe out, pipe err, const char* cwd = nullptr, const char* const* envvars = nullptr); + process (const char**, + int in, int out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const process_path&, const char* const*, + pipe in, pipe out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const process_path&, const char* const*, + int in, int out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (std::vector<const char*>&, + pipe in, pipe out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (std::vector<const char*>&, + int in, int out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const process_path&, const std::vector<const char*>&, + pipe in, pipe out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const process_path&, const std::vector<const char*>&, + int in, int out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + // The "piping" constructor, for example: // // process lhs (..., 0, -1); // Redirect stdout to a pipe. @@ -328,16 +416,36 @@ namespace butl // rhs.wait (); // Wait for last first. // lhs.wait (); // - process (const char* [], + process (const char**, process&, int out = 1, int err = 2, const char* cwd = nullptr, const char* const* envvars = nullptr); - process (const process_path&, const char* [], + process (const process_path&, const char* const*, process&, int out = 1, int err = 2, const char* cwd = nullptr, const char* const* envvars = nullptr); + process (const char**, + process&, pipe out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const char**, + process&, int out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const process_path&, const char* const*, + process&, pipe out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + + process (const process_path&, const char* const*, + process&, int out, pipe err, + const char* cwd = nullptr, + const char* const* envvars = nullptr); + // Wait for the process to terminate. Return true if the process // terminated normally and with the zero exit code. Unless ignore_error // is true, throw process_error if anything goes wrong. This function can @@ -364,7 +472,7 @@ namespace butl // Note that the destructor will wait for the process but will ignore // any errors and the exit status. // - ~process () {if (handle != 0) wait (true);} + ~process () { if (handle != 0) wait (true); } // Process termination. // @@ -391,8 +499,8 @@ namespace butl // Moveable-only type. // - process (process&&); - process& operator= (process&&); + process (process&&) noexcept; + process& operator= (process&&) noexcept (false); // Note: calls wait(). process (const process&) = delete; process& operator= (const process&) = delete; @@ -414,7 +522,7 @@ namespace butl // // ... // E.g., print args[0]. // - // process p (pp, args); + // process pr (pp, args); // // You can also specify the fallback directory which will be tried last. // This, for example, can be used to implement the Windows "search in the @@ -498,7 +606,7 @@ namespace butl // nameN arg arg ... nullptr nullptr // static void - print (std::ostream&, const char* const args[], size_t n = 0); + print (std::ostream&, const char* const* args, size_t n = 0); // Quote and escape the specified command line argument. If batch is true // then also quote the equal (`=`), comma (`,`) and semicolon (`;`) @@ -521,13 +629,16 @@ namespace butl public: handle_type handle; + static handle_type + current_handle (); + // Absence means that the exit information is not (yet) known. This can be // because you haven't called wait() yet or because wait() failed. // optional<process_exit> exit; - // Use the following file descriptors to communicate with the new process's - // standard streams. + // Use the following file descriptors to communicate with the new + // process's standard streams (if redirected to pipes; see above). // auto_fd out_fd; // Write to it to send to stdin. auto_fd in_ofd; // Read from it to receive from stdout. @@ -641,8 +752,8 @@ namespace butl // Moveable-only type. // - process_env (process_env&&); - process_env& operator= (process_env&&); + process_env (process_env&&) noexcept; + process_env& operator= (process_env&&) noexcept; process_env (const process_env&) = delete; process_env& operator= (const process_env&) = delete; @@ -678,7 +789,7 @@ namespace butl // command line or similar. It should be callable with the following // signature: // - // void (const char*[], std::size_t) + // void (const char* const*, std::size_t) // template <typename C, typename I, @@ -719,6 +830,15 @@ namespace butl const process_env&, A&&... args); + // Call the callback without actually running/starting anything. + // + template <typename C, + typename... A> + void + process_print_callback (const C&, + const process_env&, + A&&... args); + // Conversion of types to their C string representations. Can be overloaded // (including via ADL) for custom types. The default implementation calls // to_string() which covers all the numeric values via std::to_string () and |