From: Philipp Gesang Date: Wed, 3 Jan 2018 16:16:43 +0000 (+0100) Subject: handle pipe termination separately depending on whether a shell is present X-Git-Tag: v2.10~1^2~11 X-Git-Url: http://developer.intra2net.com/git/?p=libi2ncommon;a=commitdiff_plain;h=17b459b3c800f81801d70c9da644deb72b1230c5 handle pipe termination separately depending on whether a shell is present 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. --- diff --git a/src/pipestream.cpp b/src/pipestream.cpp index e026851..eeca4f2 100644 --- a/src/pipestream.cpp +++ b/src/pipestream.cpp @@ -207,7 +207,7 @@ mk_argv (const std::vector &command) * @returns A \c FILE* handle for streaming if successful, \c NULL * otherwise. */ -FILE * +std::pair 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 (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 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 &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 &command, PIPE_CTOR_FAIL("malloc"); } - this->pipe = this->init_without_shell (argv.get (), out, err); + std::pair 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 &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; diff --git a/src/pipestream.hxx b/src/pipestream.hxx index fe9e8c0..f283d87 100644 --- a/src/pipestream.hxx +++ b/src/pipestream.hxx @@ -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 + init_without_shell (const char *const *argv, + const bool out, const bool err) const; }; /** @brief stream around inpipebuf -- see comment there */