From e2d759fce9ff5e5886064ec693af54fe598773ff Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 23 Jul 2015 14:47:09 +0200 Subject: Add support for process redirection to existing fd, piping --- butl/process | 44 ++++++++++++++++++++++++++++---------- butl/process.cxx | 64 ++++++++++++++++++++++++++++++++------------------------ 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/butl/process b/butl/process index 52aad41..cf18acd 100644 --- a/butl/process +++ b/butl/process @@ -28,18 +28,36 @@ namespace butl struct process { - // Start another process using the specified command line. Connect the - // newly created process' stdin to out_fd. Also if connect_* are true, - // connect the created process' stdout and stderr to in_*fd. Throw - // process_error if anything goes wrong. + // Start another process using the specified command line. The default + // values to the in, out and err arguments indicate that the child + // process should inherit the parent process stdin, stdout, and stderr, + // respectively. If -1 is passed instead, then the corresponding child + // process descriptor is connected (via a pipe) to out_fd for stdin, + // in_ofd for stdout, and in_efd for stderr (see data members below). + // + // Instead of passing -1 or the default value, you can also pass your + // own descriptors. Note, however, that in this case they are not + // closed by 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); // - // Note that some of the exceptions (e.g., if exec() failed) can be - // thrown in the child version of us. + // Throw process_error if anything goes wrong. Note that some of the + // exceptions (e.g., if exec() failed) can be thrown in the child + // version of us. // - process (char const* args[], - bool connect_stdin = false, - bool connect_stderr = false, - bool connect_stdout = false); + process (char const* args[], int in = 0, int out = 1, int err = 2); + + // The "piping" constructor, for example: + // + // process lhs (..., 0, -1); // Redirect stdout to a pipe. + // process rhs (..., lhs); // Redirect stdin to lhs's pipe. + // + // rhs.wait (); // Wait for last first. + // lhs.wait (); + // + process (char const* args[], process& in, int out = 1, int err = 2); // Wait for the process to terminate. Return true if the process // terminated normally and with the zero exit status. Throw @@ -50,6 +68,9 @@ namespace butl ~process () {if (id != 0) wait ();} + process (const process&) = delete; + process& operator= (const process&) = delete; + #ifndef _WIN32 typedef pid_t id_type; #else @@ -57,9 +78,10 @@ namespace butl #endif id_type id; + int out_fd; // Write to this fd to send to the new process' stdin. - int in_efd; // Read from this fd to receive from the new process' stderr. int in_ofd; // Read from this fd to receive from the new process' stdout. + int in_efd; // Read from this fd to receive from the new process' stderr. }; } diff --git a/butl/process.cxx b/butl/process.cxx index 35ca26d..a456217 100644 --- a/butl/process.cxx +++ b/butl/process.cxx @@ -16,6 +16,8 @@ # include // _O_TEXT #endif +#include + using namespace std; namespace butl @@ -23,15 +25,15 @@ namespace butl #ifndef _WIN32 process:: - process (char const* args[], bool in, bool err, bool out) + process (char const* args[], int in, int out, int err) { - int out_fd[2]; - int in_efd[2]; - int in_ofd[2]; + int out_fd[2] = {in, 0}; + int in_ofd[2] = {0, out}; + int in_efd[2] = {0, err}; - if ((in && pipe (out_fd) == -1) || - (err && pipe (in_efd) == -1) || - (out && pipe (in_ofd) == -1)) + if ((in == -1 && pipe (out_fd) == -1) || + (out == -1 && pipe (in_ofd) == -1) || + (err == -1 && pipe (in_efd) == -1)) throw process_error (errno, false); id = fork (); @@ -44,31 +46,31 @@ namespace butl // Child. If requested, close the write end of the pipe and duplicate // the read end to stdin. Then close the original read end descriptor. // - if (in) + if (in != STDIN_FILENO) { - if (close (out_fd[1]) == -1 || - dup2 (out_fd[0], STDIN_FILENO) == -1 || - close (out_fd[0]) == -1) + if ((in == -1 && close (out_fd[1]) == -1) || + dup2 (out_fd[0], STDIN_FILENO) == -1 || + (in == -1 && close (out_fd[0]) == -1)) throw process_error (errno, true); } - // Do the same for the stderr if requested. + // Do the same for the stdout if requested. // - if (err) + if (out != STDOUT_FILENO) { - if (close (in_efd[0]) == -1 || - dup2 (in_efd[1], STDERR_FILENO) == -1 || - close (in_efd[1]) == -1) + if ((out == -1 && close (in_ofd[0]) == -1) || + dup2 (in_ofd[1], STDOUT_FILENO) == -1 || + (out == -1 && close (in_ofd[1]) == -1)) throw process_error (errno, true); } - // Do the same for the stdout if requested. + // Do the same for the stderr if requested. // - if (out) + if (err != STDERR_FILENO) { - if (close (in_ofd[0]) == -1 || - dup2 (in_ofd[1], STDOUT_FILENO) == -1 || - close (in_ofd[1]) == -1) + if ((err == -1 && close (in_efd[0]) == -1) || + dup2 (in_efd[1], STDERR_FILENO) == -1 || + (err == -1 && close (in_efd[1]) == -1)) throw process_error (errno, true); } @@ -79,15 +81,23 @@ namespace butl { // Parent. Close the other ends of the pipes. // - if ((in && close (out_fd[0]) == -1) || - (err && close (in_efd[1]) == -1) || - (out && close (in_ofd[1]) == -1)) + if ((in == -1 && close (out_fd[0]) == -1) || + (out == -1 && close (in_ofd[1]) == -1) || + (err == -1 && close (in_efd[1]) == -1)) throw process_error (errno, false); } - this->out_fd = in ? out_fd[1] : 0; - this->in_efd = err ? in_efd[0] : 0; - this->in_ofd = out ? in_ofd[0] : 0; + this->out_fd = in == -1 ? out_fd[1] : -1; + this->in_ofd = out == -1 ? in_ofd[0] : -1; + this->in_efd = err == -1 ? in_efd[0] : -1; + } + + process:: + process (char const* args[], process& in, int out, int err) + : process (args, in.in_ofd, out, err) + { + assert (in.in_ofd != -1); // Should be a pipe. + close (in.in_ofd); // Close it on our side. } bool process:: -- cgit v1.1