| # Copyright 2010 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| import errno |
| import pickle |
| from _emerge.FifoIpcDaemon import FifoIpcDaemon |
| |
| class EbuildIpcDaemon(FifoIpcDaemon): |
| """ |
| 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 background (as in bug #278895). |
| |
| 4) Detect cases in which bash has exited unexpectedly (as in bug #190128). |
| """ |
| |
| __slots__ = ('commands',) |
| |
| def _input_handler(self, fd, event): |
| # Read the whole pickle in a single atomic read() call. |
| buf = self._read_buf(self._files.pipe_in, event) |
| |
| if buf: |
| |
| try: |
| obj = pickle.loads(buf.tostring()) |
| except SystemExit: |
| raise |
| except Exception: |
| # The pickle module can raise practically |
| # any exception when given corrupt data. |
| pass |
| else: |
| cmd_key = obj[0] |
| cmd_handler = self.commands[cmd_key] |
| reply = cmd_handler(obj) |
| try: |
| self._send_reply(reply) |
| except OSError as e: |
| if e.errno == errno.ENXIO: |
| # This happens if the client side has been killed. |
| pass |
| else: |
| raise |
| |
| # Allow the command to execute hooks after its reply |
| # has been sent. This hook is used by the 'exit' |
| # command to kill the ebuild process. For some |
| # reason, the ebuild-ipc helper hangs up the |
| # ebuild process if it is waiting for a reply |
| # when we try to kill the ebuild process. |
| reply_hook = getattr(cmd_handler, |
| 'reply_hook', None) |
| if reply_hook is not None: |
| reply_hook() |
| |
| def _send_reply(self, reply): |
| # File streams are in unbuffered mode since we do atomic |
| # read and write of whole pickles. |
| output_file = open(self.output_fifo, 'wb', 0) |
| |
| # Write the whole pickle in a single atomic write() call, |
| # since the reader is in non-blocking mode and we want |
| # it to get the whole pickle at once. |
| output_file.write(pickle.dumps(reply)) |
| output_file.close() |