diff --git a/src/borg/archiver/__init__.py b/src/borg/archiver/__init__.py index 0a7c013cd..481b286b0 100644 --- a/src/borg/archiver/__init__.py +++ b/src/borg/archiver/__init__.py @@ -34,7 +34,7 @@ try: from ._common import Highlander from .. import __version__ from ..constants import * # NOQA - from ..helpers import EXIT_WARNING, EXIT_ERROR, EXIT_SIGNAL_BASE, classify_ec + from ..helpers import EXIT_WARNING, EXIT_ERROR, EXIT_SIGNAL_BASE from ..helpers import Error, CommandError, get_ec, modern_ec from ..helpers import add_warning, BorgWarning, BackupWarning from ..helpers import format_file_size @@ -686,21 +686,9 @@ def main(): # pragma: no cover if tb: logger.log(tb_log_level, tb) if args.show_rc: - rc_logger = logging.getLogger("borg.output.show-rc") - exit_msg = "terminating with %s status, rc %d" - try: - ec_class = classify_ec(exit_code) - except ValueError: - rc_logger.error(exit_msg % ("abnormal", exit_code or 666)) - else: - if ec_class == "success": - rc_logger.info(exit_msg % (ec_class, exit_code)) - elif ec_class == "warning": - rc_logger.warning(exit_msg % (ec_class, exit_code)) - elif ec_class == "error": - rc_logger.error(exit_msg % (ec_class, exit_code)) - elif ec_class == "signal": - rc_logger.error(exit_msg % (ec_class, exit_code)) + from ..helpers import do_show_rc + + do_show_rc(exit_code) sys.exit(exit_code) diff --git a/src/borg/archiver/mount_cmds.py b/src/borg/archiver/mount_cmds.py index d8e3eee73..13180c7cd 100644 --- a/src/borg/archiver/mount_cmds.py +++ b/src/borg/archiver/mount_cmds.py @@ -40,7 +40,7 @@ class MountMixIn: operations = FuseOperations(manifest, args, cached_repo) logger.info("Mounting filesystem") try: - operations.mount(args.mountpoint, args.options, args.foreground) + operations.mount(args.mountpoint, args.options, args.foreground, args.show_rc) except RuntimeError: # Relevant error message already printed to stderr by FUSE raise RTError("FUSE mount failed") diff --git a/src/borg/fuse.py b/src/borg/fuse.py index 166142b14..44c17c134 100644 --- a/src/borg/fuse.py +++ b/src/borg/fuse.py @@ -489,7 +489,7 @@ class FuseOperations(llfuse.Operations, FuseBackend): ) self.decrypted_repository.log_instrumentation() - def mount(self, mountpoint, mount_options, foreground=False): + def mount(self, mountpoint, mount_options, foreground=False, show_rc=False): """Mount filesystem on *mountpoint* with *mount_options*.""" def pop_option(options, key, present, not_present, wanted_type, int_base=0): @@ -562,7 +562,7 @@ class FuseOperations(llfuse.Operations, FuseBackend): if isinstance(self.repository_uncached, RemoteRepository): daemonize() else: - with daemonizing() as (old_id, new_id): + with daemonizing(show_rc=show_rc) as (old_id, new_id): # local repo: the locking process' PID is changing, migrate it: logger.debug("fuse: mount local repo, going to background: migrating lock.") self.repository_uncached.migrate_lock(old_id, new_id) diff --git a/src/borg/helpers/__init__.py b/src/borg/helpers/__init__.py index cbd73e8de..39fba0d1e 100644 --- a/src/borg/helpers/__init__.py +++ b/src/borg/helpers/__init__.py @@ -6,6 +6,7 @@ package, which are imported here for compatibility. """ import os +import logging from typing import List from collections import namedtuple @@ -172,3 +173,30 @@ def get_reset_ec(ec=None): rc = get_ec(ec) init_ec_warnings() return rc + + +def do_show_rc(exit_code): + """Log the program return code using the dedicated 'borg.output.show-rc' logger. + + Uses INFO/WARNING/ERROR levels depending on the classified exit code. + + This helper is robust: it swallows any exceptions to avoid interfering with + program exit behavior. Callers do not need to guard it with try/except. + """ + try: + exit_msg = "terminating with %s status, rc %d" + rc_logger = logging.getLogger("borg.output.show-rc") + try: + ec_class = classify_ec(exit_code) + except ValueError: + rc_logger.error(exit_msg % ("abnormal", exit_code or 666)) + else: + if ec_class == "success": + rc_logger.info(exit_msg % (ec_class, exit_code)) + elif ec_class == "warning": + rc_logger.warning(exit_msg % (ec_class, exit_code)) + elif ec_class in ("error", "signal"): + rc_logger.error(exit_msg % (ec_class, exit_code)) + except Exception: + # Never let logging issues interfere with exit behaviour + pass diff --git a/src/borg/helpers/process.py b/src/borg/helpers/process.py index 27ffcaaef..b19aabcf1 100644 --- a/src/borg/helpers/process.py +++ b/src/borg/helpers/process.py @@ -63,7 +63,7 @@ def daemonize(): @contextlib.contextmanager -def daemonizing(*, timeout=5): +def daemonizing(*, timeout=5, show_rc=False): """Like daemonize(), but as a context manager. The with-body is executed in the background process, @@ -112,6 +112,13 @@ def daemonizing(*, timeout=5): logger.warning("Daemonizing: Background process did not respond (timeout). Is it alive?") exit_code = EXIT_WARNING finally: + # Before terminating the foreground process, honor --show-rc by logging the rc here as well. + # This is mostly a consistency fix and not very useful considering that the main action + # happens in the daemon process. + if show_rc: + from ..helpers import do_show_rc + + do_show_rc(exit_code) # Don't call with-body, but die immediately! # return would be sufficient, but we want to pass the exit code. raise _ExitCodeException(exit_code)