From 9e322fb79a131de29d5563c23e92774a4c3b6dd3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 9 Feb 2018 14:21:41 +0100 Subject: [PATCH] add shell-free pipestream Overload the pipestream ctor with a variant that avoids shelling out with *popen(3)* that is chosen by passing an argument list in lieu of a command. --- src/pipestream.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/pipestream.hxx | 17 +++++++- 2 files changed, 127 insertions(+), 3 deletions(-) diff --git a/src/pipestream.cpp b/src/pipestream.cpp index afa6d56..ebcc6c2 100644 --- a/src/pipestream.cpp +++ b/src/pipestream.cpp @@ -24,16 +24,21 @@ on this file might be covered by the GNU General Public License. copyright : (C) 2001 by Intra2net AG ***************************************************************************/ +#include #include +#include #include +#include -#include #include #include #include #include +#include +#include #include "exception.hxx" +#include "stringfunc.hxx" #include "pipestream.hxx" /** @brief runs command and returns it's output as string @@ -41,7 +46,8 @@ on this file might be covered by the GNU General Public License. * @param rescode struct containing the return code, if the program exited normally and so on * @returns the output (stdout) of the called program */ -std::string capture_exec(const std::string& command, ExecResult &rescode) +template +std::string capture_exec(CmdT command, ExecResult &rescode) { std::string output; @@ -88,6 +94,109 @@ std::string capture_exec(const std::string& command, ExecResult &rescode) return output; } +std::string capture_exec (const std::string &command, ExecResult &res) +{ return capture_exec(command, res); } + +std::string capture_exec (const char *const *command, ExecResult &res) +{ return capture_exec(command, res); } + +#define PIPE_CTOR_FAIL(where) \ + do { \ + throw EXCEPTION (pipestream_error, \ + std::string (where) + ": error " \ + + I2n::to_string (errno) \ + + " (" + std::string (strerror (errno)) + ")"); \ + } while (0) + +static boost::shared_array +mk_argv (const std::vector &command) +{ + char **ret = NULL; + + try { + ret = new char *[command.size () * sizeof (ret[0]) + 1]; + } catch (std::bad_alloc &) { + return boost::shared_array (); + } + + size_t cur = 0; + BOOST_FOREACH(const std::string &arg, command) { + /* + * Casting away constness is safe since the data is always + * kept alive until after exec(). + */ + ret [cur++] = const_cast (arg.c_str ()); + } + + ret [cur] = NULL; + + return boost::shared_array (ret); +} + + +FILE * +inpipebuf::init_without_shell (const char *const *argv) const +{ + FILE *pipeobj = NULL; + int pipefd [2]; + + errno = 0; + if (::pipe (pipefd) == -1) { + PIPE_CTOR_FAIL("pipe"); + } + + errno = 0; + pid_t childpid = fork (); + switch (childpid) { + case -1: { + PIPE_CTOR_FAIL("fork"); + break; + } + case 0: { + close (pipefd [0]); + + if (dup2 (pipefd[1], STDOUT_FILENO) == -1) { + PIPE_CTOR_FAIL("dup2"); + } + + if (dup2 (pipefd[1], STDERR_FILENO) == -1) { + PIPE_CTOR_FAIL("dup2"); + } + + errno = 0; + if (execve (argv [0], const_cast (argv), NULL) == -1) { + PIPE_CTOR_FAIL("exec"); + } + break; + } + default: { + close (pipefd [1]); + + errno = 0; + if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) { + PIPE_CTOR_FAIL("fdopen"); + } + break; + } + } + + return pipeobj; +} + +inpipebuf::inpipebuf(const char *const *command) + : pipe (NULL) /* brr: shadowing global ident */ + , status_set (NULL) + , exit_status (NULL) +{ + if (command == NULL || command [0] == NULL) { + PIPE_CTOR_FAIL("command"); + } + + this->pipe = this->init_without_shell (command); + + setg (&buffer, &buffer, &buffer); +} + inpipebuf::inpipebuf(const std::string& command) { status_set = NULL; diff --git a/src/pipestream.hxx b/src/pipestream.hxx index 9280859..6f83006 100644 --- a/src/pipestream.hxx +++ b/src/pipestream.hxx @@ -54,8 +54,15 @@ struct ExecResult typedef struct ExecResult ExecResult; std::string capture_exec(const std::string& command, ExecResult &rescode); +std::string capture_exec(const char *const *command, ExecResult &rescode); -inline std::string capture_exec(const std::string& command) +inline std::string capture_exec (const std::string &command) +{ + ExecResult r; + return capture_exec(command,r); +} + +inline std::string capture_exec(const char *const *command) { ExecResult r; return capture_exec(command,r); @@ -74,6 +81,7 @@ class inpipebuf : public std::streambuf { protected: char buffer; + FILE *pipe; // "callback" variables for destructor to store exit status @@ -82,6 +90,7 @@ protected: public: inpipebuf(const std::string& command); + inpipebuf(const char *const *command); ~inpipebuf(); @@ -89,6 +98,9 @@ public: protected: virtual int_type underflow(); + +private: + FILE *init_without_shell (const char *const *argv) const; }; /** @brief stream around inpipebuf -- see comment there */ @@ -101,6 +113,9 @@ public: inpipestream(const std::string& command) : std::istream(&buf), buf(command) {} + inpipestream(const char *const command[]) + : std::istream(&buf), buf(command) + {} void store_exit_status(bool *_status_set, int *_exit_status) { buf.store_exit_status(_status_set, _exit_status); } -- 1.7.1