diff options
Diffstat (limited to 'pym/_emerge')
-rw-r--r-- | pym/_emerge/FifoIpcDaemon.py | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/pym/_emerge/FifoIpcDaemon.py b/pym/_emerge/FifoIpcDaemon.py new file mode 100644 index 000000000..16bc786ae --- /dev/null +++ b/pym/_emerge/FifoIpcDaemon.py @@ -0,0 +1,108 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import array +import pickle +from portage import os +from _emerge.AbstractPollTask import AbstractPollTask +from _emerge.PollConstants import PollConstants +from portage.cache.mappings import slot_dict_class + +class FifoIpcDaemon(AbstractPollTask): + + """ + This class serves as an IPC daemon, which ebuild processes can use + to communicate with portage's main python process. + + Here are a few possible uses: + + 1) Robust subshell/subprocess die support. This allows the ebuild + environment to reliably die without having to rely on signal IPC. + + 2) Delegation of portageq calls to the main python process, eliminating + performance and userpriv permission issues. + + 3) Reliable ebuild termination in cases when the ebuild has accidentally + left orphan processes running in the backgraound (as in bug 278895). + """ + + __slots__ = ("input_fifo", "output_fifo",) + \ + ("_files", "_reg_id",) + + _file_names = ("pipe_in",) + _files_dict = slot_dict_class(_file_names, prefix="") + + def _start(self): + self._files = self._files_dict() + input_fd = os.open(self.input_fifo, os.O_RDONLY|os.O_NONBLOCK) + self._files.pipe_in = os.fdopen(input_fd, 'rb') + + self._reg_id = self.scheduler.register( + self._files.pipe_in.fileno(), + self._registered_events, self._input_handler) + + self._registered = True + + def isAlive(self): + return self._registered + + def cancel(self): + if self.returncode is None: + self.returncode = 1 + self.cancelled = True + self._unregister() + self.wait() + + def _wait(self): + if self.returncode is not None: + return self.returncode + + if self._registered: + self.scheduler.schedule(self._reg_id) + self._unregister() + + if self.returncode is None: + self.returncode = os.EX_OK + + return self.returncode + + def _input_handler(self, fd, event): + + if event & PollConstants.POLLIN: + + buf = array.array('B') + try: + buf.fromfile(self._files.pipe_in, self._bufsize) + except (EOFError, IOError): + pass + + if buf: + obj = pickle.loads(buf.tostring()) + if isinstance(obj, list) and \ + obj and \ + obj[0] == 'exit': + output_fd = os.open(self.output_fifo, os.O_WRONLY|os.O_NONBLOCK) + output_file = os.fdopen(output_fd, 'wb') + pickle.dump('OK', output_file) + output_file.close() + self._unregister() + self.wait() + + self._unregister_if_appropriate(event) + return self._registered + + def _unregister(self): + """ + Unregister from the scheduler and close open files. + """ + + self._registered = False + + if self._reg_id is not None: + self.scheduler.unregister(self._reg_id) + self._reg_id = None + + if self._files is not None: + for f in self._files.values(): + f.close() + self._files = None |