aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-07-18 10:27:26 +0100
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-07-18 10:27:26 +0100
commit33b50256a23f195509ffc3a8ce6fc9d629cd550d (patch)
tree798d1913245d833b9220da2c7a536d9d8da3567c
parente5a2a672f06b681fb6ce55bb2d6d004cf6fe31f2 (diff)
downloadpaludis-33b50256a23f195509ffc3a8ce6fc9d629cd550d.tar.gz
paludis-33b50256a23f195509ffc3a8ce6fc9d629cd550d.tar.xz
Command::with_output_stream
-rw-r--r--paludis/util/system.cc96
-rw-r--r--paludis/util/system.hh36
-rw-r--r--paludis/util/system_TEST.cc15
3 files changed, 144 insertions, 3 deletions
diff --git a/paludis/util/system.cc b/paludis/util/system.cc
index cad7a43..b8c7c7e 100644
--- a/paludis/util/system.cc
+++ b/paludis/util/system.cc
@@ -177,6 +177,9 @@ namespace paludis
std::tr1::function<std::string (const std::string &)> pipe_command_handler;
std::ostream * captured_stdout_stream;
std::ostream * captured_stderr_stream;
+ std::ostream * output_stream;
+ int output_fd;
+ std::string output_fd_env_var;
std::istream * input_stream;
int input_fd;
std::string input_fd_env_var;
@@ -193,6 +196,9 @@ namespace paludis
const std::tr1::function<std::string (const std::string &)> & h = std::tr1::function<std::string (const std::string &)>(),
std::ostream * cs = 0,
std::ostream * ds = 0,
+ std::ostream * os = 0,
+ int osf = -1,
+ const std::string & osfe = "",
std::istream * is = 0,
int isf = -1,
const std::string & isfe = "",
@@ -212,6 +218,9 @@ namespace paludis
pipe_command_handler(h),
captured_stdout_stream(cs),
captured_stderr_stream(ds),
+ output_stream(os),
+ output_fd(osf),
+ output_fd_env_var(osfe),
input_stream(is),
input_fd(isf),
input_fd_env_var(isfe),
@@ -238,7 +247,8 @@ Command::Command(const Command & other) :
other._imp->prefix_discard_blank_output,
other._imp->prefix_blank_lines, other._imp->pipe_command_env_var_prefix,
other._imp->pipe_command_handler, other._imp->captured_stdout_stream,
- other._imp->captured_stderr_stream, other._imp->input_stream, other._imp->input_fd,
+ other._imp->captured_stderr_stream, other._imp->output_stream, other._imp->output_fd,
+ other._imp->output_fd_env_var, other._imp->input_stream, other._imp->input_fd,
other._imp->input_fd_env_var, other._imp->ptys))
{
}
@@ -260,6 +270,9 @@ Command::operator= (const Command & other)
other._imp->pipe_command_handler,
other._imp->captured_stdout_stream,
other._imp->captured_stderr_stream,
+ other._imp->output_stream,
+ other._imp->output_fd,
+ other._imp->output_fd_env_var,
other._imp->input_stream,
other._imp->input_fd,
other._imp->input_fd_env_var,
@@ -319,6 +332,15 @@ Command::with_captured_stderr_stream(std::ostream * const c)
}
Command &
+Command::with_output_stream(std::ostream * const c, int fd, const std::string & e)
+{
+ _imp->output_stream = c;
+ _imp->output_fd = fd;
+ _imp->output_fd_env_var = e;
+ return *this;
+}
+
+Command &
Command::with_input_stream(std::istream * const c, int fd, const std::string & e)
{
_imp->input_stream = c;
@@ -542,7 +564,7 @@ paludis::run_command(const Command & cmd)
<< " " << extras;
std::tr1::shared_ptr<Pipe> internal_command_reader(new Pipe), pipe_command_reader,
- pipe_command_response, input_stream;
+ pipe_command_response, input_stream, output_stream;
std::tr1::shared_ptr<Channel> captured_stdout, captured_stderr;
if (cmd.pipe_command_handler())
{
@@ -553,6 +575,8 @@ paludis::run_command(const Command & cmd)
captured_stdout.reset(cmd.ptys() ? static_cast<Channel *>(new Pty) : new Pipe);
if (cmd.captured_stderr_stream())
captured_stderr.reset(cmd.ptys() ? static_cast<Channel *>(new Pty) : new Pipe);
+ if (cmd.output_stream())
+ output_stream.reset(new Pipe);
if (cmd.input_stream())
{
input_stream.reset(new Pipe);
@@ -564,7 +588,6 @@ paludis::run_command(const Command & cmd)
throw RunCommandError("fcntl F_SETFL failed: " + stringify(strerror(errno)));
}
-
/* Why do we fork twice, rather than install a SIGCHLD handler that writes to a pipe that
* our pselect watches? Simple: Unix is retarded. APUE 12.8 says "Each thread has its own signal
* mask, but the signal disposition is shared by all threads in the process.", which means
@@ -619,6 +642,12 @@ paludis::run_command(const Command & cmd)
captured_stderr->clear_read_fd();
}
+ if (cmd.output_stream())
+ {
+ close(output_stream->read_fd());
+ output_stream->clear_read_fd();
+ }
+
if (cmd.input_stream())
{
close(input_stream->write_fd());
@@ -673,6 +702,22 @@ paludis::run_command(const Command & cmd)
close(stderr_close_fd);
}
+ if (cmd.output_stream())
+ {
+ int cmd_output_fd;
+
+ if (-1 == cmd.output_fd())
+ cmd_output_fd = dup(output_stream->write_fd());
+ else
+ cmd_output_fd = dup2(output_stream->write_fd(), cmd.output_fd());
+
+ if (-1 == cmd_output_fd)
+ throw RunCommandError("output dup2 failed: " + stringify(strerror(errno)));
+
+ if (! cmd.output_fd_env_var().empty())
+ setenv(cmd.output_fd_env_var().c_str(), stringify(cmd_output_fd).c_str(), 1);
+ }
+
if (cmd.input_stream())
{
int cmd_input_fd;
@@ -763,6 +808,14 @@ paludis::run_command(const Command & cmd)
captured_stderr->clear_write_fd();
}
+ if (cmd.output_stream())
+ {
+ close(output_stream->read_fd());
+ output_stream->clear_read_fd();
+ close(output_stream->write_fd());
+ output_stream->clear_write_fd();
+ }
+
if (cmd.input_stream())
{
close(input_stream->read_fd());
@@ -839,6 +892,12 @@ paludis::run_command(const Command & cmd)
captured_stderr->clear_write_fd();
}
+ if (cmd.output_stream())
+ {
+ close(output_stream->write_fd());
+ output_stream->clear_write_fd();
+ }
+
if (cmd.input_stream())
{
close(input_stream->read_fd());
@@ -875,6 +934,12 @@ paludis::run_command(const Command & cmd)
max_fd = std::max(max_fd, captured_stderr->read_fd());
}
+ if (cmd.output_stream() && -1 != output_stream->read_fd())
+ {
+ FD_SET(output_stream->read_fd(), &read_fds);
+ max_fd = std::max(max_fd, output_stream->read_fd());
+ }
+
if (cmd.input_stream() && -1 != input_stream->write_fd())
{
FD_SET(input_stream->write_fd(), &write_fds);
@@ -924,6 +989,13 @@ paludis::run_command(const Command & cmd)
}
}
+ if (cmd.output_stream() && FD_ISSET(output_stream->read_fd(), &read_fds))
+ {
+ int r;
+ if (((r = read(output_stream->read_fd(), buf, 1024))) > 0)
+ *cmd.output_stream() << std::string(buf, r);
+ }
+
if (cmd.pipe_command_handler() && FD_ISSET(pipe_command_reader->read_fd(), &read_fds))
{
int r;
@@ -1337,6 +1409,24 @@ Command::input_fd_env_var() const
return _imp->input_fd_env_var;
}
+std::ostream *
+Command::output_stream() const
+{
+ return _imp->output_stream;
+}
+
+int
+Command::output_fd() const
+{
+ return _imp->output_fd;
+}
+
+const std::string
+Command::output_fd_env_var() const
+{
+ return _imp->output_fd_env_var;
+}
+
bool
Command::ptys() const
{
diff --git a/paludis/util/system.hh b/paludis/util/system.hh
index 6114834..240daa5 100644
--- a/paludis/util/system.hh
+++ b/paludis/util/system.hh
@@ -209,6 +209,21 @@ namespace paludis
Command & with_captured_stderr_stream(std::ostream * const);
/**
+ * Capture input from a particular FD to a stream.
+ *
+ * May only be called once.
+ *
+ * \param fd Read to this FD. If -1, pick an unused FD.
+ * \param env_var If not empty, put the FD chosen in this env var.
+ *
+ * \since 0.50
+ */
+ Command & with_output_stream(
+ std::ostream * const,
+ int fd,
+ const std::string & env_var);
+
+ /**
* Send the contents of a stream in via a particular FD.
*
* May only be called once.
@@ -327,6 +342,27 @@ namespace paludis
const std::string input_fd_env_var() const;
/**
+ * The input stream, or null.
+ *
+ * \since 0.50
+ */
+ std::ostream * output_stream() const;
+
+ /**
+ * The output FD, if output_stream() not null.
+ *
+ * \since 0.50
+ */
+ int output_fd() const;
+
+ /**
+ * The output FD env var, if output_stream() not null.
+ *
+ * \since 0.50
+ */
+ const std::string output_fd_env_var() const;
+
+ /**
* Uses ptys instead of pipes?
*
* \since 0.38
diff --git a/paludis/util/system_TEST.cc b/paludis/util/system_TEST.cc
index e027ea1..06c619a 100644
--- a/paludis/util/system_TEST.cc
+++ b/paludis/util/system_TEST.cc
@@ -238,6 +238,21 @@ namespace test_cases
}
} test_captured_err;
+ struct CapturedExtraOutTest : TestCase
+ {
+ CapturedExtraOutTest() : TestCase("captured extra output") { }
+
+ void run()
+ {
+ std::stringstream s;
+ TEST_CHECK_EQUAL(run_command(Command("echo hi 1>&$TEST_PIPE_FD").with_output_stream(&s, -1, "TEST_PIPE_FD")), 0);
+ std::string line;
+ TEST_CHECK(std::getline(s, line));
+ TEST_CHECK_EQUAL(line, "hi");
+ TEST_CHECK(! std::getline(s, line));
+ }
+ } test_captured_extra_out;
+
struct CapturedNoExistTest : TestCase
{
CapturedNoExistTest() : TestCase("captured nonexistent command") { }