chiark / gitweb /
hacks/ssh: Add new hack for setting up background SSH master connections.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 19 Dec 2010 03:14:17 +0000 (03:14 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 19 Dec 2010 03:14:17 +0000 (03:14 +0000)
I don't really know why SSH doesn't do this by default; but it damned
well ought to.

hacks/ssh [new file with mode: 0755]
setup

diff --git a/hacks/ssh b/hacks/ssh
new file mode 100755 (executable)
index 0000000..a11c777
--- /dev/null
+++ b/hacks/ssh
@@ -0,0 +1,172 @@
+#! /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 --------------------------------------------------
diff --git a/setup b/setup
index 76b95dbaaa8f8e00e8c21ed8c00a9b314639a194..5e55c9b22f7df7868ea43d3e3202ea4948ca00f7 100755 (executable)
--- a/setup
+++ b/setup
@@ -248,6 +248,19 @@ for s in $scripts; do
 done
 echo " all done."
 
 done
 echo " all done."
 
+hacks="
+  ssh:ssh"
+echo "Installing hacks..."
+for h in $hacks; do
+  d=${h%%:*} h=${h#*:}
+  ft=$HOME$sub/bin/hacks/$d
+  mkdir -p $ft
+  ln -s $here/hacks/$h $ft/$h.new
+  mv $ft/$h.new $ft/$h
+  echo "  $d:$h"
+done
+echo " all done."
+
 ###--------------------------------------------------------------------------
 ### Set up the Emacs config.
 
 ###--------------------------------------------------------------------------
 ### Set up the Emacs config.