From 7729a6d868b9c8afc0c8719a0426485706363548 Mon Sep 17 00:00:00 2001 From: John Hood Date: Fri, 29 Sep 2017 18:45:47 -0400 Subject: [PATCH 1/2] Fix uninitialized strings in test_pstreams --- test_pstreams.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test_pstreams.cc b/test_pstreams.cc index e8a29bc..e9bac25 100644 --- a/test_pstreams.cc +++ b/test_pstreams.cc @@ -705,10 +705,12 @@ int main() const size_t len = 256; char buf[len]; + buf[0] = '\0'; char* p = fgets(buf, len, out); cout << "STDOUT: " << buf; print_result(is, p!=NULL); + buf[0] = '\0'; p = fgets(buf, len, err); cout << "STDERR: " << buf; print_result(is, p!=NULL); @@ -730,10 +732,12 @@ int main() #if 0 size_t len = 256; char buf[len]; + buf[0] = '\0'; char* p = fgets(buf, len, out); cout << "STDOUT: " << buf; print_result(ps, p!=NULL); + buf[0] = '\0'; p = fgets(buf, len, err); cout << "STDERR: " << buf; print_result(ps, p!=NULL); From 248d10453ebb0bedae17c0c9f3059b1860f194b9 Mon Sep 17 00:00:00 2001 From: John Hood Date: Fri, 29 Sep 2017 18:46:50 -0400 Subject: [PATCH 2/2] Add a fds() accessor to retrieve file descriptors This is useful for applying FD_CLOEXEC. --- pstream.h | 65 +++++++++++++++++++++++++++++++++++++++++ test_pstreams.cc | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/pstream.h b/pstream.h index a3b2027..150a0c8 100644 --- a/pstream.h +++ b/pstream.h @@ -168,6 +168,10 @@ namespace redi fopen(FILE*& in, FILE*& out, FILE*& err); #endif + /// Obtain file descriptors for each of the process' standards streams. + std::size_t + fds(fd_type& in, fd_type& out, fd_type& err); + /// Return the exit status of the process. int status() const; @@ -359,6 +363,10 @@ namespace redi fopen(FILE*& in, FILE*& out, FILE*& err); #endif + /// Obtain file descriptors for each of the process' standards streams. + std::size_t + fds(fd_type& in, fd_type& out, fd_type& err); + protected: std::string command_; ///< The command used to start the process. streambuf_type buf_; ///< The stream buffer. @@ -2440,6 +2448,63 @@ namespace redi #endif // REDI_EVISCERATE_PSTREAMS + /** + * @warning This function exposes the internals of the stream buffer and + * should be used with caution. It is the caller's responsibility + * to manage any interactions between iostream and raw I/O on the + * file descriptors. + * + * @param in An integer that will be the process' stdin. + * @param out An integer that will be the process' stdout. + * @param err An integer that will be the process' stderr. + * @return An OR of zero or more of @c pstdin, @c pstdout, @c pstderr. + * + * For each open stream shared with the child process the file descriptor + * used is assigned to the corresponding parameter. For closed + * streams @c -1 is assigned to the parameter. + * The return value can be tested to see which parameters are valid + * by masking with the corresponding @c pmode value. + */ + template + std::size_t + basic_pstreambuf::fds(fd_type& in, fd_type& out, fd_type& err) + { + in = out = err = -1; + std::size_t open_files = 0; + if (wpipe() > -1) + { + in = wpipe(); + open_files |= pstdin; + } + if (rpipe(rsrc_out) > -1) + { + out = rpipe(rsrc_out); + open_files |= pstdout; + } + if (rpipe(rsrc_err) > -1) + { + err = rpipe(rsrc_err); + open_files |= pstderr; + } + return open_files; + } + + /** + * @warning This function exposes the internals of the stream buffer and + * should be used with caution. + * + * @param in An integer that will refer to the process' stdin. + * @param out An integer that will refer to the process' stdout. + * @param err An integer that will refer to the process' stderr. + * @return A bitwise-or of zero or more of @c pstdin, @c pstdout, @c pstderr. + * @see basic_pstreambuf::fds() + */ + template + inline std::size_t + pstream_common::fds(fd_type& fin, fd_type& fout, fd_type& ferr) + { + return buf_.fds(fin, fout, ferr); + } } // namespace redi diff --git a/test_pstreams.cc b/test_pstreams.cc index e9bac25..66d0dd4 100644 --- a/test_pstreams.cc +++ b/test_pstreams.cc @@ -743,8 +743,83 @@ int main() print_result(ps, p!=NULL); #endif } +#endif + + clog << "# Testing file descriptor access\n"; + + { + opstream os("tr '[:lower:]' '[:upper:]' | sed 's/^/STDIN: /'"); + int in, out, err; + size_t res = os.fds(in, out, err); + print_result(os, res & pstreambuf::pstdin); + print_result(os, in!=-1); + const char *msg = "flax\n"; + ssize_t i = write(in, msg, strlen(msg)); + print_result(os, i==static_cast(strlen(msg))); + } + + { + std::string cmd = "ls /etc/hosts /no/such/file"; + ipstream is(cmd, pstreambuf::pstdout|pstreambuf::pstderr); + int in, out, err; + size_t res = is.fds(in, out, err); + print_result(is, res & pstreambuf::pstdout); + print_result(is, res & pstreambuf::pstderr); + print_result(is, out!=-1); + print_result(is, err!=-1); + const size_t len = 256; + char buf[len]; + ssize_t i = read(out, buf, len); + ssize_t count = 0; + if (i > 0) { + count = i; + } + cout << "STDOUT: " << std::string(buf, count); + print_result(is, i>=0); + + i = read(err, buf, len); + count = 0; + if ( i > 0) { + count = i; + } + cout << "STDERR: " << std::string(buf, count); + print_result(is, i>=0); + } + + { + std::string cmd = "grep 127 -- - /etc/hosts /no/such/file"; + pstream ps(cmd, all3streams); + int in, out, err; + size_t res = ps.fds(in, out, err); + print_result(ps, res & pstreambuf::pstdin); + print_result(ps, res & pstreambuf::pstdout); + print_result(ps, res & pstreambuf::pstderr); + print_result(ps, in!=-1); + print_result(ps, out!=-1); + print_result(ps, err!=-1); + + // ps << "12345\n1112777\n0000" << EOF; +#if 0 + const size_t len = 256; + char buf[len]; + ssize_t i = read(out, buf, len); + ssize_t count = 0; + if (i > 0) { + count = i; + } + cout << "STDOUT: " << std::string(buf, count); + print_result(is, i>=0); + + i = read(err, buf, len); + count = 0; + if ( i > 0) { + count = i; + } + cout << "STDERR: " << std::string(buf, count); + print_result(is, i>=0); #endif + } clog << "# Testing resources freed correctly\n";