summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <daniel.goodliffe@pressassociation.com>2015-08-28 00:09:51 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2015-08-28 00:59:13 +0100
commit59e902394af94d57fcd83fb1c70c97768c275567 (patch)
tree90b3edff7cecf05078c5a441e4e8e74922e0d114
parentAdd buffer, intrusivePtrBase (dep) and covering tests (diff)
downloadlibadhocutil-59e902394af94d57fcd83fb1c70c97768c275567.tar.bz2
libadhocutil-59e902394af94d57fcd83fb1c70c97768c275567.tar.xz
libadhocutil-59e902394af94d57fcd83fb1c70c97768c275567.zip
Add ProcessPipes as a container for a child process with FDs for stdin, out and err
-rw-r--r--libadhocutil/processPipes.cpp138
-rw-r--r--libadhocutil/processPipes.h33
-rw-r--r--libadhocutil/unittests/Jamfile.jam13
-rw-r--r--libadhocutil/unittests/testProcessPipes.cpp23
4 files changed, 207 insertions, 0 deletions
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 <unistd.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <stdexcept>
+
+ProcessPipes::ProcessPipes(const std::vector<std::string> & 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<struct pollfd> 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 <vector>
+#include <string>
+#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<std::string> & 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
+ : : :
+ <define>BOOST_TEST_DYN_LINK
+ <define>ROOT=\"$(me)\"
+ <library>..//adhocutil
+ <library>boost_utf
+ <library>boost_filesystem
+ <library>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 <boost/test/unit_test.hpp>
+
+#include "processPipes.h"
+#include "definedDirs.h"
+#include <sys/wait.h>
+
+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);
+}
+