diff options
| -rw-r--r-- | libadhocutil/processPipes.cpp | 138 | ||||
| -rw-r--r-- | libadhocutil/processPipes.h | 33 | ||||
| -rw-r--r-- | libadhocutil/unittests/Jamfile.jam | 13 | ||||
| -rw-r--r-- | libadhocutil/unittests/testProcessPipes.cpp | 23 | 
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); +} +  | 
