| #!/usr/bin/env python |
| # Copyright 2006-2023 Gentoo Authors |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| import os |
| import signal |
| import sys |
| |
| # For compatibility with Python < 3.8 |
| raise_signal = getattr( |
| signal, "raise_signal", lambda signum: os.kill(os.getpid(), signum) |
| ) |
| |
| |
| # Inherit from KeyboardInterrupt to avoid a traceback from asyncio. |
| class SignalInterrupt(KeyboardInterrupt): |
| def __init__(self, signum): |
| self.signum = signum |
| |
| |
| def signal_interrupt(signum, _frame): |
| raise SignalInterrupt(signum) |
| |
| |
| def debug_signal(_signum, _frame): |
| import pdb |
| |
| pdb.set_trace() |
| |
| |
| # Prevent "[Errno 32] Broken pipe" exceptions when writing to a pipe. |
| signal.signal(signal.SIGPIPE, signal.SIG_DFL) |
| signal.signal(signal.SIGTERM, signal_interrupt) |
| signal.signal(signal.SIGUSR1, debug_signal) |
| |
| from os import path as osp |
| |
| if osp.isfile( |
| osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), ".portage_not_installed") |
| ): |
| sys.path.insert( |
| 0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "lib") |
| ) |
| import portage |
| |
| portage._internal_caller = True |
| portage._disable_legacy_globals() |
| from portage.elog import mod_echo |
| from portage.exception import IsADirectory, ParseError, PermissionDenied |
| from portage.util._eventloop.global_event_loop import global_event_loop |
| from _emerge.main import emerge_main |
| |
| |
| def main(): |
| portage.process.sanitize_fds() |
| try: |
| retval = emerge_main() |
| except PermissionDenied as e: |
| sys.stderr.write(f"Permission denied: '{str(e)}'\n") |
| sys.exit(e.errno) |
| except IsADirectory as e: |
| sys.stderr.write( |
| f"'{str(e)}' is a directory, but should be a file!\n" |
| "See portage man page for information on " |
| "which files may be directories.\n" |
| ) |
| sys.exit(e.errno) |
| except ParseError as e: |
| sys.stderr.write(f"{str(e)}\n") |
| sys.exit(1) |
| except (KeyboardInterrupt, SystemExit): |
| raise |
| except Exception: |
| # If an unexpected exception occurs then we don't want the |
| # mod_echo output to obscure the traceback, so dump the |
| # mod_echo output before showing the traceback. |
| import traceback |
| |
| tb_str = traceback.format_exc() |
| mod_echo.finalize() |
| sys.stderr.write(tb_str) |
| sys.exit(1) |
| sys.exit(retval) |
| |
| |
| if __name__ == "__main__": |
| try: |
| main() |
| except KeyboardInterrupt as e: |
| # This block ensures that ^C interrupts are handled quietly. We handle |
| # KeyboardInterrupt instead of installing a SIGINT handler, since |
| # exiting from signal handlers intermittently causes python to ignore |
| # the SystemExit exception with a message like this: |
| # Exception SystemExit: 130 in <function remove at 0x7fd2146c1320> ignored |
| signum = getattr(e, "signum", signal.SIGINT) |
| signal.signal(signum, signal.SIG_DFL) |
| sys.stderr.write(f"\n\nExiting on signal {signum}\n") |
| sys.stderr.flush() |
| raise_signal(signum) |
| finally: |
| # Only close the event loop for __main__, |
| # since outside of __main__ it would close the |
| # event loop for child processes when using |
| # the multiprocessing spawn start method. |
| global_event_loop().close() |