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} | |
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 -------------------------------------------------- |