7

I'd like to use ssh's ControlMaster feature to share connections for speed increases. I'm trying to script it so that I can start/restart/stop a number of connections to different hosts.

How can I determine whether any of these connections are in use? If I kill them when an ssh session is open, it gets closed

My restart script would ideally look like (pseudo-script) - the stop script would be equivalent without the ssh command at the bottom:

for HOST in $HOST_LIST
  do
    MASTER_PID=`find_master_pid $HOST`
    if $MASTER_PID
      then
        if `find_child_pid`
          echo Connection to $HOST in use: not terminating
        else
          kill -SIGHUP $MASTER_PID
        fi
    ssh -TMNf $HOST

4 Answers 4

9

You could simply use

ssh -o ControlPath=$socket -O check 'unused-hostname'

for each $socket you've open (easy if you keep them in a single directory).

This returns 255 if the check fails (connection not active anymore), an other value if it pass. You may need to specify the hostname too, but nothing that an awk on $socket won't give you :)

3
  • That's much simpler for seeing if the existing connection is open; it doesn't tell me if it is actually being used, but I guess that isn't so important... (and I have the method below) Dec 10, 2010 at 12:24
  • 3
    I found I had to pass some argument at the end, otherwise I got an error. ssh -o ControlPath=8dcbe9e120884b5f25709f25ef2f0863ac3301d4 -O check *""*``` Master running (pid=25512)``` but without it, it replies with usage; usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] (this is on fedora f27 (OpenSSH_7.6p1, OpenSSL 1.1.0g-fips 2 Nov 2017)
    – Tom
    Jan 30, 2018 at 23:18
  • The problem with this is it will hang forever in some cases, even with ConnectTimeout options. For example, if I have a control socket "open" to some_host over a network route that no longer exists, ssh -o ConnectTimeout=5 -O check some_host hangs forever. OpenSSH itself appears to have no way to detect/recover from this: I have to use timeout 5 ssh -O check some_host.
    – Dan Lenski
    Oct 5, 2021 at 16:04
3

@Renik's answer didn't work for me. See below for what did.


This works for me using just the socket file for the control master:

$ ssh -o ControlPath=~/.ssh/<controlfile> -O check <bogus arg>

NOTE: You can also use ssh -S ~/.ssh/<controlfile> ... as well, which is a bit shorter form of the above.

Example

Here's an example where I've already established a connection to a remote server:

$ ssh -S ~/.ssh/master-57db26a0499dfd881986e23a2e4dd5c5c63e26c2 -O check blah
Master running (pid=89228)
$

And with it disconnected:

$ ssh -S ~/.ssh/master-66496a62823573e4760469df70e57ce4c15afd74 -O check blah
Control socket connect(/Users/user1/.ssh/master-66496a62823573e4760469df70e57ce4c15afd74): No such file or directory
$

If it were still connected, this would force it to exit immediately:

$ ssh -S ~/.ssh/master-66496a62823573e4760469df70e57ce4c15afd74 -O exit blah
Exit request sent.
$

It's unclear to me, but it would appear to potentially be a bug in ssh that it requires an additional argument at the end, even though blah is meaningless in the context of the switches I'm using.

Without it gives me this:

$ ssh -S ~/.ssh/master-57db26a0499dfd881986e23a2e4dd5c5c63e26c2 -O check
usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
           [-D [bind_address:]port] [-E log_file] [-e escape_char]
           [-F configfile] [-I pkcs11] [-i identity_file]
           [-L [bind_address:]port:host:hostport] [-l login_name] [-m mac_spec]
           [-O ctl_cmd] [-o option] [-p port]
           [-Q cipher | cipher-auth | mac | kex | key]
           [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port]
           [-w local_tun[:remote_tun]] [user@]hostname [command]

Version info

OSX
$ ssh -V
OpenSSH_6.9p1, LibreSSL 2.1.8
CentOS 7.x
$ ssh -V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017

I confirmed that on both of these versions, the need for the additional bogus argument was required.

References

0
1

I wrote a utility that eases management of SSH ControlMaster connections. It's called cmc: TimidRobot/cmc: ControlMaster Controller - Eases management of SSH ControlMaster connections.

0

My current approach is to use lsof to find the Unix sockets. If I know the target filename specified by ControlPath I can search for the master process like this:

function find_master_pid() {
  lsof -a -U -u $USER -c ssh | grep $CM_DIR/$RUSER@$HOST | sed 's/^ssh *//' | cut -d' ' -f1 | sort -u
}

This will then give me the PID (unfortunately, giving the filename of the unix socket file directly to lsof doesn't work otherwise I could just ask it to output me the pid with -f; hence the sed and cut)

Searching for the child process is more tricky. The master process opens a new socket for each child process, connected to the standard file. The child process contains a socket simply marked socket. However, the inode returned by lsof is one less than the corresponding master process socket. So the following finds child processes that are connected to the master process (this needs the $MASTER_PID to check against)

function find_child_pid() {
  for CHILD_PID in `lsof -a -U -u $USER -c ssh | grep socket | sed 's/^ssh *//' | cut -d' ' -f1 | sort -u`
    do
      CHECK_PID=0
      for NODE in `lsof -a -U -u $USER -p $CHILD_PID -F i | grep ^i | sed 's/i//'`
        do
          MASTER_NODE=$((NODE+1))
          CHECK_PID=`lsof -a -U -u $USER -p $MASTER_PID | grep $MASTER_NODE | wc -l`
          [[ $CHECK_PID == 1 ]] && echo $CHILD_PID
        done
    done
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .