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-16 02:26:06 +02:00
parent 4b28cfdae8
commit a775517f08
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
5 changed files with 43 additions and 20 deletions

View file

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

View file

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

View file

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

View file

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

View file

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