#!/bin/sh # # $YahooCvsId: checkhosts,v 1.28 2009/01/23 21:16:52 Exp $ # $Source: /CVSROOT/.../checkhosts,v $ # # Copyright (c) 2007,2008 Yahoo! Inc. # # Originally written by Jan Schaumann in January 2007. # # This script takes as input a list of hosts and tries to ssh to them. # It generates as output a file containing the list of hosts that did not ping # in one file, a list of hosts that do not appear to be listening on port 22 # in a second file, the list of hosts it was unable to ssh to in a third file # and the outputs of the command it was told to run in the fourth file. ### ### Globals ### BACKGROUND='no' INPUT_FILES= OUTPUT_DIR=${TMPDIR:-/tmp} SCRIPT="remote.sh" SSH_WRAPPER=".checkhosts-ssh" SCP_WRAPPER=".checkhosts-scp" CHECK_PING='yes' CHECK_SSHD='yes' HOSTNAMES_ONLY='no' ### ### Subroutines ### # function : usage # purpose : print a help message # usage : usage # inputs : none # outputs : help message usage () { cat <> ${OUTFILE_CHECKED} if [ "${HOSTNAMES_ONLY}" = "yes" -o "${host}" = "${ip}" ]; then target="${host}" ip="" fi if [ "${CHECK_PING}" = "yes" ]; then ping -c 1 -i 1 -t 3 -q ${target} >/dev/null 2>&1 || { echo ${input} >> ${OUTFILE_NOPING} return 1 # NOTREACHED } fi if [ "${CHECK_SSHD}" = "yes" ]; then check_sshd ${target} || { echo ${input} >> ${OUTFILE_NOSSHD} return 1 # NOTREACHED } fi if [ "${BACKGROUND}" = "no" ]; then ${SCP_WRAPPER} ${sshopts} ${SCRIPT} ${target}:${remotefile} if [ $? -gt 0 ]; then echo ${input} >> ${OUTFILE_NOSSH} return 1 # NOTREACHED fi host_output=$(${SSH_WRAPPER} ${sshopts} ${target} "${rcmd}") else host_output=$(cat ${SCRIPT} | ${SSH_WRAPPER} ${sshopts} ${target} "bash -s") fi if [ $? -gt 0 ]; then echo ${input} >> ${OUTFILE_NOSSH} return 1 # NOTREACHED fi echo "${host}${ip:+"::${ip}"}::${host_output}" | tr ' ' '\n' >> ${OUTFILE_OK} } # function : process_input_files # purpose : work of the lines in all input files # usage : process_input_files # inputs : none, operates on global INPUT_FILES # outputs : none process_input_files () { local file for file in ${INPUT_FILES}; do if [ -r "${file}" ]; then local tmpf="/tmp/checkhosts.$$" grep -v "^#" ${file} > ${tmpf} oIFS=${IFS} IFS=' ' for line in $(cat ${tmpf}); do IFS=${oIFS} checkhost "${line}" done rm ${tmpf} else echo "Cannot read ${file}." >&2 fi done } # function : check_sshd # purpose : verify that ssh is listening on port 22 of the given host # inputs : a hostname # returns : 0 if the host is in fact listening on port 22, >0 otherwise check_sshd() { local readonly host=${1} local i local readonly tmpfile=$(mktemp /tmp/$$.XXXXXX) ( echo | nc $host 22 | grep -i ssh >${tmpfile}; ) & pid=$! i=0 while [ ${i} -lt 60 ]; do kill -0 ${pid} >/dev/null 2>&1 if [ $? -ne 0 ]; then break fi i=$(( ${i} + 1 )) sleep 1 done kill ${pid} >/dev/null 2>&1 wait ${pid} >/dev/null 2>&1 if [ $? -eq 0 ]; then local ssh="$(cat ${tmpfile})" rm -f ${tmpfile} if [ -n "${ssh}" ]; then return 0 fi fi rm -f ${tmpfile} return 1 } # function : wrap_ssh # purpose : create a near no-op ssh wrapper so that the name of the # wrapper appears in the process table and we can more easily # identify the ssh processes related to this program # inputs : none # returns : nothing, creates a symlink pointing to ssh in the output # directory, iff it doesn't already exist wrap_ssh() { export PATH=${OUTPUT_DIR}:${PATH} if [ -h "${OUTPUT_DIR}/${SSH_WRAPPER}" -a -h "${OUTPUT_DIR}/${SCP_WRAPPER}" ]; then return fi ln -sf $(which ssh) ${OUTPUT_DIR}/${SSH_WRAPPER} ln -sf $(which scp) ${OUTPUT_DIR}/${SCP_WRAPPER} } ### ### Main ### main() { while getopts 'IPSbf:ho:r:' opt; do case ${opt} in I) HOSTNAMES_ONLY='yes' ;; P) CHECK_PING='no' ;; S) CHECK_SSHD='no' ;; b) BACKGROUND='yes' ;; f) INPUT_FILES="${INPUT_FILES} ${OPTARG}" ;; h|\?) usage exit 0 # NOTREACHED ;; o) if [ ! -d ${OPTARG} ]; then echo "Not a directory: ${OPTARG}." exit 1 # NOTREACHED fi OUTPUT_DIR="${OPTARG}" ;; r) SCRIPT="${OPTARG}" ;; *) usage exit 1 # NOTREACHED ;; esac done shift $(($OPTIND - 1)) if [ -z "${INPUT_FILES}" -a $# -lt 1 ]; then usage exit 1 # NOTREACHED fi if [ ! -r ${SCRIPT} ]; then echo "Unable to read script \"${SCRIPT}\"." >&2 exit 1 # NOTREACHED fi OUTFILE_CHECKED="${OUTPUT_DIR}/hosts_checked" OUTFILE_NOPING="${OUTPUT_DIR}/hosts_noping" OUTFILE_NOSSH="${OUTPUT_DIR}/hosts_nossh" OUTFILE_NOSSHD="${OUTPUT_DIR}/hosts_nosshd" OUTFILE_OK="${OUTPUT_DIR}/hosts_ok" # prompt for password early on to avoid waiting for user input # down the line if [ -t 0 ]; then stty -echo read -p "Password: " nada echo read -p "Enter passphrase for RSA key '/home/$(whoami)/.ssh/identity': " nada stty echo echo fi wrap_ssh process_input_files return 0 } main "$@"