From cdc166d250757b40a00fbdc82a446c615c8bdd65 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 17 Apr 2018 11:32:20 +0200 Subject: [PATCH] add option to forward environment to pipestream Add an argument to all execve-based pipestream APIs to control whether *environ* should be passed through to the executed command. Till now this used to depend on whether path lookup was requested which is rather undesirable. Unit tests included. --- src/pipestream.cpp | 37 ++++++++++++++++++------------ src/pipestream.hxx | 24 ++++++++++---------- test/test_pipestream.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/src/pipestream.cpp b/src/pipestream.cpp index 57b1dbe..fcd8161 100644 --- a/src/pipestream.cpp +++ b/src/pipestream.cpp @@ -53,7 +53,7 @@ on this file might be covered by the GNU General Public License. template std::string capture_exec(CmdT command, ExecResult &rescode, const bool out, const bool err, - const bool path) + const bool path, const bool env) { std::string output; @@ -67,7 +67,7 @@ std::string capture_exec(CmdT command, ExecResult &rescode, try { { - inpipestream ips(command, out, err, path); + inpipestream ips(command, out, err, path, env); ips.store_exit_status(&exit_set, &exit_status_waitpid); @@ -115,7 +115,7 @@ std::string capture_exec(CmdT command, ExecResult &rescode, * passed. */ std::string capture_exec (const std::string &command, ExecResult &res) -{ return capture_exec(command, res, true, false, false); } +{ return capture_exec(command, res, true, false, false, false); } /** @brief Instantiation of \c capture_exec for argument lists. The * pipestream used to run the command will not shell out. @@ -134,8 +134,9 @@ std::string capture_exec (const std::string &command, ExecResult &res) * @returns Captured output, combined into one string. */ std::string capture_exec (const char *const *command, ExecResult &res, - const bool out, const bool err, const bool path) -{ return capture_exec(command, res, out, err, path); } + const bool out, const bool err, const bool path, + const bool env) +{ return capture_exec(command, res, out, err, path, env); } /** @brief Instantiation of \c capture_exec for argument lists. The * pipestream used to run the command will not shell out. @@ -153,10 +154,11 @@ std::string capture_exec (const char *const *command, ExecResult &res, * @returns Captured output, combined into one string. */ std::string capture_exec (const std::vector &command, ExecResult &res, - const bool out, const bool err, const bool path) + const bool out, const bool err, const bool path, + const bool env) { return capture_exec &> - (command, res, out, err, path); + (command, res, out, err, path, env); } #define PIPE_CTOR_FAIL(where) \ @@ -249,12 +251,14 @@ std::pair inpipebuf::init_without_shell (const char *const *argv, const bool out, const bool err, - const bool path) const + const bool path, + const bool env) const { FILE *pipeobj = NULL; int pipefd [2]; /* for reading output from the child */ int errfd [2]; /* for determining a successful exec() */ sigset_t oldmask, newmask; + char *const *envp = env ? environ : NULL; if (!out && !err) { errno = EINVAL; @@ -311,9 +315,9 @@ inpipebuf::init_without_shell (const char *const *argv, errno = 0; if (path) { - execvpe (argv [0], const_cast (argv), environ); + execvpe (argv [0], const_cast (argv), envp); } else { - execve (argv [0], const_cast (argv), NULL); + execve (argv [0], const_cast (argv), envp); } (void)write (errfd [1], (char *)&errno, sizeof(errno)); @@ -380,7 +384,8 @@ inpipebuf::init_without_shell (const char *const *argv, inpipebuf::inpipebuf(const char *const *command, const bool out, const bool err, - const bool path) + const bool path, + const bool env) : pipe (NULL) /* brr: shadowing global ident */ , pid (-1) , status_set (NULL) @@ -391,7 +396,7 @@ inpipebuf::inpipebuf(const char *const *command, } std::pair tmp = - this->init_without_shell (command, out, err, path); + this->init_without_shell (command, out, err, path, env); this->pid = tmp.first; /* no std::tie :/ */ this->pipe = tmp.second; @@ -401,7 +406,8 @@ inpipebuf::inpipebuf(const char *const *command, inpipebuf::inpipebuf(const std::vector &command, const bool out, const bool err, - const bool path) + const bool path, + const bool env) : pipe (NULL) /* brr: shadowing global ident */ , pid (-1) , status_set (NULL) @@ -417,7 +423,7 @@ inpipebuf::inpipebuf(const std::vector &command, } std::pair tmp = - this->init_without_shell (argv.get (), out, err, path); + this->init_without_shell (argv.get (), out, err, path, env); this->pid = tmp.first; this->pipe = tmp.second; @@ -427,7 +433,8 @@ inpipebuf::inpipebuf(const std::vector &command, inpipebuf::inpipebuf(const std::string& command, const bool _ignored_out, const bool _ignored_err, - const bool _ignored_path) + const bool _ignored_path, + const bool _ignored_env) : pid (-1) , status_set (NULL) , exit_status (NULL) diff --git a/src/pipestream.hxx b/src/pipestream.hxx index f53f474..8668df3 100644 --- a/src/pipestream.hxx +++ b/src/pipestream.hxx @@ -71,10 +71,10 @@ typedef struct ExecResult ExecResult; std::string capture_exec(const std::string& command, ExecResult &rescode); std::string capture_exec(const char *const *command, ExecResult &rescode, const bool out=true, const bool err=false, - const bool path=false); + const bool path=false, const bool env=false); std::string capture_exec(const std::vector& command, ExecResult &rescode, const bool out=true, const bool err=false, - const bool path=false); + const bool path=false, const bool env=false); inline std::string capture_exec (const std::string &command) { @@ -117,11 +117,11 @@ protected: public: inpipebuf(const std::string& command, - const bool out, const bool err, const bool path); + const bool out, const bool err, const bool path, const bool env); inpipebuf(const char *const *command, - const bool out, const bool err, const bool path); + const bool out, const bool err, const bool path, const bool env); inpipebuf(const std::vector &command, - const bool out, const bool err, const bool path); + const bool out, const bool err, const bool path, const bool env); ~inpipebuf(); @@ -134,7 +134,7 @@ private: std::pair init_without_shell (const char *const *argv, const bool out, const bool err, - const bool path) const; + const bool path, const bool env) const; }; /** @brief stream around inpipebuf -- see comment there */ @@ -146,20 +146,20 @@ protected: public: inpipestream(const std::string& command, const bool out=true, const bool err=false, - const bool path=false) - : std::istream(&buf), buf(command, out, err, path) + const bool path=false, const bool env=false) + : std::istream(&buf), buf(command, out, err, path, env) {} inpipestream(const char *const command[], const bool out=true, const bool err=false, - const bool path=false) - : std::istream(&buf), buf(command, out, err, path) + const bool path=false, const bool env=false) + : std::istream(&buf), buf(command, out, err, path, env) {} inpipestream(const std::vector &command, const bool out=true, const bool err=false, - const bool path=false) - : std::istream(&buf), buf(command, out, err, path) + const bool path=false, const bool env=false) + : std::istream(&buf), buf(command, out, err, path, env) {} void store_exit_status(bool *_status_set, int *_exit_status) diff --git a/test/test_pipestream.cpp b/test/test_pipestream.cpp index e145ab7..8924861 100644 --- a/test/test_pipestream.cpp +++ b/test/test_pipestream.cpp @@ -47,7 +47,21 @@ #define TO_CHARP_TOK(x) #x #define TO_CHARP(x) TO_CHARP_TOK(x) -BOOST_AUTO_TEST_SUITE(pipestream) +#define I2N_EXTRA_ENV "I2N_EXTRA_ENV" + +struct Test_Pipestream_Fixture +{ + char **saved_environ; + + Test_Pipestream_Fixture (void) : saved_environ (environ) { } + + ~Test_Pipestream_Fixture (void) { + environ = this->saved_environ; + (void)unsetenv (I2N_EXTRA_ENV); + } +}; + +BOOST_FIXTURE_TEST_SUITE(pipestream, Test_Pipestream_Fixture) BOOST_AUTO_TEST_SUITE(read) @@ -327,5 +341,43 @@ BOOST_AUTO_TEST_SUITE(pipestream) BOOST_AUTO_TEST_SUITE_END() /* [pipestream->read] */ + BOOST_AUTO_TEST_SUITE(env) + + static const char *const env_argv_abs [] = { "/usr/bin/env", NULL }; + + #define I2N_EXTRA_ENVIRON I2N_EXTRA_ENV "=Yet verily, the rose is within the thorn." + static char *i2n_extra_environ [] = + { const_cast (I2N_EXTRA_ENVIRON) + , NULL + }; + + BOOST_AUTO_TEST_CASE(env_passthrough) + { + ExecResult exres = ExecResult (); + environ = i2n_extra_environ; + + const std::string result = + capture_exec (env_argv_abs, exres, true, true, false, true); + + BOOST_CHECK(exres.normal_exit); + BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS); + BOOST_CHECK_EQUAL(result, (std::string)(I2N_EXTRA_ENVIRON "\n")); + } + + BOOST_AUTO_TEST_CASE(env_nil) + { + ExecResult exres = ExecResult (); + environ = i2n_extra_environ; + + const std::string result = + capture_exec (env_argv_abs, exres, true, true, false, false); + + BOOST_CHECK(exres.normal_exit); + BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS); + BOOST_CHECK_EQUAL(result, std::string()); + } + + BOOST_AUTO_TEST_SUITE_END() /* [pipestream->env] */ + BOOST_AUTO_TEST_SUITE_END() /* [pipestream] */ -- 1.7.1