#!/bin/bash # checkup - check if sites are up. function debug { if false then echo "DEBUG: $*" fi } case $# in 1) config_file=$1 ;; *) echo "Usage: checkup " exit 1 ;; esac # Syntax of config file: # # Blank lines are ignored. # Lines starting with # are ignored. # Other lines consist of a command word and optional argumnets: # mailto addr - Specify email address that output should be sent to. # rundir dirname - Specify directory in which runtime state is stored (default /var/run/checkup). # sanity addr - Ping this address first as a 'sanity check'. # ping addr - An address (hostname or ip) to ping. # wget url - A URL to retrieve. # mail addr - An SMTP server address to check. PINGOPTS="-q -c 1 -w 30" mailto="" rundir="/var/run/checkup" sanities="" pings="" declare -a wgets mails="" if [ ! -f "${config_file}" ] then echo "Error: config file '${config_file}' does not exist / is not a file" exit 1; fi if [ ! -r "${config_file}" ] then echo "Error: config file '${config_file}' is not readable" exit 1; fi stripped_config=`mktemp /tmp/checkup.conf.XXXXXX` || exit 1 trap "rm ${stripped_config}" EXIT cat "${config_file}" | grep -v '^#' | grep -v '^[\t ]*$' > ${stripped_config} while read -r cmd args do case ${cmd} in mailto) mailto+="${args} " ;; rundir) rundir=${args} ;; sanity) sanities+="${args} " ;; ping) pings+="${args} " ;; wget) wgets+=("${args}") ;; mail) mails+="${args} " ;; *) echo "Error: syntax error in configuration file: '${cmd}' not understood" exit 1 ;; esac done < ${stripped_config} case ${mailto} in "") echo "Error: no email address specified." exit 1 ;; *) ;; esac if [ ! -d "${rundir}" ] then echo "Error: rundir '${rundir}' does not exist / is not a directory" exit 1 fi for host in ${sanities} do ping ${PINGOPTS} ${host} > /dev/null 2>&1 case $? in 0) ;; *) echo "Sanity-check ping $host failed, giving up" exit 1 ;; esac done cd "${rundir}" report="" function run_test { test_type=$1 target=$2 lastarg=`echo "${target}" | sed 's/.* //g'` statefile=${test_type}_`echo "${lastarg}" | tr '/' '_' `.state if [ -f "${statefile}" ] then read last_state last_timestamp up_report_interval down_report_interval < "${statefile}" else last_state="1" last_timestamp="0" up_report_interval="500" down_report_interval="500" fi timestamp=`date +%s` since_last_report=$((${timestamp} - ${last_timestamp})) #debug "since_last_report=${since_last_report}" if [ ${since_last_report} -lt ${down_report_interval} \ -a ${since_last_report} -lt ${up_report_interval} ] then debug "skipping ${target}, neither report interval reached" return fi case ${test_type} in ping) debug "pinging ${target}" ping ${PINGOPTS} ${target} > /dev/null 2>&1 case $? in 0) testok="1" ;; *) testok="0" ;; esac ;; wget) debug "wgetting ${target}" wget -q --append-output /dev/null -O /dev/null -T 10 ${target} case $? in 0) testok="1" ;; *) testok="0" ;; esac ;; mail) debug "mailing ${target}" echo -n | netcat -w 10 -q 15 "${target}" 25 | grep -q '^220' case $? in 0) testok="1" ;; 1) testok="0" ;; *) echo "Error: grep returned result $? for ${target}" exit 1 ;; esac ;; esac case "${testok}" in "0") if [ ${since_last_report} -ge ${down_report_interval} ] then case "${last_state}" in "0") report+="${test_type} ${lastarg} still down " ;; "1") report+="${test_type} ${lastarg} is DOWN " ;; esac up_report_interval=300 down_report_interval="${down_report_interval}0" echo "${testok} ${timestamp} ${up_report_interval} ${down_report_interval}" > ${statefile} fi ;; "1") if [ ${since_last_report} -ge ${up_report_interval} ] then case "${last_state}" in "0") report+="${test_type} ${lastarg} is UP " ;; "1") ;; esac down_report_interval=300 up_report_interval="${up_report_interval}0" echo "${testok} ${timestamp} ${up_report_interval} ${down_report_interval}" > ${statefile} fi ;; esac } for host in ${pings} do run_test ping "${host}" done for url in "${wgets[@]}" do run_test wget "${url}" done for host in ${mails} do run_test mail "${host}" done if [ "${report}" ] then debug "report: ${report}" echo "${report}" | mail -s "Checkup report: " ${mailto} fi