#! /bin/bash ### ### SSH wrapper to spawn separate SSH master connections on demand ### ### (c) 2010 Mark Wooding ### ###----- Licensing notice --------------------------------------------------- ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation; either version 2 of the License, or ### (at your option) any later version. ### ### This program is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with this program; if not, write to the Free Software Foundation, ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ###-------------------------------------------------------------------------- ### Configuration. : ${REAL_SSH=/usr/bin/ssh} : ${SSH_HOME=$(unset HOME; bash -c 'echo ~/.ssh')} : ${SSH_LOGDIR=$SSH_HOME/log-$(hostname)} ###-------------------------------------------------------------------------- ### Parse the command line and dredge out information. ## This is a reasonable approximation to SSH's command-line argument parser. unset host login mode=m declare -a opts masteropts while :; do case "$1,$#" in ## Nothing left. We're done. ,0) break ;; ## A command line option group. Parse it and work out what's going on. -*) opt=$1 opts=("${opts[@]}" "$opt") while :; do ## Strip off the first character, because it's the one we dealt with ## last time. opt=${opt#?} o=$opt ## Phase 1: identify the option and whether it needs an argument. unset arg case "$o" in ## Empty group. We're done. "") break ;; ## Options with arguments. [bcDeFiLlmOopRSw]*) case "$#,$o" in ## You're going to lose because there's no argument. But we'll ## let SSH deal with that. 1,?) arg= ;; ## There's an argument cuddled on to the end of the option. It ## will have been committed to the `opts' array as part of the ## option group. *,??*) arg=${opt#?} opt= ;; ## Nope. There's an argument in the next word. Grab it and ## commit it. *) arg="$2" opts=("${opts[@]}" "$arg") shift ;; esac ;; ## Anything else. Let it go even if it's not valid: SSH will moan ## if it wants. Note that `--' is ignored by SSH, but this isn't ## documented; in particular, `--' is /not/ a POSIX end-of-options ## marker, so we don't try to handle it specially here either. *) ;; esac ## Phase two. Figure out whether what this means for us. case "$mode,$o" in ## `-O foo' and `-S foo' mean that the caller wants to take ## control of the multiplexing process. ?,[MOS]*) mode=p ;; ## Catch the login name if there is one. Make sure the master ## knows it. ?,l*) masteropts=("${masteropts[@]}" "-l$arg") login=$arg ;; ## Catch the port number. We want this for building the logfile ## name. ?,p*) masteropts=("${masteropts[@]}" "-p$arg") port=$arg ;; ## These options are interesting to the master connection. m,[aADLlRSwxXv46]*) masteropts=("${masteropts[@]}" "-${o:0:1}$arg") ;; ## SSH options on the command line merit special attention. Pass ## them onto the master, if necessary. ?,o*) masteropts=("${masteropts[@]}" "${opt:0:1}$arg") case "$mode,$arg" in ## User wants to control the process. Let him. ?,ControlMaster=* | ?,ControlPath=*) mode=p ;; esac ;; esac done ;; ## A bare word. Maybe it's the hostname, or the start of the command. *) case ${host-t}${host+nil} in t) host=$1 ;; *) break ;; esac ;; esac shift done ###-------------------------------------------------------------------------- ### Now to actually do the job. ## If there's no host, pass straight through. We can't do anything useful ## anyway. case ${host+t} in t) ;; *) mode=p ;; esac ## Actually do something useful. case "$mode" in p) exec "$REAL_SSH" "${opts[@]}" ${host+"$host"} "$@" ;; m) mkdir -p -m700 "$SSH_LOGDIR" logfile=$SSH_LOGDIR/${login+"$login@"}"$host"${port+":$port"}.log if ! "$REAL_SSH" -Ocheck \ ${port+-p$port} ${login+"$login@"}"$host" \ >/dev/null 2>&1 then "$REAL_SSH" -MNf "${masteropts[@]}" "$host" \ /dev/null 2>$logfile fi exec "$REAL_SSH" ${opts[@]} "$host" "$@" ;; esac ###----- That's all, folks --------------------------------------------------