mount --show-rc: display main process rc, fixes #8308

when borg mount is used without -f/--foreground (so that the FUSE
borg process was started daemonized in the background), it did not
display the rc of the main process, even when --show-rc was used.

now it does display the rc of the main process.

note that this is rather a consistency fix than being super useful,
because the main "action" happens in the background daemon process,
not in the main process.
This commit is contained in:
Thomas Waldmann 2025-10-11 18:18:28 +02:00
parent 98b831ba8d
commit 702de47197
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
4 changed files with 40 additions and 19 deletions

View file

@ -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")
@ -5678,21 +5678,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)

View file

@ -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)

View file

@ -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

View file

@ -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)