aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2007-03-30 18:46:13 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2007-03-30 18:46:13 +0000
commitafc95e1d3d45d0b66213cc93a404dd11be83544c (patch)
treeccd43e94f69ef68f6998669bc95594f4c3af0896
parent4de95921e4018a8b8ee5bc8c2f946586e6eceecd (diff)
downloadpaludis-afc95e1d3d45d0b66213cc93a404dd11be83544c.tar.gz
paludis-afc95e1d3d45d0b66213cc93a404dd11be83544c.tar.xz
Let programs run with prefixed output
-rw-r--r--paludis/util/Makefile.am.m44
-rw-r--r--paludis/util/output_wrapper.cc220
-rw-r--r--paludis/util/system.cc59
-rw-r--r--paludis/util/system.hh4
4 files changed, 279 insertions, 8 deletions
diff --git a/paludis/util/Makefile.am.m4 b/paludis/util/Makefile.am.m4
index de0adce..85e4a7a 100644
--- a/paludis/util/Makefile.am.m4
+++ b/paludis/util/Makefile.am.m4
@@ -76,6 +76,10 @@ Makefile.am : Makefile.am.m4 files.m4
util.hh : util.hh.m4 files.m4
$(top_srcdir)/misc/do_m4.bash util.hh
+libexecprogdir = $(libexecdir)/paludis/utils
+libexecprog_PROGRAMS = outputwrapper
+outputwrapper_SOURCES = output_wrapper.cc
+
changequote(`<', `>')
built-sources : $(BUILT_SOURCES)
for s in `echo $(SUBDIRS) | tr -d .` ; do $(MAKE) -C $$s built-sources || exit 1 ; done
diff --git a/paludis/util/output_wrapper.cc b/paludis/util/output_wrapper.cc
new file mode 100644
index 0000000..b9cfef9
--- /dev/null
+++ b/paludis/util/output_wrapper.cc
@@ -0,0 +1,220 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string>
+#include <iostream>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+
+int
+main(int argc, char *argv[])
+{
+ int argi(1);
+ std::string stdout_prefix, stderr_prefix;
+
+ for ( ; argi < argc ; ++argi)
+ {
+ std::string s(argv[argi]);
+ if (s == "--")
+ {
+ ++argi;
+ break;
+ }
+ else if (s == "--stdout-prefix")
+ {
+ if (++argi >= argc)
+ {
+ std::cerr << argv[0] << ": no argument for --stdout-prefix" << std::endl;
+ return EXIT_FAILURE;
+ }
+ stdout_prefix = argv[argi];
+ }
+ else if (s == "--stderr-prefix")
+ {
+ if (++argi >= argc)
+ {
+ std::cerr << argv[0] << ": no argument for --stderr-prefix" << std::endl;
+ return EXIT_FAILURE;
+ }
+ stderr_prefix = argv[argi];
+ }
+ else
+ {
+ std::cerr << argv[0] << ": bad argument '" << s << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argi >= argc)
+ {
+ std::cerr << argv[0] << ": no -- found" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ int stdout_pipes[2], stderr_pipes[2];
+
+ if (0 != pipe(stdout_pipes))
+ {
+ std::cerr << argv[0] << ": pipe failed: " << strerror(errno) << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (0 != pipe(stderr_pipes))
+ {
+ std::cerr << argv[0] << ": pipe failed: " << strerror(errno) << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ pid_t pid(fork());
+
+ if (0 == pid)
+ {
+ if (-1 == dup2(stdout_pipes[1], 1))
+ {
+ std::cerr << argv[0] << ": dup2 failed: " << strerror(errno) << std::endl;
+ return EXIT_FAILURE;
+ }
+ close(stdout_pipes[0]);
+
+ if (-1 == dup2(stderr_pipes[1], 2))
+ {
+ std::cerr << argv[0] << ": dup2 failed: " << strerror(errno) << std::endl;
+ return EXIT_FAILURE;
+ }
+ close(stderr_pipes[0]);
+
+ execvp(argv[argi], &argv[argi]);
+ std::cerr << argv[0] << ": execvp failed: " << strerror(errno) << std::endl;
+ return EXIT_FAILURE;
+ }
+ else if (-1 == pid)
+ {
+ std::cerr << argv[0] << ": fork failed: " << strerror(errno) << std::endl;
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ close(stdout_pipes[1]);
+ close(stderr_pipes[1]);
+ bool stdout_done(false), stdout_prefix_shown(false), stdout_had_interesting_char(false),
+ stderr_done(false), stderr_prefix_shown(false), stderr_had_interesting_char(false);
+ while ((! stdout_done) || (! stderr_done))
+ {
+ fd_set fds;
+ int ret;
+
+ FD_ZERO(&fds);
+ FD_SET(stdout_pipes[0], &fds);
+ FD_SET(stderr_pipes[0], &fds);
+
+ ret = select(std::max(stdout_pipes[0], stderr_pipes[0]) + 1, &fds, 0, 0, 0);
+ if (-1 == ret)
+ {
+ std::cerr << argv[0] << ": select failed: " << strerror(errno) << std::endl;
+ return EXIT_FAILURE;
+ }
+ else if (ret)
+ {
+ char buf[1024];
+ if (FD_ISSET(stdout_pipes[0], &fds))
+ {
+ int c;
+ if (0 >= ((c = read(stdout_pipes[0], buf, 1024))))
+ stdout_done = true;
+ else
+ {
+ std::string to_write(buf, c);
+
+ for (std::string::size_type p(0) ; p < to_write.length() ; ++p)
+ {
+ if (to_write.at(p) != '\n')
+ stdout_had_interesting_char = true;
+
+ if (! stdout_prefix_shown)
+ {
+ if (stdout_had_interesting_char)
+ std::cout << stdout_prefix;
+ stdout_prefix_shown = true;
+ }
+
+ if (to_write.at(p) == '\n')
+ {
+ stdout_had_interesting_char = false;
+ stdout_prefix_shown = false;
+ }
+
+ std::cout << to_write.at(p);
+ }
+
+ std::cout << std::flush;
+ }
+ }
+
+ if (FD_ISSET(stderr_pipes[0], &fds))
+ {
+ int c;
+ if (0 >= ((c = read(stderr_pipes[0], buf, 1024))))
+ stderr_done = true;
+ else
+ {
+ std::string to_write(buf, c);
+
+ for (std::string::size_type p(0) ; p < to_write.length() ; ++p)
+ {
+ if (to_write.at(p) != '\n')
+ stderr_had_interesting_char = true;
+
+ if (! stderr_prefix_shown)
+ {
+ if (stderr_had_interesting_char)
+ std::cerr << stdout_prefix;
+ stderr_prefix_shown = true;
+ }
+
+ if (to_write.at(p) == '\n')
+ {
+ stderr_had_interesting_char = false;
+ stderr_prefix_shown = false;
+ }
+
+ std::cerr << to_write.at(p);
+ }
+
+ std::cerr << std::flush;
+ }
+ }
+ }
+ else
+ break;
+ }
+
+ int status(-1);
+ if (-1 == wait(&status))
+ {
+ std::cerr << argv[0] << ": wait failed: " << strerror(errno) << std::endl;
+ return EXIT_FAILURE;
+ }
+ return WEXITSTATUS(status);
+ }
+}
+
diff --git a/paludis/util/system.cc b/paludis/util/system.cc
index 8223d3e..546dd00 100644
--- a/paludis/util/system.cc
+++ b/paludis/util/system.cc
@@ -143,17 +143,23 @@ namespace paludis
bool echo_to_stderr;
std::tr1::shared_ptr<uid_t> uid;
std::tr1::shared_ptr<gid_t> gid;
+ std::string stdout_prefix;
+ std::string stderr_prefix;
Implementation(const std::string & c,
const std::map<std::string, std::string> & s = (std::map<std::string, std::string>()),
- const std::string & d = "", bool e = false, std::tr1::shared_ptr<uid_t> u = std::tr1::shared_ptr<uid_t>(),
- std::tr1::shared_ptr<gid_t> g = std::tr1::shared_ptr<gid_t>()) :
+ const std::string & d = "", bool e = false,
+ std::tr1::shared_ptr<uid_t> u = std::tr1::shared_ptr<uid_t>(),
+ std::tr1::shared_ptr<gid_t> g = std::tr1::shared_ptr<gid_t>(),
+ const std::string & p = "", const std::string & q = "") :
command(c),
setenv_values(s),
chdir(d),
echo_to_stderr(e),
uid(u),
- gid(g)
+ gid(g),
+ stdout_prefix(p),
+ stderr_prefix(q)
{
}
};
@@ -172,7 +178,7 @@ Command::Command(const char * const s) :
Command::Command(const Command & other) :
PrivateImplementationPattern<Command>(new Implementation<Command>(other._imp->command,
other._imp->setenv_values, other._imp->chdir, other._imp->echo_to_stderr,
- other._imp->uid, other._imp->gid))
+ other._imp->uid, other._imp->gid, other._imp->stdout_prefix, other._imp->stderr_prefix))
{
}
@@ -182,7 +188,11 @@ Command::operator= (const Command & other)
if (this != &other)
{
_imp.reset(new Implementation<Command>(other._imp->command, other._imp->setenv_values,
- other._imp->chdir, other._imp->echo_to_stderr));
+ other._imp->chdir, other._imp->echo_to_stderr,
+ std::tr1::shared_ptr<uid_t>(),
+ std::tr1::shared_ptr<gid_t>(),
+ other._imp->stdout_prefix,
+ other._imp->stderr_prefix));
if (other.uid() && other.gid())
with_uid_gid(*other.uid(), *other.gid());
}
@@ -308,11 +318,18 @@ paludis::run_command(const Command & cmd)
extras.append(" [setuid " + stringify(*cmd.uid()) + "]");
}
+ std::string command(cmd.command());
+
+ if ((! cmd.stdout_prefix().empty()) || (! cmd.stderr_prefix().empty()))
+ command = LIBEXECDIR "/paludis/utils/outputwrapper --stdout-prefix '"
+ + cmd.stdout_prefix() + "' --stderr-prefix '" + cmd.stderr_prefix() + "' -- "
+ + command;
+
cmd.echo_to_stderr();
- Log::get_instance()->message(ll_debug, lc_no_context, "execl /bin/sh -c " + cmd.command()
+ Log::get_instance()->message(ll_debug, lc_no_context, "execl /bin/sh -c " + command
+ " " + extras);
- execl("/bin/sh", "sh", "-c", cmd.command().c_str(), static_cast<char *>(0));
- throw RunCommandError("execl /bin/sh -c '" + cmd.command() + "' failed:"
+ execl("/bin/sh", "sh", "-c", command.c_str(), static_cast<char *>(0));
+ throw RunCommandError("execl /bin/sh -c '" + command + "' failed:"
+ stringify(strerror(errno)));
}
else if (-1 == child)
@@ -368,3 +385,29 @@ Command::with_echo_to_stderr()
return *this;
}
+Command &
+Command::with_stdout_prefix(const std::string & s)
+{
+ _imp->stdout_prefix = s;
+ return *this;
+}
+
+Command &
+Command::with_stderr_prefix(const std::string & s)
+{
+ _imp->stderr_prefix = s;
+ return *this;
+}
+
+std::string
+Command::stdout_prefix() const
+{
+ return _imp->stdout_prefix;
+}
+
+std::string
+Command::stderr_prefix() const
+{
+ return _imp->stderr_prefix;
+}
+
diff --git a/paludis/util/system.hh b/paludis/util/system.hh
index b8de54f..7cb10ba 100644
--- a/paludis/util/system.hh
+++ b/paludis/util/system.hh
@@ -110,12 +110,16 @@ namespace paludis
Command & with_sandbox();
Command & with_echo_to_stderr();
Command & with_uid_gid(const uid_t, const gid_t);
+ Command & with_stdout_prefix(const std::string &);
+ Command & with_stderr_prefix(const std::string &);
std::string command() const;
std::string chdir() const;
void echo_to_stderr() const;
std::tr1::shared_ptr<const uid_t> uid() const;
std::tr1::shared_ptr<const gid_t> gid() const;
+ std::string stdout_prefix() const;
+ std::string stderr_prefix() const;
typedef libwrapiter::ForwardIterator<Command, const std::pair<const std::string, std::string> > Iterator;
Iterator begin_setenvs() const;