How do I kill a backgrounded/detached ssh session?

Viewed 92112

I am using the program synergy together with an ssh tunnel

It works, i just have to open an console an type these two commands:

ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
synergyc localhost

because im lazy i made an Bash-Script which is run with one mouseclick on an icon:

#!/bin/bash
ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
synergyc localhost

the Bash-Script above works as well, but now i also want to kill synergy and the ssh tunnel via one mouseclick, so i have to save the PIDs of synergy and ssh into file to kill them later:

#!/bin/bash

mkdir -p /tmp/synergyPIDs || exit 1
rm -f /tmp/synergyPIDs/ssh || exit 1
rm -f /tmp/synergyPIDs/synergy || exit 1

[ ! -e /tmp/synergyPIDs/ssh ] || exit 1
[ ! -e /tmp/synergyPIDs/synergy ] || exit 1

ssh -f -N -L localhost:12345:otherHost:12345 otherUser@OtherHost
echo $! > /tmp/synergyPIDs/ssh
synergyc localhost
echo $! > /tmp/synergyPIDs/synergy

But the files of this script are empty.

How do I get the PIDs of ssh and synergy?
(I try to avoid ps aux | grep ... | awk ... | sed ... combinations, there has to be an easier way.)

11 Answers

You could look out for the ssh proceess that is bound to your local port, using this line:

netstat -tpln | grep 127\.0\.0\.1:12345 | awk '{print $7}' | sed 's#/.*##'

It returns the PID of the process using port 12345/TCP on localhost. So you don't have to filter all ssh results from ps.

If you just need to check, if that port is bound, use:

netstat -tln | grep 127\.0\.0\.1:12345 >/dev/null 2>&1

Returns 1 if none bound or 0 if someone is listening to this port.

There are many interesting answers here, but nobody mentioned that the manpage of SSH does describe this exact case! (see TCP FORWARDING section). And the solution they offer is much simpler:

ssh -fL 12345:localhost:12345 user@remoteserver sleep 10
synergyc localhost

Now in details:

  1. First we start SSH with a tunnel; thanks to -f it will initiate the connection and only then fork to background (unlike solutions with ssh ... &; pid=$! where ssh is sent to background and next command is executed before the tunnel is created). On the remote machine it will run sleep 10 which will wait 10 seconds and then end.
  2. Within 10 seconds, we should start our desired command, in this case synergyc localhost. It will connect to the tunnel and SSH will then know that the tunnel is in use.
  3. After 10 seconds pass, sleep 10 command will finish. But the tunnel is still in use by synergyc, so SSH will not close the underlying connection until the tunnel is released (i.e. until synergyc closes socket).
  4. When synergyc is closed, it will release the tunnel, and SSH in turn will terminate itself, closing a connection.

The only downside of this approach is that if the program we use will close and re-open connection for some reason then SSH will close the tunnel right after connection is closed, and the program won't be able to reconnect. If this is an issue then you should use an approach described in @doak's answer which uses control socket to properly terminate SSH connection and uses -f to make sure tunnel is created when SSH forks to the background.

Related