Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 400 lines (349 sloc) 9.538 kB
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
1 #!/bin/sh
2 #
3 # Copyright (c) 2007,2008,2009,2010,2011 Yahoo! Inc.
4 # All rights reserved.
5 #
6 # Originally written by Jan Schaumann <jschauma@yahoo-inc.com> in January 2007.
7 #
8 # Redistribution and use of this software in source and binary forms,
9 # with or without modification, are permitted provided that the following
10 # conditions are met:
11 #
12 # * Redistributions of source code must retain the above
13 # copyright notice, this list of conditions and the
14 # following disclaimer.
15 #
16 # * Redistributions in binary form must reproduce the above
17 # copyright notice, this list of conditions and the
18 # following disclaimer in the documentation and/or other
19 # materials provided with the distribution.
20 #
21 # * Neither the name of Yahoo! Inc. nor the names of its
22 # contributors may be used to endorse or promote products
23 # derived from this software without specific prior
24 # written permission of Yahoo! Inc.
25 #
26 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
27 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
29 # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #
38 #
39 # This script takes as input a list of hosts and tries to ssh to them.
40 # It generates as output a file containing the list of hosts that did not ping
41 # in one file, a list of hosts that do not appear to be listening on port 22
42 # in a second file, the list of hosts it was unable to ssh to in a third file
43 # and the outputs of the command it was told to run in the fourth file.
44
45 ###
46 ### Globals
47 ###
48
49 TDIR="${TMPDIR:-/tmp}"
50
51 AUTHMODE="pubkey"
52 BACKGROUND='no'
53 HEADLESS='yes'
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
54 INPUT_ARGS=
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
55 INPUT_FILES=
56 OUTPUT_DIR=${TDIR}
57 SCRIPT="remote.sh"
58 SSH_WRAPPER=".checkhosts-ssh"
59 SCP_WRAPPER=".checkhosts-scp"
60
61 CHECK_PING='yes'
62 CHECK_SSHD='yes'
63 HOSTNAMES_ONLY='no'
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
64 WANT_ERRORS=
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
65
66 ###
67 ### Subroutines
68 ###
69
70 # function : usage
71 # purpose : print a help message
72 # usage : usage
73 # inputs : none
74 # outputs : help message
75
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
76 usage() {
158a179 @jschauma merge tyop reported by blysik@; "cat", not "eat"
authored
77 cat <<EOF
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
78 Usage: ${0##*/} [-HIPSbh] [-A authmode] [-o dir] [-r script] -f file
79 -A Specify ssh authentication mode.
80 -H Do not use a headless account.
81 -I Do not use IP addresses.
82 -P Do not try to ping the hosts.
83 -S Do not check if sshd is running on the hosts.
84 -b The script is run in the background.
85 -f file Read list of hosts from file.
86 -h Print this message and exit.
87 -o dir Put output files into dir (default: TMPDIR).
88 -r script Specify the file that contains the script to run on the remote side.
89 EOF
90 }
91
92 # function : checkhost
93 # purpose : checks an individual host
94 # as such it performs:
95 # - a ping check
96 # - a check whether sshd is listening on port 22
97 # - tries to ssh to it and run uname on it
98 # usage : checkhost host
99 # inputs : a line consisting of a hostname and an optional second field
100 # assumed to be an IP address
101 # outputs : appends to the three output files
102
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
103 checkhost() {
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
104 local input="${1}"
105 local host="${1%% *}"
106 local ip="${1##* }"
107 local target="${ip}"
108
109 local host_output substr rval
110 local outfile=${OUTFILE_OK}
111
112 local readonly remotefile="${TDIR}/scanhosts.${USER}.$$"
113 local readonly rcmd="bash ${remotefile} && rm -f ${remotefile}"
114
115 local remote_shell="bash -s"
116 local sshopts="-q
117 -o ForwardAgent=no
118 -o ClearAllForwardings=yes
119 -o StrictHostKeyChecking=no
120 -o UserKnownHostsFile=/dev/null
121 -o GlobalKnownHostsFile=/dev/null
122 -o Protocol=2,1"
123
124 case x"${AUTHMODE}" in
125 xall)
126 sshopts="${sshopts} -o PasswordAuthentication=yes
127 -o PubkeyAuthentication=yes"
128 ;;
129 xpassword)
130 sshopts="${sshopts} -o PasswordAuthentication=yes
131 -o KbdInteractiveAuthentication=no
132 -o PubkeyAuthentication=no"
133 ;;
134 xpubkey)
135 sshopts="${sshopts} -o PasswordAuthentication=no
136 -o KbdInteractiveAuthentication=no
137 -o PubkeyAuthentication=yes"
138 ;;
139 esac
140
141 if [ "${HEADLESS}" = "yes" ]; then
142 sshopts="${sshopts} -o IdentitiesOnly=yes"
143 remote_shell=""
144 else
145 sshopts="${sshopts} -o NumberOfPasswordPrompts=1"
146 fi
147
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
148 if [ -z "${WANT_ERRORS}" ]; then
149 echo "${input}" >> ${OUTFILE_CHECKED}
150 fi
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
151
152 if [ "${HOSTNAMES_ONLY}" = "yes" -o "${host}" = "${ip}" ]; then
153 target="${host}"
154 ip=""
155 fi
156
157 if [ "${CHECK_PING}" = "yes" ]; then
158 ping -c 1 -i 1 -t 3 -q ${target} >/dev/null 2>&1 || {
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
159 if [ -z "${WANT_ERRORS}" ]; then
160 echo ${input} >> ${OUTFILE_NOPING}
161 else
162 echo "Ping error: ${input}" >&2
163 fi
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
164 return 1
165 # NOTREACHED
166 }
167 fi
168
169 if [ "${CHECK_SSHD}" = "yes" ]; then
170 check_sshd ${target} || {
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
171 if [ -z "${WANT_ERRORS}" ]; then
172 echo ${input} >> ${OUTFILE_NOSSHD}
173 else
174 echo "sshd error: ${input}" >&2
175 fi
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
176 return 1
177 # NOTREACHED
178 }
179 fi
180
181 if [ "${BACKGROUND}" = "no" ] && [ "${HEADLESS}" = "no" ]; then
182
183 ${SCP_WRAPPER} ${sshopts} ${SSHOPTS} ${SCRIPT} ${target}:${remotefile}
184 if [ $? -gt 0 ]; then
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
185 if [ -z "${WANT_ERRORS}" ]; then
186 echo ${input} >> ${OUTFILE_NOSSH}
187 else
188 echo "ssh error: ${input}" >&2
189 fi
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
190 return 1
191 # NOTREACHED
192 fi
193
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
194 host_output=$(${SSH_WRAPPER} ${sshopts} ${SSHOPTS} ${target} "${rcmd} 2>/dev/null")
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
195 else
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
196 host_output=$(${SSH_WRAPPER} ${sshopts} ${SSHOPTS} ${target} "${remote_shell}" < ${SCRIPT} 2>/dev/null)
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
197 fi
198
199 rval=$?
200
201 if [ ${rval} -eq 255 ]; then
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
202 if [ -z "${WANT_ERRORS}" ]; then
203 echo ${input} >> ${OUTFILE_NOSSH}
204 else
205 echo "ssh error: ${input}" >&2
206 fi
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
207 return 1
208 # NOTREACHED
209 elif [ ${rval} -gt 0 ]; then
210 outfile="${OUTFILE_EXITCODE}_${rval}"
211 fi
212
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
213 if [ -z "${WANT_ERRORS}" ]; then
214 echo "${host},${ip},${host_output}" | tr '
215 ' '\n' >> ${outfile}
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
216 fi
217 }
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
218
219 # function : process_input
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
220 # purpose : work of the lines in all input files, arguments or stdin
221 # usage : process_input_files
222 # inputs : none, operates on global INPUT_FILES
223 # outputs : none
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
224
225 process_input() {
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
226 local file line
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
227
228 # items given as arguments first...
229 for line in ${INPUT_ARGS}; do
230 checkhost "$line"
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
231 done
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
232
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
233 # next, any input files
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
234 for file in ${INPUT_FILES}; do
235 oIFS=${IFS}
236 # careful: there's a newline in quotes
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
237 IFS='
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
238 '
239 if [ -r "${file}" ]; then
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
240 for line in $(grep -v "^#" ${file}); do
241 IFS=${oIFS}
242 checkhost "${line}"
243 done
244 else
245 echo "Cannot read ${file}." >&2
246 fi
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
247 done
248
249 # if we haven't done anything, then we try to read input from
250 # stdin
251 if [ -z "${INPUT_FILES}" -a -z "${INPUT_ARGS}" ]; then
252 oIFS=${IFS}
253 # careful: there's a newline in quotes
254 IFS='
255 '
256 while read line; do
257 IFS=${oIFS}
258 checkhost "${line}"
259 done
260 fi
261
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
262
263 }
264
265 # function : check_sshd
266 # purpose : verify that ssh is listening on port 22 of the given host
267 # inputs : a hostname
268 # returns : 0 if the host is in fact listening on port 22, >0 otherwise
269
270 check_sshd() {
271 local readonly host=${1}
272
273 local i
274 local readonly tmpfile=$(mktemp ${TDIR}/$$.XXXXXX)
275
276 ( echo | nc $host 22 | grep -i ssh >${tmpfile}; ) &
277 pid=$!
278 i=0
279 while [ ${i} -lt 60 ]; do
280 kill -0 ${pid} >/dev/null 2>&1
281 if [ $? -ne 0 ]; then
282 break
283 fi
284 i=$(( ${i} + 1 ))
285 sleep 1
286 done
287 kill -9 ${pid} >/dev/null 2>&1
288 wait ${pid} >/dev/null 2>&1
289 if [ $? -eq 0 ]; then
290 local ssh="$(cat ${tmpfile})"
291 rm -f ${tmpfile}
292 if [ -n "${ssh}" ]; then
293 return 0
294 fi
295 fi
296
297 rm -f ${tmpfile}
298 return 1
299 }
300
301 # function : wrap_ssh
302 # purpose : create a near no-op ssh wrapper so that the name of the
303 # wrapper appears in the process table and we can more easily
304 # identify the ssh processes related to this program
305 # inputs : none
306 # returns : nothing, creates a symlink pointing to ssh in the output
307 # directory, iff it doesn't already exist
308
309 wrap_ssh() {
310
311 export PATH=${OUTPUT_DIR}:${PATH}
312 if [ -h "${OUTPUT_DIR}/${SSH_WRAPPER}" -a -h "${OUTPUT_DIR}/${SCP_WRAPPER}" ]; then
313 return
314 fi
315
316 ln -sf $(which ssh) ${OUTPUT_DIR}/${SSH_WRAPPER}
317 ln -sf $(which scp) ${OUTPUT_DIR}/${SCP_WRAPPER}
318 }
319
320
321 ###
322 ### Main
323 ###
324
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
325 main() {
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
326 while getopts 'A:HIPSbef:ho:r:' opt; do
327 case ${opt} in
328 A)
329 AUTHMODE="${OPTARG}"
330 ;;
331 H)
332 HEADLESS='no'
333 ;;
334 I)
335 HOSTNAMES_ONLY='yes'
336 ;;
337 P)
338 CHECK_PING='no'
339 ;;
340 S)
341 CHECK_SSHD='no'
342 ;;
343 b)
344 BACKGROUND='yes'
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
345 ;;
346 e)
347 WANT_ERRORS='yes'
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
348 ;;
349 f)
350 INPUT_FILES="${INPUT_FILES} ${OPTARG}"
351 ;;
352 h|\?)
353 usage
354 exit 0
355 # NOTREACHED
356 ;;
357 o)
358 if [ ! -d ${OPTARG} ]; then
359 echo "Not a directory: ${OPTARG}."
360 exit 1
361 # NOTREACHED
362 fi
363 OUTPUT_DIR="${OPTARG}"
364 ;;
365 r)
366 SCRIPT="${OPTARG}"
367 ;;
368 *)
369 usage
370 exit 1
371 # NOTREACHED
372 ;;
373 esac
374 done
375 shift $(($OPTIND - 1))
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
376
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
377 INPUT_ARGS="$@"
378
379 if [ ! -r ${SCRIPT} ]; then
380 echo "Unable to read script \"${SCRIPT}\"." >&2
381 exit 1
382 # NOTREACHED
383 fi
384
385 OUTFILE_CHECKED="${OUTPUT_DIR}/hosts_checked"
386 OUTFILE_EXITCODE="${OUTPUT_DIR}/hosts_exit"
387 OUTFILE_NOPING="${OUTPUT_DIR}/hosts_noping"
388 OUTFILE_NOSSH="${OUTPUT_DIR}/hosts_nossh"
389 OUTFILE_NOSSHD="${OUTPUT_DIR}/hosts_nosshd"
390 OUTFILE_OK="${OUTPUT_DIR}/hosts_ok"
391
392 wrap_ssh
5aeb55e @jschauma Version 3.3.0 (2012-02-03)
authored
393
4cd60b9 @jschauma Initial import of sources from Yahoo!
authored
394 process_input
395
396 return 0
397 }
398
Something went wrong with that request. Please try again.