chiark / gitweb /
Merge branch 'master' of vampire:etc/profile
[profile] / hacks / ssh
1 #! /bin/bash
2 ###
3 ### SSH wrapper to spawn separate SSH master connections on demand
4 ###
5 ### (c) 2010 Mark Wooding
6 ###
7
8 ###----- Licensing notice ---------------------------------------------------
9 ###
10 ### This program is free software; you can redistribute it and/or modify
11 ### it under the terms of the GNU General Public License as published by
12 ### the Free Software Foundation; either version 2 of the License, or
13 ### (at your option) any later version.
14 ###
15 ### This program is distributed in the hope that it will be useful,
16 ### but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ### GNU General Public License for more details.
19 ###
20 ### You should have received a copy of the GNU General Public License
21 ### along with this program; if not, write to the Free Software Foundation,
22 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 ###--------------------------------------------------------------------------
25 ### Configuration.
26
27 : ${REAL_SSH=/usr/bin/ssh}
28
29 ###--------------------------------------------------------------------------
30 ### Parse the command line and dredge out information.
31
32 ## This is a reasonable approximation to SSH's command-line argument parser.
33 unset host login
34 mode=m
35 declare -a opts masteropts
36 while :; do
37   case "$1,$#" in
38
39     ## Nothing left.  We're done.
40     ,0)
41       break
42       ;;
43
44     ## A command line option group.  Parse it and work out what's going on.
45     -*)
46       opt=$1
47       opts=("${opts[@]}" "$opt")
48       while :; do
49
50         ## Strip off the first character, because it's the one we dealt with
51         ## last time.
52         opt=${opt#?}
53         o=$opt
54
55         ## Phase 1: identify the option and whether it needs an argument.
56         unset arg
57         case "$o" in
58
59           ## Empty group.  We're done.
60           "")
61             break
62             ;;
63
64           ## Options with arguments.
65           [bcDeFiLlmOopRSw]*)
66             case "$#,$o" in
67
68               ## You're going to lose because there's no argument.  But we'll
69               ## let SSH deal with that.
70               1,?)
71                 arg=
72                 ;;
73
74               ## There's an argument cuddled on to the end of the option.  It
75               ## will have been committed to the `opts' array as part of the
76               ## option group.
77               *,??*)
78                 arg=${opt#?}
79                 opt=
80                 ;;
81
82               ## Nope.  There's an argument in the next word.  Grab it and
83               ## commit it.
84               *)
85                 arg="$2"
86                 opts=("${opts[@]}" "$arg")
87                 shift
88                 ;;
89             esac
90             ;;
91
92           ## Anything else.  Let it go even if it's not valid: SSH will moan
93           ## if it wants.  Note that `--' is ignored by SSH, but this isn't
94           ## documented; in particular, `--' is /not/ a POSIX end-of-options
95           ## marker, so we don't try to handle it specially here either.
96           *)
97             ;;
98         esac
99
100         ## Phase two.  Figure out whether what this means for us.
101         case "$mode,$o" in
102
103           ## `-O foo' and `-S foo' mean that the caller wants to take
104           ## control of the multiplexing process.
105           ?,[MOS]*)
106             mode=p
107             ;;
108
109           ## Catch the login name if there is one.  Make sure the master
110           ## knows it.
111           ?,l*)
112             masteropts=("${masteropts[@]}" "-l$arg")
113             login=$arg
114             ;;
115
116           ## These options are interesting to the master connection.
117           m,[aADLlRSwxXv]*)
118             masteropts=("${masteropts[@]}" "-${o:0:1}$arg")
119             ;;
120
121           ## SSH options on the command line merit special attention.  Pass
122           ## them onto the master, if necessary.
123           ?,o*)
124             masteropts=("${masteropts[@]}" "${opt:0:1}$arg")
125             case "$mode,$arg" in
126
127               ## User wants to control the process.  Let him.
128               ?,ControlMaster=* | ?,ControlPath=*)
129                 mode=p
130                 ;;
131
132             esac
133             ;;
134         esac
135       done
136       ;;
137
138     ## A bare word.  Maybe it's the hostname, or the start of the command.
139     *)
140       case ${host-t} in
141         t) host=$1 ;;
142         *) break ;;
143       esac
144       ;;
145   esac
146   shift
147 done
148
149 ###--------------------------------------------------------------------------
150 ### Now to actually do the job.
151
152 ## If there's no host, pass straight through.  We can't do anything useful
153 ## anyway.
154 case ${host+t} in
155   t) ;;
156   *) mode=p ;;
157 esac
158
159 ## Actually do something useful.
160 case "$mode" in
161   p)
162     exec "$REAL_SSH" "${opts[@]}" ${host+"$host"} "$@"
163     ;;
164   m)
165     if ! "$REAL_SSH" -Ocheck ${login+"$login@"}"$host" >/dev/null 2>&1; then
166       "$REAL_SSH" -MNf "${masteropts[@]}" "$host"
167     fi
168     exec "$REAL_SSH" ${opts[@]} "$host" "$@"
169     ;;
170 esac
171
172 ###----- That's all, folks --------------------------------------------------