diff --git a/src/borg/archiver.py b/src/borg/archiver.py index c247bb778..855747e00 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -1461,7 +1461,7 @@ class Archiver: operations = FuseOperations(key, repository, 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") @@ -5680,21 +5680,8 @@ 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/fuse.py b/src/borg/fuse.py index d0ed9b81c..a320c3291 100644 --- a/src/borg/fuse.py +++ b/src/borg/fuse.py @@ -484,7 +484,7 @@ class FuseOperations(llfuse.Operations, FuseBackend): format_file_size(sum(len(chunk) for key, chunk in self.data_cache.items()))) 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): @@ -556,7 +556,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 3a55d598f..f4d281293 100644 --- a/src/borg/helpers/__init__.py +++ b/src/borg/helpers/__init__.py @@ -5,6 +5,7 @@ Code used to be in borg/helpers.py but was split into modules in this package, which are imported here for compatibility. """ from contextlib import contextmanager +import logging from .checks import * # NOQA from .datastruct import * # NOQA @@ -150,3 +151,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 373e66b27..fcee05ee9 100644 --- a/src/borg/helpers/process.py +++ b/src/borg/helpers/process.py @@ -62,7 +62,7 @@ def daemonize(): @contextlib.contextmanager -def daemonizing(*, timeout=5): +def daemonizing(*, timeout=5, show_rc=False): """Like daemonize(), but as context manager. The with-body is executed in the background process, @@ -107,6 +107,12 @@ 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)