aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2008-08-23 00:13:33 +0100
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2008-08-23 00:13:33 +0100
commit220c57d80c1f9aa85d19283818add58960ef6c6c (patch)
treed5e06153c3fea637b348a94e2402bc3381d363a9
parent729bc1a815c1abba572213e885d5d654b09b35b4 (diff)
downloadpaludis-220c57d80c1f9aa85d19283818add58960ef6c6c.tar.gz
paludis-220c57d80c1f9aa85d19283818add58960ef6c6c.tar.xz
Allow capturing of stderr
-rw-r--r--paludis/util/system.cc75
-rw-r--r--paludis/util/system.hh14
-rw-r--r--paludis/util/system_TEST.cc15
3 files changed, 98 insertions, 6 deletions
diff --git a/paludis/util/system.cc b/paludis/util/system.cc
index fb62eed..a0e7165 100644
--- a/paludis/util/system.cc
+++ b/paludis/util/system.cc
@@ -156,6 +156,7 @@ namespace paludis
bool prefix_blank_lines;
std::tr1::function<std::string (const std::string &)> pipe_command_handler;
std::ostream * captured_stdout_stream;
+ std::ostream * captured_stderr_stream;
Implementation(const std::string & c,
const std::map<std::string, std::string> & s = (std::map<std::string, std::string>()),
@@ -165,7 +166,8 @@ namespace paludis
const std::string & p = "", const std::string & q = "",
const bool b = false, const bool bb = false,
const std::tr1::function<std::string (const std::string &)> & h = std::tr1::function<std::string (const std::string &)>(),
- std::ostream * cs = 0) :
+ std::ostream * cs = 0,
+ std::ostream * ds = 0) :
command(c),
setenv_values(s),
chdir(d),
@@ -177,7 +179,8 @@ namespace paludis
prefix_discard_blank_output(b),
prefix_blank_lines(bb),
pipe_command_handler(h),
- captured_stdout_stream(cs)
+ captured_stdout_stream(cs),
+ captured_stderr_stream(ds)
{
}
};
@@ -198,7 +201,8 @@ Command::Command(const Command & other) :
other._imp->setenv_values, other._imp->chdir, other._imp->echo_to_stderr,
other._imp->uid, other._imp->gid, other._imp->stdout_prefix, other._imp->stderr_prefix,
other._imp->prefix_discard_blank_output,
- other._imp->prefix_blank_lines, other._imp->pipe_command_handler, other._imp->captured_stdout_stream))
+ other._imp->prefix_blank_lines, other._imp->pipe_command_handler, other._imp->captured_stdout_stream,
+ other._imp->captured_stderr_stream))
{
}
@@ -215,7 +219,8 @@ Command::operator= (const Command & other)
other._imp->stderr_prefix,
other._imp->prefix_discard_blank_output,
other._imp->prefix_blank_lines,
- other._imp->pipe_command_handler, other._imp->captured_stdout_stream));
+ other._imp->pipe_command_handler, other._imp->captured_stdout_stream,
+ other._imp->captured_stderr_stream));
if (other.uid() && other.gid())
with_uid_gid(*other.uid(), *other.gid());
}
@@ -257,6 +262,13 @@ Command::with_captured_stdout_stream(std::ostream * const c)
}
Command &
+Command::with_captured_stderr_stream(std::ostream * const c)
+{
+ _imp->captured_stderr_stream = c;
+ return *this;
+}
+
+Command &
Command::with_sandbox()
{
#if HAVE_SANDBOX
@@ -329,7 +341,7 @@ paludis::run_command(const Command & cmd)
Log::get_instance()->message("util.system.execl", ll_debug, lc_no_context) << "execl /bin/sh -c " << command
<< " " << extras;
- std::tr1::shared_ptr<Pipe> internal_command_reader(new Pipe), pipe_command_reader, pipe_command_response, captured_stdout;
+ std::tr1::shared_ptr<Pipe> internal_command_reader(new Pipe), pipe_command_reader, pipe_command_response, captured_stdout, captured_stderr;
if (cmd.pipe_command_handler())
{
pipe_command_reader.reset(new Pipe);
@@ -337,6 +349,8 @@ paludis::run_command(const Command & cmd)
}
if (cmd.captured_stdout_stream())
captured_stdout.reset(new Pipe);
+ if (cmd.captured_stderr_stream())
+ captured_stderr.reset(new Pipe);
/* 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
@@ -395,6 +409,12 @@ paludis::run_command(const Command & cmd)
captured_stdout->clear_read_fd();
}
+ if (cmd.captured_stderr_stream())
+ {
+ close(captured_stderr->read_fd());
+ captured_stderr->clear_read_fd();
+ }
+
close(internal_command_reader->read_fd());
internal_command_reader->clear_read_fd();
close(internal_command_reader->write_fd());
@@ -427,7 +447,12 @@ paludis::run_command(const Command & cmd)
close(stdout_close_fd);
}
- if (-1 != stderr_write_fd)
+ if (cmd.captured_stderr_stream())
+ {
+ if (-1 == dup2(captured_stderr->write_fd(), 2))
+ throw RunCommandError("dup2 failed: " + stringify(strerror(errno)));
+ }
+ else if (-1 != stderr_write_fd)
{
if (-1 == dup2(stderr_write_fd, 2))
throw RunCommandError("dup2 failed: " + stringify(strerror(errno)));
@@ -521,6 +546,14 @@ paludis::run_command(const Command & cmd)
captured_stdout->clear_write_fd();
}
+ if (cmd.captured_stderr_stream())
+ {
+ close(captured_stderr->read_fd());
+ captured_stderr->clear_read_fd();
+ close(captured_stderr->write_fd());
+ captured_stderr->clear_write_fd();
+ }
+
close(internal_command_reader->read_fd());
internal_command_reader->clear_read_fd();
@@ -588,6 +621,12 @@ paludis::run_command(const Command & cmd)
captured_stdout->clear_write_fd();
}
+ if (cmd.captured_stderr_stream())
+ {
+ close(captured_stderr->write_fd());
+ captured_stderr->clear_write_fd();
+ }
+
close(internal_command_reader->write_fd());
internal_command_reader->clear_write_fd();
@@ -610,6 +649,12 @@ paludis::run_command(const Command & cmd)
max_fd = std::max(max_fd, captured_stdout->read_fd());
}
+ if (cmd.captured_stderr_stream())
+ {
+ FD_SET(captured_stderr->read_fd(), &read_fds);
+ max_fd = std::max(max_fd, captured_stderr->read_fd());
+ }
+
FD_SET(internal_command_reader->read_fd(), &read_fds);
max_fd = std::max(max_fd, internal_command_reader->read_fd());
@@ -641,6 +686,18 @@ paludis::run_command(const Command & cmd)
}
}
+ if (cmd.captured_stderr_stream() && FD_ISSET(captured_stderr->read_fd(), &read_fds))
+ {
+ int r;
+ if (((r = read(captured_stderr->read_fd(), buf, 1024))) > 0)
+ {
+ *cmd.captured_stderr_stream() << std::string(buf, r);
+ /* don't look at the other FDs yet to avoid a partial read from being snipped
+ * when capturing output */
+ continue;
+ }
+ }
+
if (cmd.pipe_command_handler() && FD_ISSET(pipe_command_reader->read_fd(), &read_fds))
{
int r;
@@ -836,6 +893,12 @@ Command::captured_stdout_stream() const
return _imp->captured_stdout_stream;
}
+std::ostream *
+Command::captured_stderr_stream() const
+{
+ return _imp->captured_stderr_stream;
+}
+
std::string
paludis::get_user_name(const uid_t u)
{
diff --git a/paludis/util/system.hh b/paludis/util/system.hh
index 76e8cc1..f3bb3b8 100644
--- a/paludis/util/system.hh
+++ b/paludis/util/system.hh
@@ -181,6 +181,13 @@ namespace paludis
*/
Command & with_captured_stdout_stream(std::ostream * const);
+ /**
+ * Specify a stream to which stderr is captured and written.
+ *
+ * \since 0.30
+ */
+ Command & with_captured_stderr_stream(std::ostream * const);
+
///\}
///\name Fetch command execution options
@@ -242,6 +249,13 @@ namespace paludis
*/
std::ostream * captured_stdout_stream() const;
+ /**
+ * The captured stderr stream, or null.
+ *
+ * \since 0.30
+ */
+ std::ostream * captured_stderr_stream() const;
+
///\}
///\name Iterate over our setenvs.
diff --git a/paludis/util/system_TEST.cc b/paludis/util/system_TEST.cc
index b14bba8..982be29 100644
--- a/paludis/util/system_TEST.cc
+++ b/paludis/util/system_TEST.cc
@@ -238,6 +238,21 @@ namespace test_cases
}
} test_captured;
+ struct CapturedErrTest : TestCase
+ {
+ CapturedErrTest() : TestCase("captured stderr") { }
+
+ void run()
+ {
+ std::stringstream s;
+ TEST_CHECK_EQUAL(run_command(Command("echo hi 1>&2").with_captured_stderr_stream(&s)), 0);
+ std::string line;
+ TEST_CHECK(std::getline(s, line));
+ TEST_CHECK_EQUAL(line, "hi");
+ TEST_CHECK(! std::getline(s, line));
+ }
+ } test_captured_err;
+
struct CapturedNoExistTest : TestCase
{
CapturedNoExistTest() : TestCase("captured nonexistent command") { }