There are basically two modes in which ssh
can operate: 1) interactive, 2) non-interactive. These are not official terms. I'm using them here in place of otherwise wordy names like, "an ssh session with an allocated pseudo terminal." To run a command in non-interactive mode you do:
$ ssh host echo test
Interactive mode is entered when you either do:
$ ssh host -t echo test
Or:
$ ssh host
In non-interactive mode sshd
forks the requested process (echo test
in this case), and interacts with it via a pipe. In interactive mode, it does so via a pseudo terminal (that's what -t
is there for). In interactive mode you interact with a remote process. That is, when you press Ctrl-C
it's transmitted to the remote machine, and is passed to the program that is running remotely. In non-interactive mode Ctrl-C
would kill your ssh
client. In interactive mode some key sequences are treated specially, like ~.
to close the connection. Particularly, when you do ssh host -t some command
. Also, do note that interactive mode has some side effects, like converting \n
to \r\n
, or combining stdout
and stderr
.
In non-interactive mode, when you disconnect, the command remains running, e.g.:
$ ssh host sleep 100
But if it tries to write to stdout
or stderr
it would receive SIGPIPE
, and die. You can confirm it this way:
$ ssh host 'for i in {1..100}; do echo $i; sleep 1; done'
Attach strace
to the corresponding bash
process in another console (strace -p PID
). Then Ctrl-C
the ssh
command, and you'll see the following in the console with strace
:
+++ killed by SIGPIPE +++
SIGPIPE
is sent to processes that try to write to a pipe with no readers. Otherwise commands like yes | head
would never terminate.
Let's inspect more closely what happens when you disconnect. Does the remote process die or not?
$ ssh host sleep 100
// disconnect
The sleep
process remains.
$ ssh host -t sleep 100
// disconnect
The sleep
process receives SIGHUP
and dies.
$ ssh host 'sleep 100 &'
// disconnect
The sleep
process remains.
$ ssh host -t 'sleep 100 &'
In this case ssh
doesn't wait for sleep
to finish, and disconnects. The sleep
process is supposedly killed by SIGHUP
.
$ ssh host -t 'sleep 100 & wait'
// disconnect
In this case sshd
forks a bash
process that executes the command above. When you disconnect, the bash
process receives SIGHUP
, which in its turn sends SIGHUP
to its children.
$ ssh host
# sleep 100 &
# exit // or Ctrl-D
The sleep
process remains. Because when you exit normally, by default bash
doesn't sends SIGHUP
to background jobs.
$ ssh host
# shopt -s huponexit // set huponexit option
# sleep 100 &
# exit // or Ctrl-D
The sleep
process receives SIGHUP
.
$ ssh host
# sleep 100 &
// disconnect
The bash
and sleep
processes receive SIGHUP
.
To sum it up, SIGHUP
is sent to interactive sessions when you get disconnected. What happens when you exit normally depends on huponexit
setting.
On a side note, what happens when you deploy using mina
, you might ask. mina
turns your deploy.rb
into a shell script and executes ssh host -tt -- script
. You can confirm it by running bundle exec mina deploy -s
. At the beginning you'll see:
# Executing the following via 'ssh user@host -p port -tt':
So, in case you want to run a server of sorts, you've got to use nohup
, or disown
, or some such.
Further reading:
The TTY demystified
Using pseudo-terminals (pty) to control interactive programs
How to terminate remotely called “tail -f” when connection is closed?
What happens to a continuing operation if we do ssh and then disconnect?
Why doesn't SSH -t wait for background processes?
If I launch a background process and then log out, will it continue to run?