summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Leigh <rleigh@debian.org>2009-04-10 14:34:22 +0100
committerRoger Leigh <rleigh@debian.org>2009-05-03 17:35:38 +0100
commit54c529d3bc4b6a922c0e62f36e960cfe66fc7adb (patch)
tree69abf0aa0ab9e4b3f79d2b7981ff17cdad492ae4
parentf8b431d73a3621f4b0f07491f00557cc75744aaf (diff)
downloadschroot-54c529d3bc4b6a922c0e62f36e960cfe66fc7adb.tar.gz
[sbuild] run_parts: Log script output
Instead of leaving script stdout and stderr hooked up to our stdout and stderr, attach them to pipes and read output via poll and then log as info and error messages, respectively to stderr. This has the nice benefit of never writing to stdout when --verbose is used, which could interfere with user programs reading stdout.
-rw-r--r--sbuild/sbuild-run-parts.cc176
-rw-r--r--sbuild/sbuild-run-parts.h8
2 files changed, 150 insertions, 34 deletions
diff --git a/sbuild/sbuild-run-parts.cc b/sbuild/sbuild-run-parts.cc
index fb1696b9..86f251a8 100644
--- a/sbuild/sbuild-run-parts.cc
+++ b/sbuild/sbuild-run-parts.cc
@@ -1,4 +1,4 @@
-/* Copyright © 2005-2007 Roger Leigh <rleigh@debian.org>
+/* Copyright © 2005-2009 Roger Leigh <rleigh@debian.org>
*
* schroot is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@
#include <cerrno>
+#include <poll.h>
#include <sys/wait.h>
#include <syslog.h>
@@ -47,7 +48,11 @@ namespace
emap(run_parts::CHILD_FORK, N_("Failed to fork child")),
emap(run_parts::CHILD_WAIT, N_("Wait for child failed")),
// TRANSLATORS: %1% = command name
- emap(run_parts::EXEC, N_("Failed to execute '%1%'"))
+ emap(run_parts::EXEC, N_("Failed to execute '%1%'")),
+ emap(run_parts::PIPE, N_("Failed to create pipe")),
+ emap(run_parts::DUP, N_("Failed to duplicate file descriptor")),
+ emap(run_parts::POLL, N_("Failed to poll file descriptor")),
+ emap(run_parts::READ, N_("Failed to read file descriptor"))
};
}
@@ -166,48 +171,155 @@ run_parts::run_child (std::string const& file,
string_list const& command,
environment const& env)
{
+ int stdout_pipe[2];
+ int stderr_pipe[2];
int exit_status = 0;
pid_t pid;
- if ((pid = fork()) == -1)
+ try
{
- throw error(CHILD_FORK, strerror(errno));
- }
- else if (pid == 0)
- {
- try
+ if (pipe(stdout_pipe) < 0)
+ throw error(PIPE, strerror(errno));
+ if (pipe(stderr_pipe) < 0)
+ throw error(PIPE, strerror(errno));
+
+ if ((pid = fork()) == -1)
{
- log_debug(DEBUG_INFO) << "run_parts: executing "
- << string_list_to_string(command, ", ")
- << std::endl;
- if (this->verbose)
- // TRANSLATORS: %1% = command
- log_info() << format(_("Executing '%1%'"))
- % string_list_to_string(command, " ")
- << std::endl;
- ::umask(this->umask);
-
- // Don't leak syslog file descriptor to child processes.
- closelog();
-
- exec(this->directory + '/' + file, command, env);
- error e(file, EXEC, strerror(errno));
- log_exception_error(e);
+ throw error(CHILD_FORK, strerror(errno));
}
- catch (std::exception const& e)
+ else if (pid == 0)
{
- sbuild::log_exception_error(e);
+ try
+ {
+ log_debug(DEBUG_INFO) << "run_parts: executing "
+ << string_list_to_string(command, ", ")
+ << std::endl;
+ if (this->verbose)
+ // TRANSLATORS: %1% = command
+ log_info() << format(_("Executing '%1%'"))
+ % string_list_to_string(command, " ")
+ << std::endl;
+ ::umask(this->umask);
+
+ // Don't leak syslog file descriptor to child processes.
+ closelog();
+
+ // Set up pipes for stdout and stderr
+ if (dup2(stdout_pipe[1], STDOUT_FILENO) < 0)
+ throw error(DUP, strerror(errno));
+ if (dup2(stderr_pipe[1], STDERR_FILENO) < 0)
+ throw error(DUP, strerror(errno));
+
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+ close(stderr_pipe[0]);
+ close(stderr_pipe[1]);
+
+ exec(this->directory + '/' + file, command, env);
+ error e(file, EXEC, strerror(errno));
+ log_exception_error(e);
+ }
+ catch (std::exception const& e)
+ {
+ sbuild::log_exception_error(e);
+ }
+ catch (...)
+ {
+ sbuild::log_error()
+ << _("An unknown exception occurred") << std::endl;
+ }
+ _exit(EXIT_FAILURE);
}
- catch (...)
+
+ // Log stdout and stderr.
+ close(stdout_pipe[1]);
+ close(stderr_pipe[1]);
+
+ struct pollfd pollfds[2];
+ pollfds[0].fd = stdout_pipe[0];
+ pollfds[0].events = POLLIN;
+ pollfds[0].revents = 0;
+ pollfds[1].fd = stderr_pipe[0];
+ pollfds[1].events = POLLIN;
+ pollfds[1].revents = 0;
+
+ char buffer[BUFSIZ];
+
+ std::string stdout_buf;
+ std::string stderr_buf;
+
+ while (1)
{
- sbuild::log_error()
- << _("An unknown exception occurred") << std::endl;
+ int status;
+ if ((status = poll(pollfds, 2, -1)) < 0)
+ throw error(POLL, strerror(errno));
+
+ int outdata = 0;
+ int errdata = 0;
+ if (pollfds[0].revents | POLLIN)
+ {
+ if ((outdata = read(pollfds[0].fd, buffer, BUFSIZ)) < 0)
+ throw error(READ, strerror(errno));
+
+ if (outdata)
+ stdout_buf += std::string(&buffer[0], outdata);
+ }
+
+ if (!stdout_buf.empty())
+ {
+ string_list lines = split_string(stdout_buf, "\n");
+
+ for (string_list::const_iterator pos = lines.begin();
+ pos != lines.end();
+ ++pos)
+ {
+ if (pos + 1 != lines.end() || outdata == 0)
+ log_info() << file << ": " << *pos << '\n';
+ else // Save possibly incompete line
+ stdout_buf = *pos;
+ }
+ }
+
+ if (pollfds[1].revents | POLLIN)
+ {
+ int errdata;
+ if ((errdata = read(pollfds[1].fd, buffer, BUFSIZ)) < 0)
+ throw error(READ, strerror(errno));
+
+ if (errdata)
+ stderr_buf += std::string(&buffer[0], errdata);
+ }
+
+ if (!stderr_buf.empty())
+ {
+ string_list lines = split_string(stderr_buf, "\n");
+
+ for (string_list::const_iterator pos = lines.begin();
+ pos != lines.end();
+ ++pos)
+ {
+ if (pos + 1 != lines.end() || errdata == 0)
+ log_error() << file << ": " << *pos << '\n';
+ else // Save possibly incompete line
+ stderr_buf = *pos;
+ }
+ }
+
+ if (outdata == 0 && errdata == 0) // pipes closed
+ break;
}
- _exit(EXIT_FAILURE);
+
+ close(stdout_pipe[0]);
+ close(stderr_pipe[0]);
+ wait_for_child(pid, exit_status);
}
- else
+ catch (error const& e)
{
- wait_for_child(pid, exit_status);
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+ close(stderr_pipe[0]);
+ close(stderr_pipe[1]);
+ throw;
}
if (exit_status)
diff --git a/sbuild/sbuild-run-parts.h b/sbuild/sbuild-run-parts.h
index 125d9864..7b5b6e62 100644
--- a/sbuild/sbuild-run-parts.h
+++ b/sbuild/sbuild-run-parts.h
@@ -1,4 +1,4 @@
-/* Copyright © 2005-2007 Roger Leigh <rleigh@debian.org>
+/* Copyright © 2005-2009 Roger Leigh <rleigh@debian.org>
*
* schroot is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
@@ -43,7 +43,11 @@ namespace sbuild
{
CHILD_FORK, ///< Failed to fork child.
CHILD_WAIT, ///< Wait for child failed.
- EXEC ///< Failed to execute.
+ EXEC, ///< Failed to execute.
+ PIPE, ///< Failed to create pipe.
+ DUP, ///< Failed to duplicate file descriptor.
+ POLL, ///< Failed to poll file descriptor.
+ READ ///< Failed to read file descriptor.
};
/// Exception type.