aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-08-20 19:20:21 +0100
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-08-21 14:58:17 +0100
commitfcd415e70607343e806be7e05e99629969483edd (patch)
tree13a5bd19af757e06b6dea2bf83be2508d139cc02
parent7d902d768782fae65bdc368a9c0d24b81c381e00 (diff)
downloadpaludis-fcd415e70607343e806be7e05e99629969483edd.tar.gz
paludis-fcd415e70607343e806be7e05e99629969483edd.tar.xz
Process::pipe_command_handler
-rw-r--r--paludis/util/files.m42
-rw-r--r--paludis/util/process.cc83
-rw-r--r--paludis/util/process.hh5
-rw-r--r--paludis/util/process_TEST.cc56
-rwxr-xr-xpaludis/util/process_TEST_cleanup.sh9
-rwxr-xr-xpaludis/util/process_TEST_setup.sh60
6 files changed, 214 insertions, 1 deletions
diff --git a/paludis/util/files.m4 b/paludis/util/files.m4
index 11ca69c..5862841 100644
--- a/paludis/util/files.m4
+++ b/paludis/util/files.m4
@@ -62,7 +62,7 @@ add(`output_wrapper', `test', `testscript')
add(`pimp', `hh', `impl')
add(`pipe', `hh', `cc')
add(`pretty_print', `hh', `cc', `test')
-add(`process', `hh', `cc', `fwd', `test')
+add(`process', `hh', `cc', `fwd', `test', `testscript')
add(`pty', `hh', `cc', `test')
add(`random', `hh', `cc', `test')
add(`realpath', `hh', `cc', `test', `testscript')
diff --git a/paludis/util/process.cc b/paludis/util/process.cc
index c2967db..5645208 100644
--- a/paludis/util/process.cc
+++ b/paludis/util/process.cc
@@ -125,6 +125,11 @@ namespace paludis
std::ostream * capture_output_to_fd;
std::unique_ptr<Pipe> capture_output_to_fd_pipe;
+ ProcessPipeCommandFunction pipe_command_handler;
+ std::unique_ptr<Pipe> pipe_command_handler_command_pipe;
+ std::unique_ptr<Pipe> pipe_command_handler_response_pipe;
+ std::string pipe_command_handler_buffer;
+
/* must be last, so the thread gets join()ed before its FDs vanish */
std::unique_ptr<Thread> thread;
@@ -174,6 +179,12 @@ RunningProcessThread::thread_func()
max_fd = std::max(max_fd, capture_output_to_fd_pipe->read_fd());
}
+ if (pipe_command_handler)
+ {
+ FD_SET(pipe_command_handler_command_pipe->read_fd(), &read_fds);
+ max_fd = std::max(max_fd, pipe_command_handler_command_pipe->read_fd());
+ }
+
int retval(::pselect(max_fd + 1, &read_fds, &write_fds, 0, 0, 0));
if (-1 == retval)
throw ProcessError("pselect() failed");
@@ -211,6 +222,43 @@ RunningProcessThread::thread_func()
done_anything = true;
}
+ if (pipe_command_handler && FD_ISSET(pipe_command_handler_command_pipe->read_fd(), &read_fds))
+ {
+ int n(::read(pipe_command_handler_command_pipe->read_fd(), &buf, sizeof(buf)));
+ if (-1 == n)
+ throw ProcessError("read() pipe_command_handler_command_pipe read_fd failed");
+ else if (0 != n)
+ pipe_command_handler_buffer.append(buf, n);
+ done_anything = true;
+ }
+
+ while (! pipe_command_handler_buffer.empty())
+ {
+ std::string::size_type n_p(pipe_command_handler_buffer.find('\0'));
+ if (std::string::npos == n_p)
+ break;
+
+ std::string op(pipe_command_handler_buffer.substr(0, n_p));
+ pipe_command_handler_buffer.erase(0, n_p + 1);
+
+ std::string response(pipe_command_handler(op));
+
+ ssize_t n(0);
+ while (! response.empty())
+ {
+ n = write(pipe_command_handler_response_pipe->write_fd(), response.c_str(), response.length());
+ if (-1 == n)
+ throw ProcessError("write() pipe_command_handler_response_pipe write_fd failed");
+ else
+ response.erase(0, n);
+ }
+
+ char c(0);
+ n = write(pipe_command_handler_response_pipe->write_fd(), &c, 1);
+ if (1 != n)
+ throw ProcessError("write() pipe_command_handler_response_pipe write_fd failed");
+ }
+
if (done_anything)
continue;
@@ -247,6 +295,8 @@ namespace paludis
std::ostream * capture_output_to_fd_stream;
int capture_output_to_fd_fd;
std::string capture_output_to_fd_env_var;
+ ProcessPipeCommandFunction pipe_command_handler;
+ std::string pipe_command_handler_env_var;
int set_stdin_fd;
std::map<std::string, std::string> setenvs;
@@ -315,6 +365,13 @@ Process::run()
thread->capture_output_to_fd = _imp->capture_output_to_fd_stream;
thread->capture_output_to_fd_pipe.reset(new Pipe(true));
}
+
+ if (_imp->pipe_command_handler)
+ {
+ thread->pipe_command_handler = _imp->pipe_command_handler;
+ thread->pipe_command_handler_command_pipe.reset(new Pipe(true));
+ thread->pipe_command_handler_response_pipe.reset(new Pipe(true));
+ }
}
pid_t child(fork());
@@ -350,6 +407,23 @@ Process::run()
::setenv(_imp->capture_output_to_fd_env_var.c_str(), stringify(fd).c_str(), 1);
}
+ if (thread && thread->pipe_command_handler)
+ {
+ int read_fd(::dup(thread->pipe_command_handler_response_pipe->read_fd()));
+ if (-1 == read_fd)
+ throw ProcessError("dup failed");
+
+ int write_fd(::dup(thread->pipe_command_handler_command_pipe->write_fd()));
+ if (-1 == write_fd)
+ throw ProcessError("dup failed");
+
+ if (! _imp->pipe_command_handler_env_var.empty())
+ {
+ ::setenv((_imp->pipe_command_handler_env_var + "_READ_FD").c_str(), stringify(read_fd).c_str(), 1);
+ ::setenv((_imp->pipe_command_handler_env_var + "_WRITE_FD").c_str(), stringify(write_fd).c_str(), 1);
+ }
+ }
+
if (-1 != _imp->set_stdin_fd)
{
if (-1 == ::dup2(_imp->set_stdin_fd, STDIN_FILENO))
@@ -436,6 +510,15 @@ Process::set_stdin_fd(int f)
}
Process &
+Process::pipe_command_handler(const std::string & v, const ProcessPipeCommandFunction & f)
+{
+ _imp->need_thread = true;
+ _imp->pipe_command_handler = f;
+ _imp->pipe_command_handler_env_var = v;
+ return *this;
+}
+
+Process &
Process::setenv(const std::string & a, const std::string & b)
{
_imp->setenvs.insert(std::make_pair(a, b)).first->second = b;
diff --git a/paludis/util/process.hh b/paludis/util/process.hh
index 8a3fe3f..79d9577 100644
--- a/paludis/util/process.hh
+++ b/paludis/util/process.hh
@@ -29,6 +29,7 @@
#include <string>
#include <iosfwd>
#include <memory>
+#include <functional>
#include <initializer_list>
#include <sys/types.h>
@@ -36,6 +37,8 @@
namespace paludis
{
+ typedef std::function<std::string (const std::string &)> ProcessPipeCommandFunction;
+
class PALUDIS_VISIBLE ProcessError :
public Exception
{
@@ -79,6 +82,8 @@ namespace paludis
Process & capture_stderr(std::ostream &);
Process & capture_output_to_fd(std::ostream &, int fd_or_minus_one, const std::string & env_var_with_fd);
Process & set_stdin_fd(int);
+ Process & pipe_command_handler(const std::string &, const ProcessPipeCommandFunction &);
+
Process & setenv(const std::string &, const std::string &);
Process & chdir(const FSEntry &);
Process & use_ptys();
diff --git a/paludis/util/process_TEST.cc b/paludis/util/process_TEST.cc
index b0abb15..71f2170 100644
--- a/paludis/util/process_TEST.cc
+++ b/paludis/util/process_TEST.cc
@@ -30,6 +30,23 @@
using namespace paludis;
using namespace test;
+namespace
+{
+ std::string response_handler(const std::string & s)
+ {
+ if (s == "ONE")
+ return "1";
+ else if (s == "TWO")
+ return "2";
+ else if (s == "THREE")
+ return "3";
+ else if (s == "FOUR")
+ return "4";
+ else
+ return "9";
+ }
+}
+
namespace test_cases
{
struct TrueTest : TestCase
@@ -293,5 +310,44 @@ namespace test_cases
TEST_CHECK_EQUAL(stdout_stream.str(), "sdrawkcab\n");
}
} test_stdin_fd;
+
+ struct PipeCommandTest : TestCase
+ {
+ PipeCommandTest() : TestCase("pipe command") { }
+
+ void run()
+ {
+ {
+ Process one_two_process(ProcessCommand({ "bash", "process_TEST_dir/pipe_test.bash", "ONE", "TWO" }));
+ one_two_process.pipe_command_handler("PALUDIS_PIPE_COMMAND", &response_handler);
+ TEST_CHECK_EQUAL(one_two_process.run().wait(), 12);
+ }
+
+ {
+ Process three_four_process(ProcessCommand({ "bash", "process_TEST_dir/pipe_test.bash", "THREE", "FOUR" }));
+ three_four_process.pipe_command_handler("PALUDIS_PIPE_COMMAND", &response_handler);
+ TEST_CHECK_EQUAL(three_four_process.run().wait(), 34);
+ }
+ }
+ } test_pipe_command;
+
+ struct CapturedPipeCommandTest : TestCase
+ {
+ CapturedPipeCommandTest() : TestCase("captured pipe command") { }
+
+ void run()
+ {
+ std::stringstream stdout_stream;
+ Process one_two_three_process(ProcessCommand({ "bash", "process_TEST_dir/captured_pipe_test.bash", "ONE", "TWO", "THREE" }));
+ one_two_three_process.capture_stdout(stdout_stream);
+ one_two_three_process.pipe_command_handler("PALUDIS_PIPE_COMMAND", &response_handler);
+ TEST_CHECK_EQUAL(one_two_three_process.run().wait(), 13);
+
+ std::string line;
+ TEST_CHECK(std::getline(stdout_stream, line));
+ TEST_CHECK_EQUAL(line, "2");
+ TEST_CHECK(! std::getline(stdout_stream, line));
+ }
+ } test_captured_pipe_command;
}
diff --git a/paludis/util/process_TEST_cleanup.sh b/paludis/util/process_TEST_cleanup.sh
new file mode 100755
index 0000000..9268a18
--- /dev/null
+++ b/paludis/util/process_TEST_cleanup.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d process_TEST_dir ] ; then
+ rm -fr process_TEST_dir
+else
+ true
+fi
+
diff --git a/paludis/util/process_TEST_setup.sh b/paludis/util/process_TEST_setup.sh
new file mode 100755
index 0000000..8749b0b
--- /dev/null
+++ b/paludis/util/process_TEST_setup.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir process_TEST_dir || exit 2
+cd process_TEST_dir || exit 3
+
+cat <<'END' > pipe_test.bash
+#!/usr/bin/env bash
+
+echo "$1" | tr "\n" "\0" 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+response1=
+while true ; do
+ read -n 1 -u $PALUDIS_PIPE_COMMAND_READ_FD c
+ [[ "$c" == $'\0' ]] && break
+ response1="${response1}${c}"
+done
+
+echo "$2" | tr "\n" "\0" 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+response2=
+while true ; do
+ read -n 1 -u $PALUDIS_PIPE_COMMAND_READ_FD c
+ [[ "$c" == $'\0' ]] && break
+ response2="${response2}${c}"
+done
+
+exit $response1$response2
+END
+
+cat <<'END' > captured_pipe_test.bash
+#!/usr/bin/env bash
+
+echo "$1" | tr "\n" "\0" 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+response1=
+while true ; do
+ read -n 1 -u $PALUDIS_PIPE_COMMAND_READ_FD c
+ [[ "$c" == $'\0' ]] && break
+ response1="${response1}${c}"
+done
+
+echo "$2" | tr "\n" "\0" 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+response2=
+while true ; do
+ read -n 1 -u $PALUDIS_PIPE_COMMAND_READ_FD c
+ [[ "$c" == $'\0' ]] && break
+ response2="${response2}${c}"
+done
+
+echo "$3" | tr "\n" "\0" 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+response3=
+while true ; do
+ read -n 1 -u $PALUDIS_PIPE_COMMAND_READ_FD c
+ [[ "$c" == $'\0' ]] && break
+ response3="${response3}${c}"
+done
+
+echo $response2
+
+exit $response1$response3
+END
+