From 1da28d80c00e24e4aa65ab56bbfb6337d61fc75e Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 28 Mar 2024 22:07:39 +0100 Subject: [PATCH] fix Ctrl-C / SIGINT behaviour for pyinstaller-made binaries, fixes #8155 --- src/borg/helpers/process.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/borg/helpers/process.py b/src/borg/helpers/process.py index 3dd19c7a4b..d3e42e3f70 100644 --- a/src/borg/helpers/process.py +++ b/src/borg/helpers/process.py @@ -188,6 +188,9 @@ def __init__(self): self._action_triggered = False self._action_done = False self.ctx = signal_handler('SIGINT', self.handler) + self.debounce_interval = 20000000 # ns + self.last = -self.debounce_interval # monotonic time when we last processed SIGINT + self.count = 0 # how many SIGINT did we process? def __bool__(self): # this will be True (and stay True) after the first Ctrl-C/SIGINT @@ -208,10 +211,24 @@ def action_completed(self): self._action_done = True def handler(self, sig_no, stack): - # handle the first ctrl-c / SIGINT. - self.__exit__(None, None, None) - self._sig_int_triggered = True - self._action_triggered = True + # ignore a SIGINT if it comes too quickly after the last one, e.g. because it + # was caused by the same Ctrl-C key press and a parent process forwarded it to us. + # this can easily happen for the pyinstaller-made binaries because the bootloader + # process and the borg process are in same process group (see #8155), but maybe also + # under other circumstances. + now = time.monotonic_ns() + if now - self.last >= self.debounce_interval: + self.last = now + self.count += 1 + if self.count == 1: # first SIGINT + self._sig_int_triggered = True + self._action_triggered = True + else: # second SIGINT + # restore the original signal handler for the 3rd+ SIGINT - + # this implies that this handler here loses control! + self.__exit__(None, None, None) + # handle 2nd SIGINT like the default handler would do it: + raise KeyboardInterrupt # python docs say this might show up at an arbitrary place. def __enter__(self): self.ctx.__enter__()