#1052451 when receiving a SIGINT, unison should send it to the process group, not just to ssh

Package:
unison-2.53
Source:
unison-2.53
Description:
file-synchronization tool for Unix and Windows
Submitter:
Vincent Lefevre
Date:
2024-06-28 14:30:03 UTC
Severity:
normal
#1052451#5
Date:
2023-09-22 09:28:12 UTC
From:
To:
It seems that that ssh command runs ssh-add automatically.
While I did a Ctrl-C to interrupt ssh started by unison (for
synchronization with a remote machine) from mutt, ssh-add is
still running and attached to the terminal:

UID          PID    PPID  C STIME TTY          TIME CMD
vlefevre    2129    2128  0 11:00 pts/5    00:00:04 mutt
vlefevre    2358       1  0 11:03 pts/5    00:00:00 ssh-add [...]

and it steals input (typed keys) that should go to mutt.

Note: ssh-add was asking for a passphrase, and I did not want to
enter it now. That's why I did Ctrl-C.

#1052451#10
Date:
2023-09-22 10:10:19 UTC
From:
To:
The current ssh-add backtrace shows successive calls to do_file,
add_file, read_passphrase, readpassphrase, read, __GI___libc_read.
So it is still trying to read a passphrase from the terminal.

#1052451#15
Date:
2023-09-22 10:18:24 UTC
From:
To:
And a strace shows that it still receives most keys rather than
mutt, except when I'm doing a Ctrl-C, in which case only mutt
receives the signal:

cventin:~> strace -p 2358
strace: Process 2358 attached
read(4, "a", 1)                         = 1
read(4, "b", 1)                         = 1
read(4, "c", 1)                         = 1
read(4, "d", 1)                         = 1
read(4, "e", 1)                         = 1
read(4, "f", 1)                         = 1
read(4, "g", 1)                         = 1
read(4, "h", 1)                         = 1
read(4, "i", 1)                         = 1
read(4, "j", 1)                         = 1
read(4, "k", 1)                         = 1
read(4, "l", 1)                         = 1
read(4, "m", 1)                         = 1
read(4, "n", 1)                         = 1
read(4,

and a "kill -INT 2358" doesn't have any effect.

But a "kill -QUIT 2358" killed it:

read(4, 0x7ffd25d91c1f, 1)              = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGQUIT {si_signo=SIGQUIT, si_code=SI_USER, si_pid=24725, si_uid=1000} ---
rt_sigreturn({mask=[INT]})              = -1 EINTR (Interrupted system call)
write(4, "\n", 1)                       = 1
ioctl(4, TCGETS, {c_iflag=IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCSETSF, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCGETS, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
rt_sigaction(SIGALRM, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
rt_sigaction(SIGHUP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
rt_sigaction(SIGTSTP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
rt_sigaction(SIGTTIN, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
rt_sigaction(SIGTTOU, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f3b4125a510}, NULL, 8) = 0
close(4)                                = 0
getpid()                                = 2358
kill(2358, SIGQUIT)                     = 0
--- SIGQUIT {si_signo=SIGQUIT, si_code=SI_USER, si_pid=2358, si_uid=1000} ---
+++ killed by SIGQUIT (core dumped) +++

#1052451#20
Date:
2023-09-22 12:13:52 UTC
From:
To:
(No issues with ssh-add is run directly from the shell, see below.)
the wrapper was not involved here because it was no longer running.
What happens is that the Ctrl-C seems to kill the wrapper (thus ssh
and unison terminate too, returning to mutt), but not ssh-add, hence
the issue.

A possibly important point: I call ssh-add with several files in
argument, and a Ctrl-C aborts the first passphrase, but ssh-add is
still running for the other passphrases, which is bad.

An example:

cventin:~> unison mail -root "ssh://joooj/Mail"
Unison 2.53.3 (ocaml 4.13.1): Contacting server...
Enter passphrase for /home/vlefevre/.ssh/id_rsa:

"ps -fu vlefevre" shows in particular:

vlefevre   54481   54467  0 13:50 pts/4    00:00:00 cat
vlefevre   54483   54467  0 13:50 pts/4    00:00:00 ssh-add /home/vlefevre/.ssh/id_rsa /home/vlefevre/.ssh/id_rsa-and

(the long line is truncated).

Now I type Ctrl-C, which gives:

Enter passphrase for /home/vlefevre/.ssh/id_rsa: Terminated!
cventin:~[3]>                                                         <13:50:24

and "ps -fu vlefevre":

vlefevre   54481       1  0 13:50 pts/4    00:00:00 cat
vlefevre   54483       1  0 13:50 pts/4    00:00:00 ssh-add /home/vlefevre/.ssh/id_rsa /home/vlefevre/.ssh/id_rsa-and

These processes are now attached to pid 1, with still pts/4 as
the associated terminal.

If I type any character, ssh-add receives the key as the following
is output:

Enter passphrase for /home/vlefevre/.ssh/id_rsa-android:
Enter passphrase for /home/vlefevre/.ssh/id_rsa-cfarm:
[...]

and ssh-add terminates.

But when unison is run from Mutt, this is worse, as ssh-add doesn't
terminate on receiving a key.

So it seems that ssh-add incorrectly handles signals.

Note that if I run ssh-add with several files directly from the
shell, there are no issues: ssh-add is killed as expected, without
printing anything about the other files.

[...]
[...]

However, the terminal got in a broken state and mutt was no longer
receiving any key.

#1052451#27
Date:
2023-09-22 13:56:05 UTC
From:
To:
A summary of the issue: I'm using a wrapper to ssh in order to
call ssh-add before the real ssh, when needed. When I run unison
and type Ctrl-C when a passphrase is asked by ssh-add, this kills
unison and my wrapper, but not ssh-add.

BTW, I've noticed that with svn (which uses my ssh wrapper in the
same way as unison), everything is OK. So this issue is specific
to the execution via unison.

In my wrapper, I've replaced "ssh-add" by "strace -o ~/str.out ssh-add",
so that I can now see more about the issue: with svn, ssh-add receives
the SIGINT, but not with unison!

In the ssh-add strace output when using svn:

[...]
openat(AT_FDCWD, "/dev/tty", O_RDWR)    = 4
ioctl(4, TCGETS, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCGETS, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCSETSF, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCGETS, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
rt_sigaction(SIGALRM, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGHUP, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGINT, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTSTP, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTTIN, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTTOU, {sa_handler=0x55993f0238b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f305625a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
write(4, "Enter passphrase for /home/vlefe"..., 49) = 49
read(4, 0x7ffd198c78cf, 1)              = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
write(4, "\n", 1)                       = 1
[...]

In the ssh-add strace output when using unison:

[...]
openat(AT_FDCWD, "/dev/tty", O_RDWR)    = 4
ioctl(4, TCGETS, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCGETS, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCSETSF, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCGETS, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
rt_sigaction(SIGALRM, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGHUP, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGINT, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTSTP, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTTIN, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTTOU, {sa_handler=0x55e65c2078b0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
write(4, "Enter passphrase for /home/vlefe"..., 49) = 49
read(4,

and Ctrl-C does nothing, I suppose because SIGINT is ignored here.

In the unison-2.53-2.53.3 src/remote.ml file:

        (* Signals generated by the terminal from user input are sent to all
           processes in the foreground process group. This means that the ssh
           child process will receive SIGINT at the same time as Unison and
           close the connection before Unison has the chance to do cleanup with
           the remote end. To make matters more complicated, the ssh process
           must be in the foreground process group because interaction with the
           user is done via the terminal (not via stdin, stdout) and background
           processes can't read from the terminal (unless we'd set up a pty
           like is done for the GUI).

           Don't let these signals reach ssh by blocking them.

           The signals could be ignored instead of being blocked because ssh
           does not set handlers for SIGINT and SIGQUIT if they've been ignored
           at startup. But this triggers an error in ssh. The interactive
           passphrase reading function captures these signals for the purpose
           of restoring terminal settings (echo). When receiving a signal, and
           after restoring previous signal handlers, it resends the signal to
           itself. But now the signal is ignored and instead of terminating,
           the process will continue running as if passphrase reading function
           had returned with an empty result.

           Since the ssh process no longer receives the signals generated by
           user input we have to make sure that it terminates when Unison does.
           This usually happens due to its stdin and stdout being closed,
           except for when it is interacting with the user via terminal. To get
           around that, an [at_exit] handler is registered to send a SIGTERM
           and SIGKILL to the ssh process.  (Note, for [at_exit] handlers to
           run, unison process must terminate normally, not be killed. For
           SIGINT, this means that [Sys.catch_break true] (or an alternative
           SIGINT handler) must be set before creating the ssh process.) *)

So, in my case, unison sends SIGINT only to my ssh wrapper. It should
send the signal to the process group in order to mimic what normally
happens with a Ctrl-C.

Then, if I type a character, e.g. "z", ssh-add gets an I/O error, but
it reopens /dev/tty for the next passphrase instead of exiting. I'm
not sure whether this is correct, but via Mutt, the terminal was still
working for ssh-add, so that fixing the issue in unison would be the
only good solution.

write(4, "Enter passphrase for /home/vlefe"..., 49) = 49
read(4, "z", 1)                         = 1
read(4, 0x7ffc8bf3e17f, 1)              = -1 EIO (Input/output error)
write(4, "\n", 1)                       = 1
ioctl(4, TCGETS, {c_iflag=INLCR|ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
ioctl(4, TCSETSF, {c_iflag=ICRNL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = -1 EIO (Input/output error)
rt_sigaction(SIGALRM, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
rt_sigaction(SIGHUP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
rt_sigaction(SIGTSTP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
rt_sigaction(SIGTTIN, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
rt_sigaction(SIGTTOU, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f77bb25a510}, NULL, 8) = 0
close(4)                                = 0
openat(AT_FDCWD, "/home/vlefevre/.ssh/id_rsa-android", O_RDONLY) = 4
newfstatat(4, "", {st_mode=S_IFREG|0600, st_size=1766, ...}, AT_EMPTY_PATH) = 0
getuid()                                = 1000
newfstatat(4, "", {st_mode=S_IFREG|0600, st_size=1766, ...}, AT_EMPTY_PATH) = 0
read(4, "-----BEGIN RSA PRIVATE KEY-----\n"..., 4096) = 1766
read(4, "", 2330)                       = 0
read(4, "", 4096)                       = 0
close(4)                                = 0
ioctl(0, TCGETS, 0x7ffc8bf3e7e0)        = -1 ENOTTY (Inappropriate ioctl for device)
getpid()                                = 61437
openat(AT_FDCWD, "/dev/tty", O_RDWR)    = 4
[...]

#1052451#40
Date:
2024-04-19 15:17:32 UTC
From:
To:
While doing tests, I've noticed a similar issue when I don't type
the passphrase and wait. In this case, there is a timeout from
unison: "Timed out negotiating connection with the server".
This corresponds (in src/remote.ml) to

let initConnection ?(connReady=fun () -> ()) ?cleanup in_ch out_ch =
  (* [makeConnection] is not expected to raise any recoverable exceptions.
     If this assumption changes in the future then [in_ch] and [out_ch] must
     be closed in the recovery code. *)
  let conn = makeConnection false in_ch out_ch in
  let close_on_fail t =
    Lwt.catch (fun () -> t) (fun e -> closeConnection conn; Lwt.fail e)
  in
  let with_timeout t =
    Lwt.choose [t;
      Lwt_unix.sleep 120. >>= fun () ->
      Lwt.fail (Util.Fatal "Timed out negotiating connection with the server")]
  in
  close_on_fail (with_timeout (
    peekWithBlocking conn.inputBuffer >>= fun _ ->
    connReady (); Lwt.return () >>= fun () -> (* Connection working, notify *)
    checkHeader conn >>=
    checkServerUpgrade conn >>=
    checkServerVersion conn)) >>= fun () ->
  registerConnCleanup conn cleanup;
[...]

I can see that the process gets a SIGTERM.

But at least in this case, I think that unison is correct, and that my
wrapper should catch the signal and kill its child process if there is
one. And it could do that for SIGINT too (original issue).

In short, it may not be worth to change unison.

#1052451#45
Date:
2024-06-28 13:59:16 UTC
From:
To:
[...]

After thinking more about this... The current status is that unison
does not create its own process group. This is not an issue when it
is run directly from a shell, because the shell itself creates a
process group for the command (e.g. unison). But, like in my case,
if unison is started by a process that also deals with the terminal,
one may have problems.

The whole process group normally receives the SIGINT due to the
Ctrl-C, which may kill an ancestor of unison. But ssh and its
descendants do not receive it because unison has set it to be
ignored. And unison has trapped it for the cleanup.

In the case

  Shell → command → unison

the command and unison both receive the SIGINT. If the command
traps the SIGINT to wait for unison, no problems. But if it is
killed by SIGINT, the shell will start again to read from the
terminal. The following case is similar:

  Shell → A → B → unison

where B is killed by the SIGINT and A traps it and starts again
to read from the terminal. As an example, in my case:

  ... → Mutt → wrapper → unison

Now, the issue with the above situation is if unison or one of its
descendants reads from the terminal after the SIGINT, so that there
will be a fight between this process and the ancestor. It appears
that unison does not read from the terminal, but ssh or one of its
descendants (in my case, ssh-add via a wrapper) might do so.

Currently, there isn't any issue with ssh alone, but I'm wondering
whether this will always be the case in the future.

I had suggested "when receiving a SIGINT, unison should send it to
the process group, not just to ssh", but now, I do not think that
this will necessarily solve the problem (it could just mitigate the
issue, and perhaps not in all cases). One point is that the signal
is not sent to ssh until unison has completed the cleanup. So there
will be at least a race condition.

So I can see 2 solutions:

1. Fix outside of unison: Every command involved must trap the signals
and wait for their child to terminate.

2. Fixes in unison:
  A. Make unison create its own process group (but is this OK?),
     so that the Ctrl-C doesn't affect the ancestors.
  B. Make unison send the signal to the process group, just as if
     SIGINT were not ignored.

#1052451#50
Date:
2024-06-28 14:26:28 UTC
From:
To:
Actually since this wrapper exec's unison, this gives

  ... → Mutt → unison

So, in my case, the issue only concerns the descendants of unison
(in particular, the ssh wrapper).