#! /bin/sh # shell script "fsa" (find ssh-agent) # Steve Allen # original incarnation 1998-11-17 # reworked for OpenSSH during 2001 # this script will typically be invoked as # eval `fsa ssh_key_comment` # The PATH must include ssh-add, and should include ssh-agent usage () { echo "Usage: $0 [-s] [-c] [-f] ssh_key_comment" 1>&2 exit 1 } semantics="" narg=0 for arg in "$@"; do case "$arg" in -s) # had to add this because some helpful person recently made # OpenSSH ssh-agent sensitive to the SHELL envar. semantics="-s" ;; -c) semantics="-c" ;; -f) STARTSSHAGENT=1 export STARTSSHAGENT ;; *) if [ $narg -gt 0 ]; then echo "too many args" 1>&2 usage exit 1 else narg=1 keycmnt=$arg fi ;; esac done if [ $narg -eq 0 ]; then echo "too few args" 1>&2 usage exit 1 fi ######################################################################## # This shell function searches for an ssh-agent process which holds # a key whose comment contains the regexp given as its first argument. # It can be used from a cron job which is following the suggestion in # the appendix of http://www.ucolick.org/~sla/ssh/sshcron.html # Its effective use obviously presumes that the comment of the key # is unique among all those which LOGNAME might be running. findsshagent() { # the only argument we expect is the key comment/name # trim any pathname elements that may be present on a file name thekey=`echo $1 | sed "s%.*/\([a-z][0-9a-z]*@[^ ]*\)%\1%"` # the next line was the old way that worked with ssh1 #agentsocks=`ls /tmp/ssh-$LOGNAME/ssh-[0-9]*-agent /tmp/ssh-$LOGNAME/agent-socket-[0-9]* 2>/dev/null` # the next line is the current way that works with ssh1 and OpenSSH agentsocks=`find /tmp -user $LOGNAME -type s -name "*agent*" -print 2>/dev/null` howmany=`echo $agentsocks | wc -w` if [ $howmany -eq 0 ]; then reason="could not find any ssh-agent socket" unset SSH_AUTH_SOCK else lastlive="" for i in $agentsocks; do #echo "checking for agent at $i" 1>&2 SSH_AUTH_SOCK=$i; export SSH_AUTH_SOCK # throwing error to devnull means we can't be sure # whether ssh-add has error or our shell can't find ssh-add # but message is confusing pubkey=`ssh-add -l 2>/dev/null` status=$? if [ $status -eq 0 ]; then # there is a live agent at socket $i #echo "agent at $i holds keys" 1>&2 echo "$pubkey" 1>&2 lastlive=$SSH_AUTH_SOCK echo "$pubkey" | egrep "$thekey" >/dev/null case $? in 0) # found matching key, exit loop hoping it's right break ;; 2) # this should never happen echo "$i gives bad status from grep" 1>&2 ;; *) # confusing to see this message if another holds key # but still worth bothering telling you so echo "$i does not hold matching key" 1>&2 ;; esac elif [ $status -eq 2 ]; then # an error running ssh-add against agent at $i # ssh-add man page says unable to contact agent # presume it is a leftover socket needing removal # but it may be a live agent socket but we can't see ssh-add echo "status $status: removing dead agent socket $i" 1>&2 /bin/rm -rf `dirname $i` elif [ $status -eq 1 ]; then # an error running ssh-add against agent at $i # ssh-add man page says specified command failed # but it may be a live agent socket but we can't see ssh-add echo "status $status for ssh-add with agent socket $i" 1>&2 else # an error running ssh-add against agent at $i # this error status is not documented by ssh-add man page # presume we can't find ssh-add # but it may be a dead agent socket echo "status $status for ssh-add with agent socket $i" 1>&2 fi # unless we break from this for loop we have not succeeded unset SSH_AUTH_SOCK done if [ x"$SSH_AUTH_SOCK" = x ]; then # we have not succeeded, try to explain why if [ x"$lastlive" = x ]; then reason="could not find live ssh-agent" else reason="could not find ssh-agent with key $thekey" unset lastlive fi else # we have succeeded unset reason fi fi # At this point we know whether there is an agent, and whether it # holds the requested key. if [ x"$reason" != x ]; then # We did not find an ssh-agent which holds the requested key. # yes, this next test is a bit Linux-specific if [ "`tty`" != "not a tty" ]; then # This is an interactive job. STARTSSHAGENT=1 fi if [ x"$STARTSSHAGENT" = x1 ]; then # We have been told to start an agent echo "$reason" 1>&2 if [ x"$lastlive" = x ]; then # We did not find any useable live ssh-agent. # Let this subshell become an ssh-agent. # Presuming that this procedure was called by another shell # using an eval, the ssh-agent will echo its environment # settings so that the caller can use them. echo "A new ssh-agent is being started" 1>&2 echo "You should now use ssh-add to give it the $thekey key" 1>&2 exec ssh-agent $semantics else # We did find at least one useable live ssh-agent. # Set things up so that it can be echoed to the eval # of the calling shell. echo "$lastlive is a live ssh-agent that should work" 1>&2 SSH_AUTH_SOCK=$lastlive fi else # We have not been told to start an agent. # Send mail to myself telling me why the job failed. message="job titled $0 on `hostname` failed because $reason Suggest $LOGNAME login there and create an agent authorized with key $thekey When logged in the agent can be started by interactively typing eval \`ssh-agent $semantics\` and then by typing ssh-add " echo "$message" 1>&2 echo "$message" | mail -s "cannot find agent" $LOGNAME >/dev/null 2>&1 # Presuming that this has been called from another shell script # using eval, respond to cause the calling shell to terminate. echo "exit 1;" # And terminate the shell executing this script. exit 1 fi fi } ######################################################################## findsshagent "$keycmnt" unset STARTSSHAGENT if [ x"$semantics" = x"-c" ]; then # csh-like shell echo "setenv SSH_AUTH_SOCK $SSH_AUTH_SOCK;" else # Bourne-like shell echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK; export SSH_AUTH_SOCK;" fi