2
0
Эх сурвалжийг харах

Merge pull request #6949 from ThomasWaldmann/fix-ctrl-c-remote-repo-master

ctrl-c must not kill important subprocesses, fixes #6912
TW 2 жил өмнө
parent
commit
7b2be94262

+ 3 - 3
src/borg/archiver/create.py

@@ -26,7 +26,7 @@ from ..helpers import dir_is_tagged
 from ..helpers import log_multi
 from ..helpers import basic_json_data, json_print
 from ..helpers import flags_root, flags_dir, flags_special_follow, flags_special
-from ..helpers import sig_int
+from ..helpers import sig_int, ignore_sigint
 from ..helpers import iter_separated
 from ..patterns import PatternMatcher
 from ..platform import get_flags
@@ -68,7 +68,7 @@ class CreateMixIn:
                 if not dry_run:
                     try:
                         try:
-                            proc = subprocess.Popen(args.paths, stdout=subprocess.PIPE)
+                            proc = subprocess.Popen(args.paths, stdout=subprocess.PIPE, preexec_fn=ignore_sigint)
                         except (FileNotFoundError, PermissionError) as e:
                             self.print_error("Failed to execute command: %s", e)
                             return self.exit_code
@@ -89,7 +89,7 @@ class CreateMixIn:
                 paths_sep = eval_escapes(args.paths_delimiter) if args.paths_delimiter is not None else "\n"
                 if args.paths_from_command:
                     try:
-                        proc = subprocess.Popen(args.paths, stdout=subprocess.PIPE)
+                        proc = subprocess.Popen(args.paths, stdout=subprocess.PIPE, preexec_fn=ignore_sigint)
                     except (FileNotFoundError, PermissionError) as e:
                         self.print_error("Failed to execute command: %s", e)
                         return self.exit_code

+ 2 - 1
src/borg/helpers/__init__.py

@@ -31,7 +31,8 @@ from .parseformat import Location, location_validator, archivename_validator
 from .parseformat import BaseFormatter, ArchiveFormatter, ItemFormatter, file_status
 from .parseformat import swidth_slice, ellipsis_truncate
 from .parseformat import BorgJsonEncoder, basic_json_data, json_print, json_dump, prepare_dump_dict
-from .process import daemonize, daemonizing, signal_handler, raising_signal_handler, sig_int, SigHup, SigTerm
+from .process import daemonize, daemonizing
+from .process import signal_handler, raising_signal_handler, sig_int, ignore_sigint, SigHup, SigTerm
 from .process import popen_with_error_handling, is_terminal, prepare_subprocess_env, create_filter_process
 from .progress import ProgressIndicatorPercent, ProgressIndicatorEndless, ProgressIndicatorMessage
 from .time import parse_timestamp, timestamp, safe_timestamp, safe_s, safe_ns, MAX_S, SUPPORT_32BIT_PLATFORMS

+ 25 - 2
src/borg/helpers/process.py

@@ -236,6 +236,19 @@ class SigIntManager:
 sig_int = SigIntManager()
 
 
+def ignore_sigint():
+    """
+    Ignore SIGINT, see also issue #6912.
+
+    Ctrl-C will send a SIGINT to both the main process (borg) and subprocesses
+    (e.g. ssh for remote ssh:// repos), but often we do not want the subprocess
+    getting killed (e.g. because it is still needed to cleanly shut down borg).
+
+    To avoid that: Popen(..., preexec_fn=ignore_sigint)
+    """
+    signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+
 def popen_with_error_handling(cmd_line: str, log_prefix="", **kwargs):
     """
     Handle typical errors raised by subprocess.Popen. Return None if an error occurred,
@@ -323,11 +336,21 @@ def create_filter_process(cmd, stream, stream_close, inbound=True):
         # for us to do something while we block on the process for something different.
         if inbound:
             proc = popen_with_error_handling(
-                cmd, stdout=subprocess.PIPE, stdin=filter_stream, log_prefix="filter-process: ", env=env
+                cmd,
+                stdout=subprocess.PIPE,
+                stdin=filter_stream,
+                log_prefix="filter-process: ",
+                env=env,
+                preexec_fn=ignore_sigint,
             )
         else:
             proc = popen_with_error_handling(
-                cmd, stdin=subprocess.PIPE, stdout=filter_stream, log_prefix="filter-process: ", env=env
+                cmd,
+                stdin=subprocess.PIPE,
+                stdout=filter_stream,
+                log_prefix="filter-process: ",
+                env=env,
+                preexec_fn=ignore_sigint,
             )
         if not proc:
             raise Error(f"filter {cmd}: process creation failed")

+ 4 - 2
src/borg/remote.py

@@ -26,7 +26,7 @@ from .helpers import replace_placeholders
 from .helpers import sysinfo
 from .helpers import format_file_size
 from .helpers import safe_unlink
-from .helpers import prepare_subprocess_env
+from .helpers import prepare_subprocess_env, ignore_sigint
 from .logger import create_logger, setup_logging
 from .helpers import msgpack
 from .repository import Repository
@@ -582,7 +582,9 @@ class RemoteRepository:
         if not testing:
             borg_cmd = self.ssh_cmd(location) + borg_cmd
         logger.debug("SSH command line: %s", borg_cmd)
-        self.p = Popen(borg_cmd, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
+        # we do not want the ssh getting killed by Ctrl-C/SIGINT because it is needed for clean shutdown of borg.
+        # borg's SIGINT handler tries to write a checkpoint and requires the remote repo connection.
+        self.p = Popen(borg_cmd, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env, preexec_fn=ignore_sigint)
         self.stdin_fd = self.p.stdin.fileno()
         self.stdout_fd = self.p.stdout.fileno()
         self.stderr_fd = self.p.stderr.fileno()