Commit | Line | Data |
---|---|---|
4d7d040b MW |
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} | |
e41b452f MW |
28 | : ${SSH_HOME=$(unset HOME; bash -c 'echo ~/.ssh')} |
29 | : ${SSH_LOGDIR=$SSH_HOME/log-$(hostname)} | |
4d7d040b MW |
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 | ||
e41b452f MW |
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 | ||
4d7d040b | 125 | ## These options are interesting to the master connection. |
ef864444 | 126 | m,[aADLlRSwxXv46]*) |
4d7d040b MW |
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 | *) | |
af844120 | 149 | case ${host-t}${host+nil} in |
4d7d040b MW |
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) | |
e41b452f MW |
174 | mkdir -p -m700 "$SSH_LOGDIR" |
175 | logfile=$SSH_LOGDIR/${login+"$login@"}"$host"${port+":$port"}.log | |
cfed2fe1 MW |
176 | if ! "$REAL_SSH" -Ocheck \ |
177 | ${port+-p$port} ${login+"$login@"}"$host" \ | |
afda7f51 MW |
178 | >/dev/null 2>&1 |
179 | then | |
e41b452f MW |
180 | "$REAL_SSH" -MNf "${masteropts[@]}" "$host" \ |
181 | </dev/null >/dev/null 2>$logfile | |
4d7d040b MW |
182 | fi |
183 | exec "$REAL_SSH" ${opts[@]} "$host" "$@" | |
184 | ;; | |
185 | esac | |
186 | ||
187 | ###----- That's all, folks -------------------------------------------------- |