03.29.10

Kill That Hanging SSH!

EDIT: The scipt below was updated on 2010-04-08 to identify the
correct xterm-ssh pair based on the X window id.

I spend a lot of time working remotely, ssh-ing to hosts from places
with bad wireless connection or glitchy firewalls. When an ssh session
hangs, it is bad enough. Trying to locate one dead ssh amid a dozen
live ones is tedious. It grows into a major headache if you need to do
that several times a hour. So, I have a dead ssh in an xterm
window, how do I kill it? Moreover, how do I automate the process?

Luckily, most applications nowadays export the process id, controlling a
particular X window, as a property of that window and xprop is a tool
that can help us query that.

Run xprop as follows and click on the window of interest:

arteme@book:~$ xprop _NET_WM_PID
_NET_WM_PID(CARDINAL) = 17843

Knowing the process id of the terminal we can track down the ssh process
that runs within:

arteme@book:~$ pstree -Ap 17843
xterm(17843)---bash(17889)---ssh(21687)

There’s a process id that can be killed.

Half the work is done and it seems that it can be easily rolled into a shell
script, but if you did, you would notice that if you started an xterm within
an xterm and an ssh within that, pstree output would look like:

arteme@book:~$ pstree -Alp 3443
xterm(3443)---bash(3447)---xterm(3705)---bash(3706)---ssh(9612)

If you start the second xterm in background and start ssh in both of them,
the output becomes even more chaotic:

arteme@book:~$ pstree -Alp 3444
xterm(3444)---bash(3446)-+-ssh(12549)
                         `-xterm(12508)---bash(12509)---ssh(12540)

So, what to kill?

Luckily, you can trace the ssh process back to the terminal window it is
running in by it’s window id. Using xprop we can find out the window id
of the terminal, which is passed in the WINDOWID variable to the
processes started within. So, for the two xterms above with their
ssh:

arteme@book:~$ xprop -f WM_CLIENT_LEADER '32c' ' = $0\n' WM_CLIENT_LEADER
WM_CLIENT_LEADER(WINDOW) = 79691810
arteme@book:~$ cat /proc/12549/environ | tr '\0' '\n' | grep WINDOWID
WINDOWID=79691810

and:

arteme@book:~$ xprop -f WM_CLIENT_LEADER '32c' ' = $0\n' WM_CLIENT_LEADER
WM_CLIENT_LEADER(WINDOW) = 77594658
arteme@book:~$ cat /proc/12540/environ | tr '\0' '\n' | grep WINDOWID
WINDOWID=77594658

By default, “xprop” will return the window id as in hex, so the “-f …”
argument is necessary to override that and ease the comparison, since the
“WINDOWID” environment variable is in decimal.

Note, however, that if you run screen with multiple ssh instances within,
the environment of those ssh processes contains the WINDOWID of the
original xterm they were started in, if any. That information is not reliable
as it if you detached from the screen in one xterm and attached to it in
another one, the WINDOWID will no longer be correct. Luckily, one can
identify a screen session by the presence of the STY variable in the
environment…

Now this all can be rolled into a shell script:

#!/bin/bash

# Get the X terminal window properties and extract window id and process id
prop=`xprop -notype -f WM_CLIENT_LEADER '32c' ' = $0\n' \
            WM_CLIENT_LEADER \
            _NET_WM_PID`

wid=`echo $prop | sed -n 's/.*WM_CLIENT_LEADER = \([0-9]\{1,\}\).*/\1/p'`
pid=`echo $prop | sed -n 's/.*NET_WM_PID = \([0-9]\{1,\}\).*/\1/p'`

# Exit if any of the data was missing
[ -z "$wid" -o -z "$pid" ] && exit

# Get the process tree and extract the ssh process id from there
ssh=`pstree -Apl $pid | sed -n 's/.*ssh(\([0-9]\{1,\}\))/\1/p'`

# Filter ssh windows so that they do not belong to a screen session and
# do run in the X terminal the user clicked on
for i in $ssh; do
    sty=`cat /proc/$i/environ | tr '\0' '\n' | grep '^STY'`
    window=`cat /proc/$i/environ | tr '\0' '\n' |\
            sed -n 's/WINDOWID=\(.*\)/\1/p'`

    [ -n "$sty" ] && continue
    [ "$wid" != "$window" ] && continue

    tokill="$tokill $i"
done

# Exit if there's nothing to kill
[ -z "$tokill" ] && exit

# Kill the process
kill -9 $tokill

Grab that as a bash script, kill_that_ssh.sh. Furthermore, you can add it
to your quick-launch bar to never have to let go of your mouse when reaching
for the tool:

http://g5.arteme.fi/wordpress/wp-content/uploads/2010/03/kill-that-ssh.png

Tags: ,
| Posted in blog | No Comments »
04.26.07

Subversion-only SSH users

Sometimes you’re stuck with svn+ssh://… access for your repository. You need to give someone SSH access to access the repository, but you don’t want to give them full shell access. What you can do is create a group, whose users will only have access to svnserve.

Create a group called “svnonly”. /etc/groups will look something like:

...
svnonly:x:414
...

Then you can add users into that group. /etc/passwd will look something like:

...
john:x:1001:414::/home/svnonly/john:/bin/bash
mike:x:1002:414::/home/svnonly/mike:/bin/bash
bob:x:1003:414::/home/svnonly/bob:/bin/bash
...

The home directory is entirely up to you – I choose /home/svnonly as a top directory for svnonly-users to keep them separate from full-access users and to keep /home neat. Yet, each can have their own home directory to have .ssh/authorized_keys, for example.

Next, add the following to your /etc/ssh/sshd_config:

Match Group svnonly
        X11Forwarding no
        AllowTcpForwarding no
        ForceCommand svnserve -t

Now svnonly-users will have svnserve started for them automatically when they connect instead of a normal shell.

Tags: ,
| Posted in blog | No Comments »