handle pipe termination separately depending on whether a shell is present
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Wed, 3 Jan 2018 16:16:43 +0000 (17:16 +0100)
committerPhilipp Gesang <philipp.gesang@intra2net.com>
Tue, 14 Aug 2018 14:53:34 +0000 (16:53 +0200)
Keep the child's pid around and wait() for it to perish in the
dtor of inpipestream. Return the error from closing the pipe or
terminating the process in the shell-free version. The mechanism
for retrieving the status does not allow to distinguish between
the two, so always prefer the latter.

src/pipestream.cpp
src/pipestream.hxx

index e026851..eeca4f2 100644 (file)
@@ -207,7 +207,7 @@ mk_argv (const std::vector<std::string> &command)
  *  @returns          A \c FILE* handle for streaming if successful, \c NULL
  *                    otherwise.
  */
-FILE *
+std::pair <pid_t, FILE *>
 inpipebuf::init_without_shell (const char *const *argv,
                                const bool out,
                                const bool err) const
@@ -247,6 +247,8 @@ inpipebuf::init_without_shell (const char *const *argv,
                 PIPE_CTOR_FAIL("dup2/stderr");
             }
 
+            close (pipefd [1]);
+
             errno = 0;
             if (execve (argv [0], const_cast <char *const *>(argv), NULL) == -1) {
                 PIPE_CTOR_FAIL("exec");
@@ -264,13 +266,14 @@ inpipebuf::init_without_shell (const char *const *argv,
         }
     }
 
-    return pipeobj;
+    return std::make_pair (childpid, pipeobj);
 }
 
 inpipebuf::inpipebuf(const char *const *command,
                      const bool out,
                      const bool err)
     : pipe (NULL) /* brr: shadowing global ident */
+    , pid (-1)
     , status_set (NULL)
     , exit_status (NULL)
 {
@@ -278,7 +281,9 @@ inpipebuf::inpipebuf(const char *const *command,
         PIPE_CTOR_FAIL("command");
     }
 
-    this->pipe = this->init_without_shell (command, out, err);
+    std::pair <pid_t, FILE *> tmp = this->init_without_shell (command, out, err);
+    this->pid  = tmp.first; /* no std::tie :/ */
+    this->pipe = tmp.second;
 
     setg (&buffer, &buffer, &buffer);
 }
@@ -287,6 +292,7 @@ inpipebuf::inpipebuf(const std::vector<std::string> &command,
                      const bool out,
                      const bool err)
     : pipe (NULL) /* brr: shadowing global ident */
+    , pid (-1)
     , status_set (NULL)
     , exit_status (NULL)
 {
@@ -299,7 +305,9 @@ inpipebuf::inpipebuf(const std::vector<std::string> &command,
         PIPE_CTOR_FAIL("malloc");
     }
 
-    this->pipe = this->init_without_shell (argv.get (), out, err);
+    std::pair <pid_t, FILE *> tmp = this->init_without_shell (argv.get (), out, err);
+    this->pid  = tmp.first;
+    this->pipe = tmp.second;
 
     setg (&buffer, &buffer, &buffer);
 }
@@ -307,10 +315,10 @@ inpipebuf::inpipebuf(const std::vector<std::string> &command,
 inpipebuf::inpipebuf(const std::string& command,
                      const bool _ignored_out,
                      const bool _ignored_err)
+    : pid (-1)
+    , status_set (NULL)
+    , exit_status (NULL)
 {
-    status_set = NULL;
-    exit_status = NULL;
-
     pipe = popen (command.c_str(), "r");
     if (pipe == NULL)
         throw EXCEPTION (pipestream_error, "can't open program or permission denied");
@@ -322,13 +330,47 @@ inpipebuf::inpipebuf(const std::string& command,
 inpipebuf::~inpipebuf()
 {
     if (pipe != NULL) {
-        int pclose_exit = pclose (pipe);
+        int status;
 
-        if (exit_status && pclose_exit != -1)
+        if (this->pid == -1)
         {
-            if (status_set)
-                *status_set = true;
-            *exit_status = pclose_exit;
+            errno = 0;
+            status = pclose (pipe);
+            if (status != -1) {
+                if (exit_status != NULL) {
+                    *exit_status = status;
+                    if (status_set != NULL) {
+                        *status_set = true;
+                    }
+                }
+            }
+        }
+        else
+        {
+            errno = 0;
+            status = fclose (pipe);
+            if (status != EOF) {
+                if (exit_status != NULL) {
+                    *exit_status = status; /* might be overwritten below */
+                    if (status_set != NULL) {
+                        *status_set = true;
+                    }
+                }
+            }
+
+            errno = 0;
+            while (waitpid (this->pid, &status, 0) == -1) {
+                if (errno != EINTR) {
+                    status = -1;
+                    break;
+                }
+            }
+            if (status != 0 && exit_status != NULL) {
+                *exit_status = status; /* might overwrite pipe status above */
+                if (status_set != NULL) {
+                    *status_set = true;
+                }
+            }
         }
 
         pipe = NULL;
index fe9e8c0..f283d87 100644 (file)
@@ -93,6 +93,7 @@ protected:
     char buffer;
 
     FILE *pipe;
+    pid_t pid;
 
     // "callback" variables for destructor to store exit status
     bool *status_set;
@@ -114,8 +115,9 @@ protected:
     virtual int_type underflow();
 
 private:
-    FILE *init_without_shell (const char *const *argv,
-                              const bool out, const bool err) const;
+    std::pair <pid_t, FILE *>
+    init_without_shell (const char *const *argv,
+                        const bool out, const bool err) const;
 };
 
 /** @brief stream around inpipebuf -- see comment there */