> 
> From emacs without any special config (/bin/sh -i showing as the process 
> name): sending SIGUSR2 from emacs *actually works*!  I'm not sure how it is 
> passing a keyless signal over the SSH connection, but I have confirmed that 
> it is.  

OK, progress.  I now understand that TRAMP intercepts calls to signal-process 
in `tramp-signal-process', and in that function, it simply sends, e.g., `kill 
-USR2 PID` to the remote PID, via SSH.  Similarly with `interrupt-process'.  So 
far, so good.

However, when `direct-async' is configured for a process, TRAMP for some reason 
does not have access to the remote PID: its 'remote-pid PLIST entry is missing. 
 This means it cannot send signals or interrupts in that manner.  If you 
attempt to signal or interrupt a direct-async process, TRAMP will silently pass 
that along to Emacs internal code.  Emacs then delivers the signal to the 
client SSH process itself, killing it.

One way around all this I've found: setup `direct-async' as we discussed (full, 
no "-t -t"), then, upon startup, collect the PID from the process itself (e.g., 
in python: import os; os.getpid()).  Set this PID into the process' plist as 
'remote-pid.

Good news: this actually works well for signals!  Unfortunately 
`tramp-interrupt-process' causes problems here, due to this unusual bit at the 
end:

;; Wait, until the process has disappeared.  If it doesn't,
;; fall back to the default implementation.
(while (tramp-accept-process-output proc))
(not (process-live-p proc)))))

I must admit I don't understand this code.  If the process remains alive after 
the interruption is sent, the "default implementation" is subsequently 
employed, which, as mentioned above, kills the SSH process itself.  Long-lived 
processes should _not_ necessarily exit upon interrupt.  Many of them, e.g. the 
Python REPL, specifically catch SIGINT and use that to interrupt their own 
running commands.

I wonder if this behavior can be gated behind a custom variable, like 
`tramp-interrupt-ends-process'.  So something like:

;; If we expect interrupt to end the process, wait until it
;; has disappeared.  If it doesn't, fall back to the default
;; implementation.
(if tramp-interrupt-ends-process
    (progn (while (tramp-accept-process-output proc))
           (not (process-live-p proc)))
  t))))) ; handled

Reply via email to