add option to forward environment to pipestream
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Tue, 17 Apr 2018 09:32:20 +0000 (11:32 +0200)
committerPhilipp Gesang <philipp.gesang@intra2net.com>
Tue, 14 Aug 2018 14:53:34 +0000 (16:53 +0200)
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
src/pipestream.hxx
test/test_pipestream.cpp

index 57b1dbe..fcd8161 100644 (file)
@@ -53,7 +53,7 @@ on this file might be covered by the GNU General Public License.
 template <typename CmdT>
 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<const std::string &>(command, res, true, false, false); }
+{ return capture_exec<const std::string &>(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<const char *const *>(command, res, out, err, path); }
+                          const bool out, const bool err, const bool path,
+                          const bool env)
+{ return capture_exec<const char *const *>(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<std::string> &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<const std::vector<std::string> &>
-        (command, res, out, err, path);
+        (command, res, out, err, path, env);
 }
 
 #define PIPE_CTOR_FAIL(where) \
@@ -249,12 +251,14 @@ std::pair <pid_t, FILE *>
 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 <char *const *>(argv), environ);
+                execvpe (argv [0], const_cast <char *const *>(argv), envp);
             } else {
-                execve (argv [0], const_cast <char *const *>(argv), NULL);
+                execve (argv [0], const_cast <char *const *>(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 <pid_t, FILE *> 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<std::string> &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<std::string> &command,
     }
 
     std::pair <pid_t, FILE *> 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<std::string> &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)
index f53f474..8668df3 100644 (file)
@@ -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<std::string>& 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<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();
 
@@ -134,7 +134,7 @@ private:
     std::pair <pid_t, FILE *>
     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<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)
     {}
 
     void store_exit_status(bool *_status_set, int *_exit_status)
index e145ab7..8924861 100644 (file)
 #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<char*> (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] */