From 59e902394af94d57fcd83fb1c70c97768c275567 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 28 Aug 2015 00:09:51 +0100 Subject: Add ProcessPipes as a container for a child process with FDs for stdin, out and err --- libadhocutil/processPipes.cpp | 138 ++++++++++++++++++++++++++++ libadhocutil/processPipes.h | 33 +++++++ libadhocutil/unittests/Jamfile.jam | 13 +++ libadhocutil/unittests/testProcessPipes.cpp | 23 +++++ 4 files changed, 207 insertions(+) create mode 100644 libadhocutil/processPipes.cpp create mode 100644 libadhocutil/processPipes.h create mode 100644 libadhocutil/unittests/testProcessPipes.cpp diff --git a/libadhocutil/processPipes.cpp b/libadhocutil/processPipes.cpp new file mode 100644 index 0000000..55050ae --- /dev/null +++ b/libadhocutil/processPipes.cpp @@ -0,0 +1,138 @@ +#include "processPipes.h" +#include +#include +#include +#include +#include + +ProcessPipes::ProcessPipes(const std::vector & args, bool i, bool o, bool e) : + in(-1), + out(-1), + error(-1) +{ + int ipipes[2], opipes[2], epipes[2]; + if (i) { + if (pipe(ipipes)) { + throw std::runtime_error("Failed to create stdin pipe"); + } + } + if (o) { + if (pipe(opipes)) { + if (i) { + close(ipipes[0]); + close(ipipes[1]); + } + throw std::runtime_error("Failed to create stdout pipe"); + } + } + if (e) { + if (pipe(epipes)) { + if (i) { + close(ipipes[0]); + close(ipipes[1]); + } + if (o) { + close(opipes[0]); + close(opipes[1]); + } + throw std::runtime_error("Failed to create stderr pipe"); + } + } + switch (child = fork()) { + case -1: // fail + if (i) { + close(ipipes[0]); + close(ipipes[1]); + } + if (o) { + close(opipes[0]); + close(opipes[1]); + } + if (e) { + close(epipes[0]); + close(epipes[1]); + } + throw std::runtime_error("Failed to fork"); + default: // parent + if (i) { + close(ipipes[0]); + in = ipipes[1]; + } + if (o) { + close(opipes[1]); + out = opipes[0]; + } + if (e) { + close(epipes[1]); + error = epipes[0]; + } + break; + case 0: // in child + rlimit lim; + getrlimit(RLIMIT_NOFILE, &lim); + if (i) { + close(ipipes[1]); + dup2(ipipes[0], 0); + } + if (o) { + close(opipes[0]); + dup2(opipes[1], 1); + } + if (e) { + close(epipes[0]); + dup2(epipes[1], 2); + } + std::vector fds; + fds.reserve(lim.rlim_max); + for (int n = 3; n < (int)lim.rlim_max; n += 1) { + fds.push_back({n, 0, 0}); + } + poll(&fds.front(), fds.size(), 0); + for(const auto & pfd : fds) { + if (!(pfd.revents & POLLNVAL)) { + close(pfd.fd); + } + } + char * buf[100]; + char ** w = &buf[0]; + for (const auto & p : args) { + *w++ = strdup(p.c_str()); + } + *w = NULL; + execv(buf[0], buf); + abort(); + break; + } +} + +ProcessPipes::~ProcessPipes() +{ + if (in) close(in); + if (out) close(out); + if (error) close(error); +} + +int +ProcessPipes::fdIn() const +{ + return in; +} + +int +ProcessPipes::fdOut() const +{ + return out; +} + +int +ProcessPipes::fdError() const +{ + return error; +} + +pid_t +ProcessPipes::pid() const +{ + return child; +} + diff --git a/libadhocutil/processPipes.h b/libadhocutil/processPipes.h new file mode 100644 index 0000000..a1d0800 --- /dev/null +++ b/libadhocutil/processPipes.h @@ -0,0 +1,33 @@ +#ifndef ADHOCUTIL_PROCESSPIPES_H +#define ADHOCUTIL_PROCESSPIPES_H + +#include +#include +#include "visibility.h" + +/** + * Spawn a new process, providing access to it's stdin and stdout + * @param params path and arguments to spawn (copied and passed to execv) + * @param fds (out) the FDs on the childs stdin(0) and stdout(1) + * @return the process ID of the child + */ +class DLL_PUBLIC ProcessPipes { + public: + ProcessPipes(const std::vector & args, bool in, bool out, bool err); + ~ProcessPipes(); + + ProcessPipes(const ProcessPipes &) = delete; + void operator=(const ProcessPipes &) = delete; + + int fdIn() const; + int fdOut() const; + int fdError() const; + pid_t pid() const; + + private: + int in, out, error; + pid_t child; +}; + +#endif + diff --git a/libadhocutil/unittests/Jamfile.jam b/libadhocutil/unittests/Jamfile.jam index 8332544..de5336e 100644 --- a/libadhocutil/unittests/Jamfile.jam +++ b/libadhocutil/unittests/Jamfile.jam @@ -40,3 +40,16 @@ run testBuffer ; +run + testProcessPipes.cpp + : : : + BOOST_TEST_DYN_LINK + ROOT=\"$(me)\" + ..//adhocutil + boost_utf + boost_filesystem + boost_system + : + testProcessPipes + ; + diff --git a/libadhocutil/unittests/testProcessPipes.cpp b/libadhocutil/unittests/testProcessPipes.cpp new file mode 100644 index 0000000..7131e70 --- /dev/null +++ b/libadhocutil/unittests/testProcessPipes.cpp @@ -0,0 +1,23 @@ +#define BOOST_TEST_MODULE ProcessPipes +#include + +#include "processPipes.h" +#include "definedDirs.h" +#include + +BOOST_AUTO_TEST_CASE ( readfind ) +{ + ProcessPipes pp({"/usr/bin/find", RootDir.string(), "-maxdepth", "1"}, false, true, true); + BOOST_REQUIRE_EQUAL(pp.fdIn(), -1); + BOOST_REQUIRE(pp.fdOut() != -1); + BOOST_REQUIRE(pp.fdError() != -1); + char buf[BUFSIZ]; + ssize_t bytes = read(pp.fdOut(), buf, BUFSIZ); + BOOST_REQUIRE_MESSAGE(bytes > 0, "bytes = " << bytes); + buf[bytes] = '\0'; + char * lnf = strstr(buf, "testProcessPipes.cpp"); + BOOST_REQUIRE_MESSAGE(lnf, buf); + int status; + waitpid(pp.pid(), &status, 0); +} + -- cgit v1.2.3