chiark / gitweb /
el/dot-emacs.el: Better fontification for Ediff.
[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 : ${SSH_HOME=$(unset HOME; bash -c 'echo ~/.ssh')}
29 : ${SSH_LOGDIR=$SSH_HOME/log-$(hostname)}
30
31 ###--------------------------------------------------------------------------
32 ### Parse the command line and dredge out information.
33
34 ## This is a reasonable approximation to SSH's command-line argument parser.
35 unset host login
36 mode=m
37 declare -a opts masteropts
38 while :; do
39   case "$1,$#" in
40
41     ## Nothing left.  We're done.
42     ,0)
43       break
44       ;;
45
46     ## A command line option group.  Parse it and work out what's going on.
47     -*)
48       opt=$1
49       opts=("${opts[@]}" "$opt")
50       while :; do
51
52         ## Strip off the first character, because it's the one we dealt with
53         ## last time.
54         opt=${opt#?}
55         o=$opt
56
57         ## Phase 1: identify the option and whether it needs an argument.
58         unset arg
59         case "$o" in
60
61           ## Empty group.  We're done.
62           "")
63             break
64             ;;
65
66           ## Options with arguments.
67           [bcDeFiLlmOopRSw]*)
68             case "$#,$o" in
69
70               ## You're going to lose because there's no argument.  But we'll
71               ## let SSH deal with that.
72               1,?)
73                 arg=
74                 ;;
75
76               ## There's an argument cuddled on to the end of the option.  It
77               ## will have been committed to the `opts' array as part of the
78               ## option group.
79               *,??*)
80                 arg=${opt#?}
81                 opt=
82                 ;;
83
84               ## Nope.  There's an argument in the next word.  Grab it and
85               ## commit it.
86               *)
87                 arg="$2"
88                 opts=("${opts[@]}" "$arg")
89                 shift
90                 ;;
91             esac
92             ;;
93
94           ## Anything else.  Let it go even if it's not valid: SSH will moan
95           ## if it wants.  Note that `--' is ignored by SSH, but this isn't
96           ## documented; in particular, `--' is /not/ a POSIX end-of-options
97           ## marker, so we don't try to handle it specially here either.
98           *)
99             ;;
100         esac
101
102         ## Phase two.  Figure out whether what this means for us.
103         case "$mode,$o" in
104
105           ## `-O foo' and `-S foo' mean that the caller wants to take
106           ## control of the multiplexing process.
107           ?,[MOS]*)
108             mode=p
109             ;;
110
111           ## Catch the login name if there is one.  Make sure the master
112           ## knows it.
113           ?,l*)
114             masteropts=("${masteropts[@]}" "-l$arg")
115             login=$arg
116             ;;
117
118           ## Catch the port number.  We want this for building the logfile
119           ## name.
120           ?,p*)
121             masteropts=("${masteropts[@]}" "-p$arg")
122             port=$arg
123             ;;
124
125           ## These options are interesting to the master connection.
126           m,[aADLlRSwxXv46]*)
127             masteropts=("${masteropts[@]}" "-${o:0:1}$arg")
128             ;;
129
130           ## SSH options on the command line merit special attention.  Pass
131           ## them onto the master, if necessary.
132           ?,o*)
133             masteropts=("${masteropts[@]}" "${opt:0:1}$arg")
134             case "$mode,$arg" in
135
136               ## User wants to control the process.  Let him.
137               ?,ControlMaster=* | ?,ControlPath=*)
138                 mode=p
139                 ;;
140
141             esac
142             ;;
143         esac
144       done
145       ;;
146
147     ## A bare word.  Maybe it's the hostname, or the start of the command.
148     *)
149       case ${host-t}${host+nil} in
150         t) host=$1 ;;
151         *) break ;;
152       esac
153       ;;
154   esac
155   shift
156 done
157
158 ###--------------------------------------------------------------------------
159 ### Now to actually do the job.
160
161 ## If there's no host, pass straight through.  We can't do anything useful
162 ## anyway.
163 case ${host+t} in
164   t) ;;
165   *) mode=p ;;
166 esac
167
168 ## Actually do something useful.
169 case "$mode" in
170   p)
171     exec "$REAL_SSH" "${opts[@]}" ${host+"$host"} "$@"
172     ;;
173   m)
174     mkdir -p -m700 "$SSH_LOGDIR"
175     logfile=$SSH_LOGDIR/${login+"$login@"}"$host"${port+":$port"}.log
176     if ! "$REAL_SSH" -Ocheck \
177       ${port+-p$port} ${login+"$login@"}"$host" \
178       >/dev/null 2>&1
179     then
180       "$REAL_SSH" -MNf "${masteropts[@]}" "$host" \
181         </dev/null >/dev/null 2>$logfile
182     fi
183     exec "$REAL_SSH" ${opts[@]} "$host" "$@"
184     ;;
185 esac
186
187 ###----- That's all, folks --------------------------------------------------