+#! /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}
+
+###--------------------------------------------------------------------------
+### 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
+ ;;
+
+ ## These options are interesting to the master connection.
+ m,[aADLlRSwxXv]*)
+ 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} 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)
+ if ! "$REAL_SSH" -Ocheck ${login+"$login@"}"$host" >/dev/null 2>&1; then
+ "$REAL_SSH" -MNf "${masteropts[@]}" "$host"
+ fi
+ exec "$REAL_SSH" ${opts[@]} "$host" "$@"
+ ;;
+esac
+
+###----- That's all, folks --------------------------------------------------