How to Send Sigint (Ctrl-C) to Current Remote Process Over Ssh (Without -T Option)

How to send SIGINT (Ctrl-C) to current remote process over SSH (without -t option)

If you use ssh to start a process without a PTY on a remote system, then as far as I can tell, there's no way to signal the remote process through that ssh session.

The SSH protocol has a message to send a signal to the remote process. However, you're probably using OpenSSH for either the client or the server or both, and OpenSSH doesn't implement the signal message. So the OpenSSH client can't send the message, and the OpenSSH server won't act on it.

There is an SSH extension to send a "break" message which is supported by OpenSSH. In an interactive session, the OpenSSH client has an escape sequence that you can type to send a break to the server. The OpenSSH server handles break messages by sending a break to the PTY for the remote session, and unix PTYs will normally treat a break as a SIGINT. However, breaks are fundamentally a TTY concept, and none of this will work for remote sessions which don't have a PTY.

How to send SIGINT to a remote process over SSH?



$ ssh -t foo.bar.com gdb
...
(gdb) ^C
Quit

Forward Ctrl+C &c. over ssh -tt running w/ command from heredoc

I think you just need the -t flag and not use the heredoc. Using the heredoc means that the ssh process doesn't have the terminal as its stdin (it has the heredoc instead) so it can't forward it to pseudo-terminal on the remote side. Using the -tt flag forces the pts to be allocated without having an input which means that keypresses go to the local process not the remote one.

#!/bin/bash
ssh $1@$2 -t tmux a

works for me

Starting a process over ssh using bash and then killing it on sigint

It would definitely be preferable to keep your cleanup managed by the ssh that starts the process rather than moving in for the kill with a second ssh session later on.

When ssh is attached to your terminal; it behaves quite well. However, detach it from your terminal and it becomes (as you've noticed) a pain to signal or manage remote processes. You can shut down the link, but not the remote processes.

That leaves you with one option: Use the link as a way for the remote process to get notified that it needs to shut down. The cleanest way to do this is by using blocking I/O. Make the remote read input from ssh and when you want the process to shut down; send it some data so that the remote's reading operation unblocks and it can proceed with the cleanup:

command & read; kill $!

This is what we would want to run on the remote. We invoke our command that we want to run remotely; we read a line of text (blocks until we receive one) and when we're done, signal the command to terminate.

To send the signal from our local script to the remote, all we need to do now is send it a line of text. Unfortunately, Bash does not give you a lot of good options, here. At least, not if you want to be compatible with bash < 4.0.

With bash 4 we can use co-processes:

coproc ssh user@host 'command & read; kill $!'
trap 'echo >&"${COPROC[1]}"' EXIT
...

Now, when the local script exits (don't trap on INT, TERM, etc. Just EXIT) it sends a new line to the file in the second element of the COPROC array. That file is a pipe which is connected to ssh's stdin, effectively routing our line to ssh. The remote command reads the line, ends the read and kills the command.

Before bash 4 things get a bit harder since we don't have co-processes. In that case, we need to do the piping ourselves:

mkfifo /tmp/mysshcommand
ssh user@host 'command & read; kill $!' < /tmp/mysshcommand &
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT

This should work in pretty much any bash version.

Send Ctrl-C to remote processes started via subprocess.Popen and ssh

The only way I was able to successfully kill all of my child processes was by using pexpect:

a = pexpect.spawn(['ssh', 'remote-host', './script.sh', 'a'])
a.expect('something')

b = pexpect.spawn(['ssh', 'remote-host', './script.sh', 'b'])
b.expect('something else')

# ...

# to kill ALL of the children
a.sendcontrol('c')
a.close()

b.sendcontrol('c')
b.close()

This is reliable enough. I believe someone else posted this answer earlier, but then deleted the answer, so I will post it in case someone else is curious.



Related Topics



Leave a reply



Submit