aboutsummaryrefslogtreecommitdiff
path: root/paludis
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-01-05 15:42:55 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-01-05 18:42:59 +0000
commit1d7fa3dd097834ee102a741b245b669cb3387661 (patch)
tree621f05a44d6d02ff5bf1bdf92b046422cd09dbec /paludis
parent9657d3023e92d82de18ad8d92af0cc12637c72ef (diff)
downloadpaludis-1d7fa3dd097834ee102a741b245b669cb3387661.tar.gz
paludis-1d7fa3dd097834ee102a741b245b669cb3387661.tar.xz
Output via IPC
Diffstat (limited to 'paludis')
-rw-r--r--paludis/Makefile.am.m42
-rw-r--r--paludis/files.m41
-rw-r--r--paludis/ipc_output_manager-fwd.hh32
-rw-r--r--paludis/ipc_output_manager.cc405
-rw-r--r--paludis/ipc_output_manager.hh103
5 files changed, 542 insertions, 1 deletions
diff --git a/paludis/Makefile.am.m4 b/paludis/Makefile.am.m4
index e352c7ad8..6f0cc456d 100644
--- a/paludis/Makefile.am.m4
+++ b/paludis/Makefile.am.m4
@@ -64,7 +64,7 @@ ifelse(`$2', `testscript', `addtestscript(`$1')', `')')dnl
define(`add', `addthis(`$1',`$2')addthis(`$1',`$3')addthis(`$1',`$4')dnl
addthis(`$1',`$5')addthis(`$1',`$6')addthis(`$1',`$7')addthis(`$1',`$8')')dnl
-AM_CXXFLAGS = -I$(top_srcdir) @PALUDIS_CXXFLAGS@ @PALUDIS_CXXFLAGS_VISIBILITY@
+AM_CXXFLAGS = -I$(top_srcdir) @PALUDIS_CXXFLAGS@ @PALUDIS_CXXFLAGS_VISIBILITY@ @PALUDIS_CXXFLAGS_NO_WOLD_STYLE_CAST@
include(`paludis/files.m4')
diff --git a/paludis/files.m4 b/paludis/files.m4
index 4c346e9ff..62f2d1c82 100644
--- a/paludis/files.m4
+++ b/paludis/files.m4
@@ -48,6 +48,7 @@ add(`handled_information', `hh', `fwd', `cc')
add(`hook', `hh', `cc', `fwd', `se')
add(`hooker', `hh', `cc', `test', `testscript')
add(`install_task', `hh', `cc', `se')
+add(`ipc_output_manager', `hh', `cc', `fwd')
add(`literal_metadata_key', `hh', `cc')
add(`mask', `hh', `cc', `fwd', `se')
add(`match_package', `hh', `cc', `se', `fwd')
diff --git a/paludis/ipc_output_manager-fwd.hh b/paludis/ipc_output_manager-fwd.hh
new file mode 100644
index 000000000..328e449d9
--- /dev/null
+++ b/paludis/ipc_output_manager-fwd.hh
@@ -0,0 +1,32 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 Ciaran McCreesh
+ *
+ * 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_IPC_OUTPUT_MANAGER_FWD_HH
+#define PALUDIS_GUARD_PALUDIS_IPC_OUTPUT_MANAGER_FWD_HH 1
+
+namespace paludis
+{
+ struct IPCOutputManager;
+
+ struct IPCInputManager;
+
+ struct OutputManagerFromIPC;
+}
+
+#endif
diff --git a/paludis/ipc_output_manager.cc b/paludis/ipc_output_manager.cc
new file mode 100644
index 000000000..3eaa22c89
--- /dev/null
+++ b/paludis/ipc_output_manager.cc
@@ -0,0 +1,405 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 Ciaran McCreesh
+ *
+ * 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 <paludis/ipc_output_manager.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/safe_ofstream.hh>
+#include <paludis/util/safe_ifstream.hh>
+#include <paludis/util/set.hh>
+#include <paludis/util/map.hh>
+#include <paludis/util/wrapped_forward_iterator.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/destringify.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/system.hh>
+#include <paludis/util/tokeniser.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/iterator_funcs.hh>
+#include <paludis/util/join.hh>
+#include <paludis/util/thread.hh>
+#include <paludis/util/pipe.hh>
+#include <paludis/standard_output_manager.hh>
+#include <paludis/create_output_manager_info.hh>
+#include <paludis/serialise.hh>
+#include <paludis/environment.hh>
+#include <tr1/functional>
+#include <vector>
+#include <cstdlib>
+#include <cstring>
+#include <unistd.h>
+#include <errno.h>
+
+using namespace paludis;
+
+namespace paludis
+{
+ template <>
+ struct Implementation<IPCOutputManager>
+ {
+ std::tr1::shared_ptr<SafeOFStream> stdout_stream;
+ std::tr1::shared_ptr<SafeOFStream> stderr_stream;
+
+ std::tr1::shared_ptr<SafeIFStream> pipe_command_read_stream;
+ std::tr1::shared_ptr<SafeOFStream> pipe_command_write_stream;
+
+ Implementation(int r, int w) :
+ pipe_command_write_stream(new SafeOFStream(w))
+ {
+ *pipe_command_write_stream << "PING 1 GOAT" << '\0' << std::flush;
+
+ pipe_command_read_stream.reset(new SafeIFStream(r));
+ std::string response;
+ if (! std::getline(*pipe_command_read_stream, response, '\0'))
+ throw InternalError(PALUDIS_HERE, "couldn't get a pipe command response");
+ if (response != "OPONG GOAT")
+ throw InternalError(PALUDIS_HERE, "got response '" + response + "'");
+ }
+ };
+}
+
+IPCOutputManager::IPCOutputManager(const int r, const int w, const CreateOutputManagerInfo & i) :
+ PrivateImplementationPattern<IPCOutputManager>(new Implementation<IPCOutputManager>(r, w))
+{
+ std::stringstream ser_stream;
+ Serialiser ser(ser_stream);
+ i.serialise(ser);
+ *_imp->pipe_command_write_stream << "CREATE 1 " << ser_stream.str() << '\0' << std::flush;
+
+ std::string response;
+ if (! std::getline(*_imp->pipe_command_read_stream, response, '\0'))
+ throw InternalError(PALUDIS_HERE, "couldn't get a pipe command response");
+
+ std::vector<std::string> tokens;
+ tokenise_whitespace(response, std::back_inserter(tokens));
+
+ if (tokens.size() != 4 || tokens[0] != "O" || tokens[1] != "1")
+ throw InternalError(PALUDIS_HERE, "got response '" + response + "'");
+
+ _imp->stdout_stream.reset(new SafeOFStream(destringify<int>(tokens[2])));
+ _imp->stderr_stream.reset(new SafeOFStream(destringify<int>(tokens[3])));
+}
+
+IPCOutputManager::~IPCOutputManager()
+{
+ *_imp->pipe_command_write_stream << "FINISHED 1" << '\0' << std::flush;
+
+ std::string response;
+ if (! std::getline(*_imp->pipe_command_read_stream, response, '\0'))
+ throw InternalError(PALUDIS_HERE, "couldn't get a pipe command response");
+ if (response != "O")
+ throw InternalError(PALUDIS_HERE, "got response '" + response + "'");
+}
+
+std::ostream &
+IPCOutputManager::stdout_stream()
+{
+ return *_imp->stdout_stream;
+}
+
+std::ostream &
+IPCOutputManager::stderr_stream()
+{
+ return *_imp->stderr_stream;
+}
+
+void
+IPCOutputManager::message(const MessageType t, const std::string & s)
+{
+ *_imp->pipe_command_write_stream << "MESSAGE 1 " << t << " " << s << '\0' << std::flush;
+
+ std::string response;
+ if (! std::getline(*_imp->pipe_command_read_stream, response, '\0'))
+ throw InternalError(PALUDIS_HERE, "couldn't get a pipe command response");
+ if (response != "O")
+ throw InternalError(PALUDIS_HERE, "got response '" + response + "'");
+}
+
+void
+IPCOutputManager::succeeded()
+{
+ *_imp->pipe_command_write_stream << "SUCCEEDED 1" << '\0' << std::flush;
+
+ std::string response;
+ if (! std::getline(*_imp->pipe_command_read_stream, response, '\0'))
+ throw InternalError(PALUDIS_HERE, "couldn't get a pipe command response");
+ if (response != "O")
+ throw InternalError(PALUDIS_HERE, "got response '" + response + "'");
+}
+
+void
+IPCOutputManager::flush()
+{
+}
+
+bool
+IPCOutputManager::want_to_flush() const
+{
+ return false;
+}
+
+void
+IPCOutputManager::nothing_more_to_come()
+{
+ *_imp->pipe_command_write_stream << "NOTHING_MORE_TO_COME 1" << '\0' << std::flush;
+
+ std::string response;
+ if (! std::getline(*_imp->pipe_command_read_stream, response, '\0'))
+ throw InternalError(PALUDIS_HERE, "couldn't get a pipe command response");
+ if (response != "O")
+ throw InternalError(PALUDIS_HERE, "got response '" + response + "'");
+}
+
+namespace paludis
+{
+ template <>
+ struct Implementation<IPCInputManager>
+ {
+ const Environment * const env;
+ const OutputExclusivity exclusivity;
+
+ std::tr1::shared_ptr<OutputManager> output_manager;
+ std::tr1::shared_ptr<Thread> copy_thread;
+
+ Pipe stdout_pipe, stderr_pipe, finished_pipe;
+
+ Implementation(const Environment * const e, const OutputExclusivity x) :
+ env(e),
+ exclusivity(x)
+ {
+ }
+ };
+}
+
+IPCInputManager::IPCInputManager(const Environment * const e, const OutputExclusivity x) :
+ PrivateImplementationPattern<IPCInputManager>(new Implementation<IPCInputManager>(e, x))
+{
+}
+
+IPCInputManager::~IPCInputManager()
+{
+ char c('x');
+ if (1 != write(_imp->finished_pipe.write_fd(), &c, 1))
+ throw InternalError(PALUDIS_HERE, "write failed");
+}
+
+const std::tr1::function<std::string (const std::string &)>
+IPCInputManager::pipe_command_handler()
+{
+ return std::tr1::bind(&IPCInputManager::_pipe_command_handler, this, std::tr1::placeholders::_1);
+}
+
+std::string
+IPCInputManager::_pipe_command_handler(const std::string & s)
+{
+ std::vector<std::string> tokens;
+ tokenise_whitespace(s, std::back_inserter(tokens));
+
+ if (tokens.empty())
+ throw InternalError(PALUDIS_HERE, "tokens is empty");
+
+ if (tokens[0] == "PING")
+ {
+ if (tokens.size() != 3 || tokens[1] != "1")
+ return "Ebad PING subcommand";
+ return "OPONG " + tokens[2];
+ }
+ else if (tokens[0] == "CREATE")
+ {
+ if (tokens.size() != 3 || tokens[1] != "1")
+ return "Ebad CREATE subcommand";
+
+ if (_imp->output_manager)
+ return "Ealready constructed";
+
+ Deserialiser deserialiser(_imp->env, tokens[2]);
+ Deserialisation deserialisation("CreateOutputManagerInfo", deserialiser);
+ const std::tr1::shared_ptr<CreateOutputManagerInfo> i(CreateOutputManagerInfo::deserialise(deserialisation));
+
+ _imp->output_manager = _imp->env->create_output_manager(*i);
+ _imp->copy_thread.reset(new Thread(std::tr1::bind(&IPCInputManager::_copy_thread, this)));
+
+ return "O 1 " + stringify(_imp->stdout_pipe.write_fd()) + " " + stringify(_imp->stderr_pipe.write_fd());
+ }
+ else if (tokens[0] == "MESSAGE")
+ {
+ if (tokens.size() < 3 || tokens[1] != "1")
+ return "Ebad MESSAGE subcommand";
+
+ MessageType t(destringify<MessageType>(tokens[2]));
+ std::string m(join(next(tokens.begin(), 3), tokens.end(), " "));
+ _imp->output_manager->message(t, m);
+
+ return "O";
+ }
+ else if (tokens[0] == "SUCCEEDED")
+ {
+ if (tokens.size() != 2 || tokens[1] != "1")
+ return "Ebad SUCCEEDED subcommand";
+ _imp->output_manager->succeeded();
+
+ return "O";
+ }
+ else if (tokens[0] == "NOTHING_MORE_TO_COME")
+ {
+ if (tokens.size() != 2 || tokens[1] != "1")
+ return "Ebad NOTHING_MORE_TO_COME subcommand";
+ _imp->output_manager->nothing_more_to_come();
+
+ return "O";
+ }
+ else if (tokens[0] == "FINISHED")
+ {
+ if (tokens.size() != 2 || tokens[1] != "1")
+ return "Ebad FINISHED subcommand";
+
+ char c('x');
+ if (1 != write(_imp->finished_pipe.write_fd(), &c, 1))
+ throw InternalError(PALUDIS_HERE, "write failed");
+
+ return "O";
+ }
+ else
+ return "Eunknown pipe command";
+}
+
+void
+IPCInputManager::_copy_thread()
+{
+ bool done(false);
+ while (! done)
+ {
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+ int max_fd(0);
+
+ FD_SET(_imp->stdout_pipe.read_fd(), &read_fds);
+ max_fd = std::max(max_fd, _imp->stdout_pipe.read_fd());
+
+ FD_SET(_imp->stderr_pipe.read_fd(), &read_fds);
+ max_fd = std::max(max_fd, _imp->stderr_pipe.read_fd());
+
+ FD_SET(_imp->finished_pipe.read_fd(), &read_fds);
+ max_fd = std::max(max_fd, _imp->finished_pipe.read_fd());
+
+ int retval(pselect(max_fd + 1, &read_fds, 0, 0, 0, 0));
+ if (-1 == retval)
+ throw InternalError(PALUDIS_HERE, "pselect failed: " + stringify(strerror(errno)));
+
+ bool got_output(false);
+ char buf[1024];
+
+ if (FD_ISSET(_imp->stdout_pipe.read_fd(), &read_fds))
+ {
+ got_output = true;
+ int r;
+ if (((r = read(_imp->stdout_pipe.read_fd(), buf, 1024))) > 0)
+ _imp->output_manager->stdout_stream() << std::string(buf, r);
+ }
+
+ if (FD_ISSET(_imp->stderr_pipe.read_fd(), &read_fds))
+ {
+ got_output = true;
+ int r;
+ if (((r = read(_imp->stderr_pipe.read_fd(), buf, 1024))) > 0)
+ _imp->output_manager->stderr_stream() << std::string(buf, r);
+ }
+
+ if (got_output)
+ continue;
+
+ if (FD_ISSET(_imp->finished_pipe.read_fd(), &read_fds))
+ {
+ int r;
+ if (((r = read(_imp->finished_pipe.read_fd(), buf, 1024))) > 0)
+ done = true;
+ }
+ }
+}
+
+namespace paludis
+{
+ template <>
+ struct Implementation<OutputManagerFromIPC>
+ {
+ const Environment * const env;
+ const std::tr1::shared_ptr<const PackageID> id;
+ const OutputExclusivity exclusivity;
+
+ int read_fd, write_fd;
+
+ std::tr1::shared_ptr<OutputManager> result;
+
+ Implementation(const Environment * const e, const std::tr1::shared_ptr<const PackageID> & i,
+ const OutputExclusivity x) :
+ env(e),
+ id(i),
+ exclusivity(x),
+ read_fd(destringify<int>(getenv_with_default("PALUDIS_IPC_READ_FD", "-1"))),
+ write_fd(destringify<int>(getenv_with_default("PALUDIS_IPC_WRITE_FD", "-1")))
+ {
+ if (-1 == read_fd || -1 == write_fd)
+ throw InternalError(PALUDIS_HERE, "no pipe command handler available");
+ unsetenv("PALUDIS_IPC_READ_FD");
+ unsetenv("PALUDIS_IPC_WRITE_FD");
+ }
+ };
+}
+
+OutputManagerFromIPC::OutputManagerFromIPC(const Environment * const e,
+ const std::tr1::shared_ptr<const PackageID> & i,
+ const OutputExclusivity x) :
+ PrivateImplementationPattern<OutputManagerFromIPC>(new Implementation<OutputManagerFromIPC>(e, i, x))
+{
+}
+
+OutputManagerFromIPC::~OutputManagerFromIPC()
+{
+}
+
+const std::tr1::shared_ptr<OutputManager>
+OutputManagerFromIPC::operator() (const Action & a)
+{
+ if (! _imp->result)
+ _imp->result.reset(new IPCOutputManager(_imp->read_fd, _imp->write_fd,
+ CreateOutputManagerForPackageIDActionInfo(_imp->id, a, _imp->exclusivity)));
+ return _imp->result;
+}
+
+const std::tr1::shared_ptr<OutputManager>
+OutputManagerFromIPC::output_manager_if_constructed()
+{
+ return _imp->result;
+}
+
+void
+OutputManagerFromIPC::construct_standard_if_unconstructed()
+{
+ if (! _imp->result)
+ {
+ Log::get_instance()->message("output_manager_from_ipc.constructed_standard", ll_warning, lc_context)
+ << "No output manager available, creating a standard output manager. This is probably a bug.";
+ _imp->result.reset(new StandardOutputManager);
+ }
+}
+
+template class PrivateImplementationPattern<IPCOutputManager>;
+template class PrivateImplementationPattern<IPCInputManager>;
+template class PrivateImplementationPattern<OutputManagerFromIPC>;
+
diff --git a/paludis/ipc_output_manager.hh b/paludis/ipc_output_manager.hh
new file mode 100644
index 000000000..24fde29dc
--- /dev/null
+++ b/paludis/ipc_output_manager.hh
@@ -0,0 +1,103 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 Ciaran McCreesh
+ *
+ * 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_IPC_OUTPUT_MANAGER_HH
+#define PALUDIS_GUARD_PALUDIS_IPC_OUTPUT_MANAGER_HH 1
+
+#include <paludis/ipc_output_manager-fwd.hh>
+#include <paludis/util/attributes.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/output_manager.hh>
+#include <paludis/environment-fwd.hh>
+#include <paludis/create_output_manager_info-fwd.hh>
+#include <paludis/package_id-fwd.hh>
+#include <paludis/action-fwd.hh>
+#include <tr1/memory>
+#include <tr1/functional>
+#include <string>
+
+namespace paludis
+{
+ class PALUDIS_VISIBLE IPCOutputManager :
+ private PrivateImplementationPattern<IPCOutputManager>,
+ public OutputManager
+ {
+ public:
+ IPCOutputManager(
+ const int pipe_read_fd,
+ const int pipe_write_fd,
+ const CreateOutputManagerInfo &);
+ ~IPCOutputManager();
+
+ virtual std::ostream & stdout_stream() PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual std::ostream & stderr_stream() PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual void succeeded();
+ virtual void flush();
+ virtual bool want_to_flush() const;
+ virtual void nothing_more_to_come();
+ virtual void message(const MessageType, const std::string &);
+ };
+
+ class PALUDIS_VISIBLE IPCInputManager :
+ private PrivateImplementationPattern<IPCInputManager>
+ {
+ private:
+ std::string _pipe_command_handler(const std::string &);
+ void _copy_thread();
+
+ public:
+ IPCInputManager(
+ const Environment * const,
+ const OutputExclusivity);
+
+ ~IPCInputManager();
+
+ const std::tr1::function<std::string (const std::string &)> pipe_command_handler()
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+ };
+
+ class PALUDIS_VISIBLE OutputManagerFromIPC :
+ private PrivateImplementationPattern<OutputManagerFromIPC>
+ {
+ public:
+ OutputManagerFromIPC(
+ const Environment * const,
+ const std::tr1::shared_ptr<const PackageID> &,
+ const OutputExclusivity
+ );
+
+ ~OutputManagerFromIPC();
+
+ const std::tr1::shared_ptr<OutputManager> operator() (const Action &);
+
+ const std::tr1::shared_ptr<OutputManager> output_manager_if_constructed();
+
+ void construct_standard_if_unconstructed();
+ };
+
+#ifdef PALUDIS_HAVE_EXTERN_TEMPLATE
+ extern template class PrivateImplementationPattern<IPCOutputManager>;
+ extern template class PrivateImplementationPattern<IPCInputManager>;
+ extern template class PrivateImplementationPattern<OutputManagerFromIPC>;
+#endif
+
+}
+
+#endif