#!/bin/bash # $Id: pt,v 3.7 2005/08/28 22:18:21 jlarsen Exp $ #Copyright: 2004 and 2005 by John R Larsen #Free for personal use. Contact the author for commercial use. #http://larsen-family.us theClaw56@larsen-family.us # # Description: bash script to ping an IP forever and redirect output to a file. # It can be run as a cron job or from the shell. Help is available by # entering "pt" without arguments on the command line. # # Two logfiles are maintained as well as several statistical counters. Each # ping is logged to $IP.logfile. A counter is incremented each time a ping # is dropped and decremented each time a ping is successful. If the number # of dropped pings exceeds a threshold (default 9) an email is sent to the # address stored in $EMAIL_ADDRESS (default value set below). When the # dropped count returns to zero another email is sent indicating the site # is accessible again. Each time an email is sent an entry is added to the # file $IP.emails for easy analysis. # # This is where the default email address is set. This is overridden by -e option: EMAIL_ADDRESS=email@address.com # # The $IP.logfile is compressed weekly and 4 old logfiles are maintained. The # $IP.emails file isn't automatically deleted. It should be small since # it only has the email lines in it. # # The script can be setup to periodically update a local or remote webpage. # # Read the help for the various options. # # Below is a typical crontab line for using this script. # 0,15,30,45 * * * * /home/jlarsen/ping_test/pt mc.com -r 15 -t 4 -e john@larsen-family.us,theClaw56@netzero.com -c # # This script has been tested on the following systems: # Solaris 8 # Mandrake Linux 8.2, 9.1, and 10.1 # RedHat Linux Workstation 3 # Cygwin 1.5.13 and 1.5.14 #------------------------------------------------------------------------------ # display_help: Function to display the help screen function display_help () { local func_name=display_help if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside display_help" >> $IP.logfile; fi cat << EOF ----- ping_test ($SCRIPT_VER $SCRIPT_DATE) -------------------------------------------- usage: pt IP | Domain_name [ options ] The IP or domain name is pinged once every ping_rate until the program is halted. Ping results stored in \$IP.logfile. Email log is \$IP.emails. Options: -c Cron job processing -d n debug level (ie. -d 0x4040 ) -ds Delete Statistics -dl Delete Logs -s Show statistics -e adr Email address (Default: $EMAIL_ADDRESS) (Separate multiple email addresses with commas and no spaces) -h Help screen -hd Help Debug -hourly Enable hourly tracking of ping statistics (Default: Disabled) -k Kill a running instance of pt -m Make a \$IP.copy script. You MUST modify this for your situation! -mv IP Rename all the files associated with this test to the IP entered. -r n ping Rate in seconds (Default: $PING_RATE) -t n Threshold number of dropped packets before sending an email (Default: $DROPPED_THRESHOLD) -tto n Traceroute TimeOut value in seconds (Default: 15) -wr n Web page update Rate in minutes (Default: 10) (Note: This feature enabled if script \$IP.copy exists in `pwd`. The name of the generated web page (\$IP.html) is passed in \$1 to the script to allow renaming. \$IP.copy must be stand alone containing all steps needed to transfer the file, ie. scp or cp) Copyright: 2004 and 2005 by John R Larsen Free for personal use. Contact the author for commercial use. http://larsen-family.us theClaw56@larsen-family.us EOF } # display_help #------------------------------------------------------------------------------- # get_version. Function that parses script version and date info from the RCS Id line. function get_version () { SCRIPT_VER="v`echo '$Id: pt,v 3.7 2005/08/28 22:18:21 jlarsen Exp $' | awk '{print $3}'`" SCRIPT_DATE="`echo '$Id: pt,v 3.7 2005/08/28 22:18:21 jlarsen Exp $' | awk '{print $4}'`" } # get_version #------------------------------------------------------------------------------ # display_debug_help: Function to display debug help. function display_debug_help () { local func_name=display_debug_help if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside display_debug_help" >> $IP.logfile; fi cat << EOF | more ----- Debug Help ($SCRIPT_VER $SCRIPT_DATE) --------------------------------------- Debug is enabled in one of three ways in decending order of precedence: "-d 0xNNNN" on command line PT_DEBUG_FLAGS environment variable "debug_flags" file in same directory as pt Debug output can be changed "on the fly" by changing the contents of "debug_flags", which is read each time pt goes through its while loop. Debug output is added only to the logfile. If you want to see in real time what is going on then tail the logfile with this command: "tail -f $IP.logfile". Each debug line is qualified with a construct like the following: if [ \$((\$D & 0x1)) -ne 0 ]; then echo "[\$LINENO]\$func_name:\$\$> Debug message or action";fi The expression on the left of -ne is a bash construct. The $((expr)) gets evaluated and returns a value. The expression I'm using is "$D & 0xNN". The & performs a bitwise AND and returns the result. If it is zero (no matching bits) then the debug line is skipped. If it is non zero then the debug line is performed. Each debug line can have multiple bits that turn it on or just a single bit. The pattern "0xNN" is the collection of bits that turn the debug on. The action of each bit is defined below. 00000000 - No debug 00000001 - Enable all the "Inside function name" debug lines 00000002 - process_command_line verbose output 00000004 - Inside while loop message 00000008 - send_email: output message each time an email is sent 00000010 - main: Display environment when starting up 00000020 - fp: Display calls to fp with arguments and results 00000040 - test_pid: Display contents of \$\$.test.pid 00000080 - main: Force TEST_PING_STATUS to toggle every 5 while loops for testing 00000100 - main: Output the contents of $IP.stats 00000200 - main: Output the contents of $IP.weeks 00000400 - main: Update and output the contents of $IP.route 00000800 - main: Output the contents of $IP.sums 00001000 - ping_ip: Output \$PING_TIME extracted from \$PARSED_TIMES 00002000 - sleep_time: Output runtime statistics 00004000 - read_file: Display contents of \$LINES array 00008000 - get_route: Enable debugging output 80000000 - Don't output the "Debug flags changed" messages in "set_debug_level" Copyright: 2004 and 2005 by John R Larsen Free for personal use. Contact the author for commercial use. http://larsen-family.us theClaw56@larsen-family.us EOF } # display_debug_help #------------------------------------------------------------------------------ # fp: Function that does floating point math and tests using awk # $1 - First argument # $2 - Operation. One of -ne -eq -ge -gt -le -lt -add -sub -div -mult # $3 - Second argument # $RC_FP contains the results of the operation. For relational operations a return # value of 0 means the condition is true. -1 means the condition is false. The # arithmetic operations return the mathmatical result. # $? is 0 for success or -1 if error occurs. Check $? after call to test for errors. # Each of the tests has error trapping built in. The calculation must be repeated # twice in a row with the same value before the answer is returned. The assumption # is that it is highly unlikely that awk would end in an error condition twice in # a row. Each fp operation is performed in a while loop. As soon as two identical # results in a row are detected, the operation returns. An error message is output # if more than two tests are required. function fp () { local func_name=fp if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside fp: $1 $2 $3" >> $IP.logfile; fi # Verify all three required arguments were passed if [ "$1" = "" ]; then echo "ERROR [$LINENO]$func_name> ERROR. Missing \$3, \$2, and \$1 arguments in call to fp" | tee -a $IP.logfile RC_FP=0 return -1; fi if [ "$2" = "" ]; then echo "ERROR [$LINENO]$func_name> ERROR. Missing \$3 and \$2 arguments in call to fp" | tee -a $IP.logfile RC_FP=0 return -1; fi if [ "$3" = "" ]; then echo "ERROR [$LINENO]$func_name> ERROR. Missing \$3 argument in call to fp" | tee -a $IP.logfile RC_FP=0 return -1; fi case $2 in -ne) # Return 0 if arg_1 is not equal to arg_2; else return -1 local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ if ( $1 != $2 ) { \ printf ("%d", 0) \ } else { \ printf ("%d", -1) \ } \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; -eq) # Return 0 if arg_1 is equal to arg_2; else return -1 local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ if ( $1 == $2 ) { \ printf ("%d", 0) \ } else { \ printf ("%d", -1) \ } \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; -ge) # Return 0 if arg_1 is greater than or equal to arg_2; else return -1 local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ if ( $1 >= $2 ) { \ printf ("%d", 0) \ } else { \ printf ("%d", -1) \ } \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; -gt) # Return 0 if arg_1 is greater than arg_2; else return -1 local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ if ( $1 > $2 ) { \ printf ("%d", 0) \ } else { \ printf ("%d", -1) \ } \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; -le) # Return 0 if arg_1 is less than arg_2; else return -1 local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ if ( $1 <= $2 ) { \ printf ("%d", 0) \ } else { \ printf ("%d", -1) \ } \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; -lt) # Return 0 if arg_1 is less than arg_2; else return -1 local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ if ( $1 < $2 ) { \ printf ("%d", 0) \ } else { \ printf ("%d", -1) \ } \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; -add) # Return the sum of the two numbers local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ printf "%f", $1 + $2 \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; -sub) # Return the difference of the two numbers local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ printf "%f", $1 - $2 \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; -div) # Return the quotient of the two numbers # Check for divide by zero condition error. Look for any nonzero digit. local non_zero=`echo $3 | sed -n -e 's/.*[1-9].*/non_zero/p'` if [ "$non_zero" == "non_zero" ]; then local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ printf "%f", $1 / $2 \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done else # Trying to divide by zero. Return error. echo "ERROR [$LINENO]$func_name> fp -div ERROR. Trying to divide by zero." | tee -a $IP.logfile RC_FP=0 return -1; fi ;; -mult) # Return the product of the two numbers local rc_fp1="" local count=0 while [ true ]; do RC_FP=`echo "$1 $3" | awk ' { \ printf "%f", $1 * $2 \ }'` if [ "$RC_FP" == "$rc_fp1" ]; then if [ $count -gt 1 ]; then echo "ERROR [$LINENO]$func_name> fp $1 $2 $3 ERROR. \$count: $count \$rc_fp2: $rc_fp2 \$RC_FP: $RC_FP" | tee -a $IP.logfile fi if [ $(($D & 0x20)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> fp: $1 $2 $3 = $RC_FP" >> $IP.logfile; fi return 0 else rc_fp2=$rc_fp1 rc_fp1=$RC_FP let count=count+1 fi done ;; *) # Unsupported operation echo "ERROR [$LINENO]$func_name> Unsupported operation $2 in call to fp" | tee -a $IP.logfile RC_FP=0 return -1; ;; esac } # fp #------------------------------------------------------------------------------ # set_debug_level: Function that sets the $D variable based on a file, an #environment variable, or a command line variable. See display_debug_help. function set_debug_level () { local func_name=set_debug_level # Note: Command line -d overrides environment variable PT_DEBUG_FLAGS if it exists, which overrides debug_flags file if it exists # Check one time if PT_DEBUG_FLAGS env variable exists and use it if it does if [ "${ENV_VAR_TESTED:=FALSE}" = "FALSE" ]; then ENV_VAR_TESTED=TRUE export D=${PT_DEBUG_FLAGS:-0} if [ "$IP" != "" ] && [ "$D" != "0" ] && [ $(($D & 0x80000000)) -eq 0 ]; then echo "[$LINENO]$$>: set_debug_level: Debug flags changed to: $D at `date +%Y\-%b\-%d\ %H\:%M\:%S\ %a`" >> $IP.logfile fi fi # Check the existence of a file named "debug_flags" and use it if conditions are right if [ -e debug_flags ]; then if [ "${DEBUG_FLAGS_FILE:=FIRST_READ}" = "FIRST_READ" ]; then # Getting here means the debug_flags file has never been read. DEBUG_FLAGS_FILE=`cat -s debug_flags` if [ "$D" = "0" ]; then # Getting here means $D was zero and the debug_flags contents should be used instead since env variable doesn't exist or has value of 0 D=$DEBUG_FLAGS_FILE if [ "$IP" != "" ] && [ "$D" != "0" ] && [ $(($D & 0x80000000)) -eq 0 ]; then echo "[$LINENO]$$>: set_debug_level: Debug flags changed to: $D at `date +%Y\-%b\-%d\ %H\:%M\:%S\ %a`" >> $IP.logfile fi fi else # Read debug_flags file and update $D if the file has changed NEW_DEBUG_FLAGS_FILE=`cat -s debug_flags` if [ "$NEW_DEBUG_FLAGS_FILE" != $DEBUG_FLAGS_FILE ]; then DEBUG_FLAGS_FILE=$NEW_DEBUG_FLAGS_FILE D=$DEBUG_FLAGS_FILE if [ "$IP" != "" ] && [ $(($D & 0x80000000)) -eq 0 ]; then echo "[$LINENO]$$>: set_debug_level: Debug flags changed to: $D at `date +%Y\-%b\-%d\ %H\:%M\:%S\ %a`" >> $IP.logfile fi fi fi fi # This line is at the end because $D is set in this function if [ "$IP" != "" ] && [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside set_debug_level (Flags: $D)" >> $IP.logfile; fi } # set_debug_level #------------------------------------------------------------------------------- # setup_env. Function that determines what OS the script is running on and # setups environment variables accordingly. function setup_env () { local func_name=setup_env if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside setup_env" >> $IP.logfile; fi # Figure out which OS you're running on and setup program paths accordingly OS_TYPE=`uname -a | awk '{print $3}'` case $OS_TYPE in 5.8*) # Solaris 8 export HOST=`/usr/ucb/hostname` LS=/bin/ls MAIL=/usr/ucb/mail NETSTAT="/usr/bin/netstat -P tcp" PING=/usr/sbin/ping PS=/bin/ps export SCP=/opt/bin/scp TRACE_ROUTE=/usr/sbin/traceroute W=/usr/bin/w ZIP_PGM=/usr/bin/zip ;; 2.*) # Linux 2.x kernels export HOST=`/bin/hostname` LS=/bin/ls MAIL=/bin/mail NETSTAT="/bin/netstat -t" PING=/bin/ping PS=/bin/ps export SCP=/usr/bin/scp TRACE_ROUTE=/usr/sbin/traceroute W=/usr/bin/w ZIP_PGM=/usr/bin/zip ;; 1.5*) # cygwin # Base cygwin doesn't include many packages required for pt to work. # Check that these packages have been installed in their default locations. # Make sure the ping package has been installed if [ -e /usr/bin/ping ]; then PING=/usr/bin/ping else echo "ERROR [$LINENO]$func_name> Missing ping. Install the ping package." | tee -a $IP.logfile exit fi # Make sure the openssh package has been installed if [ -e /usr/bin/ssh ]; then SSH=/usr/bin/ssh else echo "ERROR [$LINENO]$func_name> Missing ssh. Install the openssh package." | tee -a $IP.logfile exit fi # scp is part of the openssh package export SCP=/usr/bin/scp # Make sure the procps package has been installed if [ -e /usr/bin/oldps ]; then PS=/usr/bin/oldps elif [ -e /usr/bin/procps ]; then PS=/usr/bin/procps else echo "ERROR [$LINENO]$func_name> Missing oldps or procps. Install the procps package." | tee -a $IP.logfile exit fi # w is in the procps package W=/usr/bin/w # Make sure the email package has been installed if [ -e /usr/bin/email ]; then MAIL=/usr/bin/email else echo "ERROR [$LINENO]$func_name> Missing email. Install the email package." | tee -a $IP.logfile exit fi # Make sure the zip package has been installed if [ -e /usr/bin/zip ]; then ZIP_PGM=/usr/bin/zip else echo "ERROR [$LINENO]$func_name> Missing zip. Install the zip package." | tee -a $IP.logfile exit fi # cygwin uses the Windows native netstat program if [ -e /c/WINDOWS/system32/netstat ]; then NETSTAT=/c/WINDOWS/system32/netstat elif [ -e /c/WINNT/system32/netstat ]; then NETSTAT=/c/WINNT/system32/netstat else echo "ERROR [$LINENO]$func_name> Can't find location of Windows netstat program." | tee -a $IP.logfile exit fi # cygwin uses the Windows native tracert program if [ -e /c/WINDOWS/system32/tracert ]; then TRACE_ROUTE=/c/WINDOWS/system32/tracert elif [ -e /c/WINNT/system32/tracert ]; then TRACE_ROUTE=/c/WINNT/system32/tracert else echo "ERROR [$LINENO]$func_name> Can't find location of Windows tracert program." | tee -a $IP.logfile exit fi # The following cygwin programs are part of base packages LS=/usr/bin/ls export HOST=`/usr/bin/hostname` ;; *) # Unknown OS echo "ERROR [$LINENO]$func_name> Unknown OS" | tee -a $IP.logfile exit ;; esac # Set the default values of all environment variables here DASH_HOURLY="" HOURLY_STATS=FALSE WEB_PAGE_UPDATE_RATE=10 CRONJOB=FALSE DELETE_STATS=FALSE DELETE_LOGS=FALSE DROPPED_THRESHOLD=9 IP=not_entered KILL_PT=FALSE PING_RATE=5 SHOW_STATS=FALSE WORKING_DIR="" YES=1 NO=0 TRUE=1 FALSE=0 ACTIVE=0 DEAD=1 TRACE_COMPLETED=0 TIMED_OUT=255 TRACEROUTE_TIMEOUT=15 } # setup_env #------------------------------------------------------------------ # get_route: Function that does traceroute on IP # $1: IP to traceroute # $2: Timeout value (default: 15 seconds) # The route is saved to a file $1.route in the current directory. # $RC_GET_ROUTE has the value $TIMED_OUT if the traceroute gets killed # or it has the value $TRACE_COMPLETE if the trace completes normally. function get_route () { local func_name=get_route if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside get_route (IP: $1 Timeout: ${2:-15})" >> $IP.logfile; fi # This local function is what gets timed function _get_route() { local start_trace=`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` case $OS_TYPE in 5.8*) # Solaris 8 $TRACE_ROUTE -I $1 &> $1.route ;; 2.*) # Linux 2.x kernels $TRACE_ROUTE -I $1 &> $1.route ;; 1.5*) # Cygwin: Requires procps package installed $TRACE_ROUTE $1 &> $1.route # cygwin doesn't know -I argument because it uses windows tracert ;; *) # Unknown OS echo "ERROR [$LINENO]$func_name> Unknown OS" | tee -a $IP.logfile exit ;; esac echo "traceroute started: $start_trace" >> $1.route echo "traceroute ended: `date +%Y\-%b\-%d\ %H\:%M\:%S\ %a`" >> $1.route echo $TRACE_COMPLETE >| $1.ret_val if [ $(($D & 0x1000)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> traceroute -I $1 completed." >> $IP.logfile; fi } # Initialize the file that gets tested to detect timeout condition # The timed_function overwrites this with numerical return value when it succeeds echo "TIMED_OUT" >| $1.ret_val # Start the timed_function as a subshell in the background (_get_route $1 ) & # Allow up to $2 seconds for timed_function to complete but default to 15 seconds local count=0 local max_count=${2:-15} while [ $count -le $max_count ]; do ret_val=`cat $1.ret_val` if [ "$ret_val" != "TIMED_OUT" ]; then rm -f $1.ret_val RC_GET_ROUTE=$ret_val return $ret_val fi let count=count+1 if [ $(($D & 0x1000)) -ne 0 ]; then printf "." >> $IP.logfile; fi sleep 1 done # Getting here means timed_function timed out. Kill the subshell $! kill -9 $! &>/dev/null echo "traceroute timed out at: `date +%Y\-%b\-%d\ %H\:%M\:%S\ %a`" >> $1.route if [ $(($D & 0x1000)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> traceroute -I $1 timed out" >> $IP.logfile; fi rm -f $1.ret_val RC_GET_ROUTE=$TIMED_OUT return $TIMED_OUT } # get_route #------------------------------------------------------------------ # init_hour_array: Function that initializes the HOUR_ARRAY function init_hour_array () { local func_name=init_hour_array if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside init_hour_array" >> $IP.logfile; fi HOUR_ARRAY_TOP=648 HOUR_COL="Hour:00" HOUR_ARRAY[0]=$HOUR_COL local array_cnt=1 local row_mod=1 local hour_mod=1 while [ $array_cnt -lt $HOUR_ARRAY_TOP ]; do row_mod=`expr $array_cnt % $COLUMNS` if [ "$row_mod" -eq 0 ]; then # Change the hour value every 3 rows hour_mod=`expr $array_cnt % 27` if [ "$hour_mod" -eq 0 ]; then # Change the hour field as required case $HOUR_COL in Hour:00) HOUR_COL="Hour:01";; Hour:01) HOUR_COL="Hour:02";; Hour:02) HOUR_COL="Hour:03";; Hour:03) HOUR_COL="Hour:04";; Hour:04) HOUR_COL="Hour:05";; Hour:05) HOUR_COL="Hour:06";; Hour:06) HOUR_COL="Hour:07";; Hour:07) HOUR_COL="Hour:08";; Hour:08) HOUR_COL="Hour:09";; Hour:09) HOUR_COL="Hour:10";; Hour:10) HOUR_COL="Hour:11";; Hour:11) HOUR_COL="Hour:12";; Hour:12) HOUR_COL="Hour:13";; Hour:13) HOUR_COL="Hour:14";; Hour:14) HOUR_COL="Hour:15";; Hour:15) HOUR_COL="Hour:16";; Hour:16) HOUR_COL="Hour:17";; Hour:17) HOUR_COL="Hour:18";; Hour:18) HOUR_COL="Hour:19";; Hour:19) HOUR_COL="Hour:20";; Hour:20) HOUR_COL="Hour:21";; Hour:21) HOUR_COL="Hour:22";; Hour:22) HOUR_COL="Hour:23";; Hour:23) HOUR_COL="Hour:00";; esac fi HOUR_ARRAY[$array_cnt]=$HOUR_COL else HOUR_ARRAY[$array_cnt]=0 #HOUR_ARRAY[$array_cnt]=$array_cnt ########## DEBUG LINE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! fi let array_cnt=array_cnt+1 done } # init_hour_array #------------------------------------------------------------------------------ # read_pt_stats. Function that reads the $IP.stats file. function read_pt_stats () { local func_name=read_pt_stats if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside read_pt_stats" >> $IP.logfile; fi # The size of the week array is defined here. If changes are made to the weekly data # then the size must be adjusted accordingly. The $COLUMNS defines how many fields # are in a week's data. If a new column is added then that will need to change. ARRAY_TOP=63 COLUMNS=9 if [ ! -e $IP.stats ]; then # If the statistics file doesn't exist then initialize variables with default values PID=32767 TOTAL_PINGS=0 GOOD_PINGS=0 DROPPED_PINGS=0 PERCENT_LOSS=0 MIN_PING_TIME=10000.0 MIN_PING_DATE="YYYY-MMM-DD HH:MM:SS DDD" AVG_PING_TIME=0 MAX_PING_TIME=0 MAX_PING_DATE="YYYY-MMM-DD HH:MM:SS DDD" EMAILS_SENT=0 LAST_EMAIL="YYYY-MMM-DD HH:MM:SS DDD" TEST_STARTED=`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` OLD_WEEK_NUM=`date +%U` # Create the first week's statistics array # Load first array position and setup counters WEEK_DATE=`date +%y\-%b\-%d` ARRAY[0]=$WEEK_DATE local array_cnt=1 local row_mod=1 while [ $array_cnt -lt $ARRAY_TOP ]; do row_mod=`expr $array_cnt % $COLUMNS` if [ "$row_mod" -eq 0 ]; then ARRAY[$array_cnt]=$WEEK_DATE else ARRAY[$array_cnt]=0 fi let array_cnt=array_cnt+1 done # Initialize the HOUR_ARRAY if -hourly is on the command line if [ "$HOURLY_STATS" == "TRUE" ]; then init_hour_array fi # Initialize the $IP.sums file to hold the ping time sums for average calculations sums[0]=0 # Day of Week 0 - Sunday sums[1]=0 # Day of Week 1 - Monday sums[2]=0 # Day of Week 2 - Tuesday sums[3]=0 # Day of Week 3 - Wednesday sums[4]=0 # Day of Week 4 - Thursday sums[5]=0 # Day of Week 5 - Friday sums[6]=0 # Day of Week 6 - Saturday sums[7]=0 # Week sum sums[8]=0 # Overall sum # Save the newly initialized values to disk. write_pt_stats else # Load the sums array from the file. This loads each line into a separate cell. sums=(`cat $IP.sums`) # Initialize variable values from the statistics file PID=`cat $IP.stats | awk '/^Ping Test:.*/{print $7}'` TOTAL_PINGS=`cat $IP.stats | awk '/^Total pings.*/{print $3}'` GOOD_PINGS=`cat $IP.stats | awk '/^Total good pings.*/{print $4}'` DROPPED_PINGS=`cat $IP.stats | awk '/^Total dropped pings.*/{print $4}'` MIN_PING_TIME=`cat $IP.stats | awk '/^Minimum ping time.*/{print $4}'` MIN_PING_DATE=`cat $IP.stats | awk '/^Minimum ping time.*/{print $7,$8,$9}'` MAX_PING_TIME=`cat $IP.stats | awk '/^Maximum ping time.*/{print $4}'` MAX_PING_DATE=`cat $IP.stats | awk '/^Maximum ping time.*/{print $7,$8,$9}'` EMAILS_SENT=`cat $IP.stats | awk '/^Total emails sent.*/{print $4}'` LAST_EMAIL=`cat $IP.stats | awk '/^Last email sent.*/{print $4,$5,$6}'` TEST_STARTED=`cat $IP.stats | awk '/^Test started.*/{print $3,$4,$5}'` OLD_WEEK_NUM=`cat $IP.stats | awk '/^Week:.*/{print $2}'` # Read in the contents of $IP.stats read_file $IP.stats # Read in all the week statistical data from the file local array_cnt=0 # Initialize line_cnt to the first line of the array in the file local line_cnt=18 let local dash_mod=line_cnt+7 let dash_mod=dash_mod%8 # Parse through lines 18 through 24. That's where the weekly array data is kept. while [ $line_cnt -le 24 ]; do # Now parse the data from the line into ARRAY local loop_cnt=1 while [ $loop_cnt -le 11 ]; do case $loop_cnt in 1) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $1}'` ;; # Fields 2 and 3 are text to throw away. Decrement counter so it doesn't move 2) let array_cnt=array_cnt-1 ;; 3) let array_cnt=array_cnt-1 ;; 4) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $4}'` ;; 5) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $5}'` ;; 6) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $6}'` ;; 7) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $7}'` ;; 8) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $8}'` ;; 9) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $9}'` ;; 10) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $10}'` ;; 11) ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $11}'` ;; esac let array_cnt=array_cnt+1 let loop_cnt=loop_cnt+1 done # Point to the next line in the file let line_cnt=line_cnt+1 # Check if next line is dashes. If so, then skip it by incrementing line_cnt an extra time local line_mod=`expr $line_cnt % 8` if [ "$line_mod" -eq $dash_mod ]; then let line_cnt=line_cnt+1 fi done ARRAY_TOP=$array_cnt # Read in the hourly statistics if -hourly was on the command line if [ "$HOURLY_STATS" == "TRUE" ]; then # Check the number of lines in the $IP.stats file. The file might already exist # and now -hourly is on the command line and wasn't before. If that is the case # then need to initialize the array. local stats_file_size=`wc -l < $IP.stats` if [ "$NUM_LINES" -gt 26 ]; then # The $IP.stats file already has hourly data in it so read it in. # Parse through lines 21 through 120. That's where the hourly array data is kept. line_cnt=26 let dash_mod=line_cnt+3 let dash_mod=dash_mod%4 array_cnt=0 while [ $line_cnt -le 120 ]; do # Create a sed script to isolate one line from the file then read it in #echo "${line_cnt}p" >| $IP.sed # Now parse the data from the line into HOUR_ARRAY local loop_cnt=1 while [ $loop_cnt -le 11 ]; do case $loop_cnt in 1) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $1}'` ;; # Fields 2 and 3 are text to throw away. Decrement counter so it doesn't move 2) let array_cnt=array_cnt-1 ;; 3) let array_cnt=array_cnt-1 ;; 4) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $4}'` ;; 5) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $5}'` ;; 6) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $6}'` ;; 7) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $7}'` ;; 8) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $8}'` ;; 9) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $9}'` ;; 10) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $10}'` ;; 11) HOUR_ARRAY[$array_cnt]=`echo ${LINES[$line_cnt]} | awk '{print $11}'` ;; esac let array_cnt=array_cnt+1 let loop_cnt=loop_cnt+1 done # Point to the next line in the file let line_cnt=line_cnt+1 # Check if next line is dashes. If so, then skip it by incrementing line_cnt an extra time line_mod=`expr $line_cnt % 4` if [ "$line_mod" -eq $dash_mod ]; then let line_cnt=line_cnt+1 fi done HOUR_ARRAY_TOP=$array_cnt else # The $IP.stats file doesn't have hourly data in it. Need to initialize the hour array and save. init_hour_array write_pt_stats fi fi # Remove the temporary $IP.sed file rm -fr $IP.sed fi } # read_pt_stats #------------------------------------------------------------------------------ # write_week: Function that appends the current week's values to file named in $1 (Default: $IP.stats.tmp) function write_week () { local func_name=write_week if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside write_week (File name: $1)" >> $IP.logfile; fi local filename=${1:=$IP.stats.tmp} local array_0=0 local array_1=1 local array_2=2 local array_3=3 local array_4=4 local array_5=5 local array_6=6 local array_7=7 local array_8=8 while [ $array_8 -lt $ARRAY_TOP ]; do local row_mod=`expr $array_8 % $ARRAY_TOP` case $row_mod in 8) # First data row printf "%s - Good: %7d%8d%8d%8d%8d%8d%8d%8d\n" \ ${ARRAY[$array_0]} \ ${ARRAY[$array_1]} \ ${ARRAY[$array_2]} \ ${ARRAY[$array_3]} \ ${ARRAY[$array_4]} \ ${ARRAY[$array_5]} \ ${ARRAY[$array_6]} \ ${ARRAY[$array_7]} \ ${ARRAY[$array_8]} >> $filename ;; 17) # Second data row printf "%s - Dropped: %7d%8d%8d%8d%8d%8d%8d%8d\n" \ ${ARRAY[$array_0]} \ ${ARRAY[$array_1]} \ ${ARRAY[$array_2]} \ ${ARRAY[$array_3]} \ ${ARRAY[$array_4]} \ ${ARRAY[$array_5]} \ ${ARRAY[$array_6]} \ ${ARRAY[$array_7]} \ ${ARRAY[$array_8]} >> $filename ;; 26) # Third data row printf "%s - Loss: %7.3f%8.3f%8.3f%8.3f%8.3f%8.3f%8.3f%8.3f\n" \ ${ARRAY[$array_0]} \ ${ARRAY[$array_1]} \ ${ARRAY[$array_2]} \ ${ARRAY[$array_3]} \ ${ARRAY[$array_4]} \ ${ARRAY[$array_5]} \ ${ARRAY[$array_6]} \ ${ARRAY[$array_7]} \ ${ARRAY[$array_8]} >> $filename ;; 35) # Fourth data row printf "%s - Emails: %7d%8d%8d%8d%8d%8d%8d%8d\n" \ ${ARRAY[$array_0]} \ ${ARRAY[$array_1]} \ ${ARRAY[$array_2]} \ ${ARRAY[$array_3]} \ ${ARRAY[$array_4]} \ ${ARRAY[$array_5]} \ ${ARRAY[$array_6]} \ ${ARRAY[$array_7]} \ ${ARRAY[$array_8]} >> $filename ;; 44) # Fifth data row printf "%s - Min-ms: %7.1f%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f\n" \ ${ARRAY[$array_0]} \ ${ARRAY[$array_1]} \ ${ARRAY[$array_2]} \ ${ARRAY[$array_3]} \ ${ARRAY[$array_4]} \ ${ARRAY[$array_5]} \ ${ARRAY[$array_6]} \ ${ARRAY[$array_7]} \ ${ARRAY[$array_8]} >> $filename ;; 53) # Sixth data row printf "%s - Avg-ms: %7.1f%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f\n" \ ${ARRAY[$array_0]} \ ${ARRAY[$array_1]} \ ${ARRAY[$array_2]} \ ${ARRAY[$array_3]} \ ${ARRAY[$array_4]} \ ${ARRAY[$array_5]} \ ${ARRAY[$array_6]} \ ${ARRAY[$array_7]} \ ${ARRAY[$array_8]} >> $filename ;; 62) # Seventh data row printf "%s - Max-ms: %7.1f%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f\n" \ ${ARRAY[$array_0]} \ ${ARRAY[$array_1]} \ ${ARRAY[$array_2]} \ ${ARRAY[$array_3]} \ ${ARRAY[$array_4]} \ ${ARRAY[$array_5]} \ ${ARRAY[$array_6]} \ ${ARRAY[$array_7]} \ ${ARRAY[$array_8]} >> $filename echo "---- $SCRIPT_VER $SCRIPT_DATE ----------------------------------------------------------------" >> $filename ;; esac let array_0=array_0+9 let array_1=array_1+9 let array_2=array_2+9 let array_3=array_3+9 let array_4=array_4+9 let array_5=array_5+9 let array_6=array_6+9 let array_7=array_7+9 let array_8=array_8+9 done # Write the hourly statistics if -hourly is on the command line if [ "$HOURLY_STATS" == "TRUE" ]; then array_0=0 array_1=1 array_2=2 array_3=3 array_4=4 array_5=5 array_6=6 array_7=7 array_8=8 while [ $array_8 -lt $HOUR_ARRAY_TOP ]; do local hour_mod=`expr $array_8 % 27` case $hour_mod in 8) # First data row printf "%s - Good: %7d%8d%8d%8d%8d%8d%8d%8d\n" \ ${HOUR_ARRAY[$array_0]} \ ${HOUR_ARRAY[$array_1]} \ ${HOUR_ARRAY[$array_2]} \ ${HOUR_ARRAY[$array_3]} \ ${HOUR_ARRAY[$array_4]} \ ${HOUR_ARRAY[$array_5]} \ ${HOUR_ARRAY[$array_6]} \ ${HOUR_ARRAY[$array_7]} \ ${HOUR_ARRAY[$array_8]} >> $filename ;; 17) # Second data row printf "%s - Dropped: %7d%8d%8d%8d%8d%8d%8d%8d\n" \ ${HOUR_ARRAY[$array_0]} \ ${HOUR_ARRAY[$array_1]} \ ${HOUR_ARRAY[$array_2]} \ ${HOUR_ARRAY[$array_3]} \ ${HOUR_ARRAY[$array_4]} \ ${HOUR_ARRAY[$array_5]} \ ${HOUR_ARRAY[$array_6]} \ ${HOUR_ARRAY[$array_7]} \ ${HOUR_ARRAY[$array_8]} >> $filename ;; 26) # Third data row printf "%s - Loss: %7.3f%8.3f%8.3f%8.3f%8.3f%8.3f%8.3f%8.3f\n" \ ${HOUR_ARRAY[$array_0]} \ ${HOUR_ARRAY[$array_1]} \ ${HOUR_ARRAY[$array_2]} \ ${HOUR_ARRAY[$array_3]} \ ${HOUR_ARRAY[$array_4]} \ ${HOUR_ARRAY[$array_5]} \ ${HOUR_ARRAY[$array_6]} \ ${HOUR_ARRAY[$array_7]} \ ${HOUR_ARRAY[$array_8]} >> $filename # Put a double dash line at the end of the 23rd hour let test_for_top=array_8+1 if [ $test_for_top -eq $HOUR_ARRAY_TOP ]; then echo "======================================================================================" >> $filename else echo "--------------------------------------------------------------------------------------" >> $filename fi ;; esac let array_0=array_0+9 let array_1=array_1+9 let array_2=array_2+9 let array_3=array_3+9 let array_4=array_4+9 let array_5=array_5+9 let array_6=array_6+9 let array_7=array_7+9 let array_8=array_8+9 done fi } # write_week #------------------------------------------------------------------------------ # write_pt_stats: Function that outputs the statistics to $IP.stats function write_pt_stats { local func_name=write_pt_stats if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside write_pt_stats" >> $IP.logfile; fi # Use a temp file while building up the statistics. cat << EOF >| $IP.stats.tmp ------------------------------------------------------- $SCRIPT_VER $SCRIPT_DATE ------------- Ping Test: $HOST to $IP (pid: $PID ) Total pings: $TOTAL_PINGS Total good pings: $GOOD_PINGS Total dropped pings: $DROPPED_PINGS Percent loss: $PERCENT_LOSS Minimum ping time: $MIN_PING_TIME ms on $MIN_PING_DATE Average ping time: $AVG_PING_TIME ms Maximum ping time: $MAX_PING_TIME ms on $MAX_PING_DATE Total emails sent: $EMAILS_SENT Last email sent: $LAST_EMAIL Test started: $TEST_STARTED Threshold: $DROPPED_THRESHOLD dropped packets before email sent Ping rate: $PING_RATE seconds between pings Email address: $EMAIL_ADDRESS Week: $OLD_WEEK_NUM Sun Mon Tue Wed Thu Fri Sat Week ====================================================================================== EOF write_week $IP.stats.tmp # Now that the $IP.stats.tmp file has been created mv it to $IP.stats. # A temp file was used so that the $IP.stats file won't # be corrupted if the script is killed in the middle of write_pt_stats. mv $IP.stats.tmp $IP.stats # Save current sums array to $IP.sums. Put each value on a separate line. printf "%f\n%f\n%f\n%f\n%f\n%f\n%f\n%f\n%f\n" \ ${sums[0]} \ ${sums[1]} \ ${sums[2]} \ ${sums[3]} \ ${sums[4]} \ ${sums[5]} \ ${sums[6]} \ ${sums[7]} \ ${sums[8]} >| $IP.sums } # write_pt_stats #------------------------------------------------------------------------------ # display_env # Function to display the values of environment variables function display_env () { local func_name=display_env if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside display_env" >> $IP.logfile; fi echo "----- CURRENT ENVIRONMENT VARIABLE VALUES -----------------" >> $IP.logfile echo "PID: $PID" >> $IP.logfile echo "HOST: $HOST" >> $IP.logfile echo "CRONJOB: $CRONJOB" >> $IP.logfile echo "DELETE_STATS: $DELETE_STATS" >> $IP.logfile echo "DELETE_LOGS: $DELETE_LOGS" >> $IP.logfile echo "DROPPED_THRESHOLD: $DROPPED_THRESHOLD" >> $IP.logfile echo "EMAIL_ADDRESS: $EMAIL_ADDRESS" >> $IP.logfile echo "IP: $IP" >> $IP.logfile echo "KILL_PT: $KILL_PT" >> $IP.logfile echo "PING_RATE: $PING_RATE" >> $IP.logfile echo "SHOW_STATS: $SHOW_STATS" >> $IP.logfile echo "TRACEROUTE_TIMEOUT: $TRACEROUTE_TIMEOUT" >> $IP.logfile echo "WORKING_DIR: $WORKING_DIR" >> $IP.logfile echo "WEB_PAGE_UPDATE_RATE: $WEB_PAGE_UPDATE_RATE" >> $IP.logfile echo "HOURLY_STATS: $HOURLY_STATS" >> $IP.logfile echo "DASH_HOURLY: $DASH_HOURLY" >> $IP.logfile echo "-----------------------------------------------------------" >> $IP.logfile return 0 } # display_env #------------------------------------------------------------------------------ # test_pid: Function that returns ACTIVE if passed pid is active else DEAD # Pass PID to test in $1 # It puts first 8 characters of the name of the program associated with the PID in $TEST_PID_PGM function test_pid () { local func_name=test_pid if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside test_pid (\$1: $1)" >> $IP.logfile; fi # 040224 jrl - The following approach to isolating the pid may seem less efficient than doing # it all in one line. I was having intermittent problems with the "set" declaring "no match" and # bombing out of the script, even though the pid was really there. This approach, writing the ps output # to a file and then using awk to match everything and print field 5 seems to be reliable all the time. # 040516 jrl - Seemed to be getting dead pid reported even though it was alive. Put test in a # while loop to try it three times before declaring it failed. See if it makes a difference. # 040518 jrl - On Solaris the CMD field returned by "ps -p pid" is only 8 characters wide so it # truncates the program name. Use ${TEST_PID_PGM:0:8} so that all names returned by this program # are the same length for testing later on. # 050317 jrl - Cygwin uses "oldps" which has different options and also has data in different # fields than Linux and Solaris. This function now performs differently based on OS. COUNT=3 while [ $COUNT -ne 0 ]; do case $OS_TYPE in 5.8*) # Solaris 8 echo `$PS -p $1` >| $$.test.pid TEST_PID=`cat $$.test.pid | awk '/.*/{print $5}'` TEST_PID_PGM=`cat $$.test.pid | awk '/.*/{print $8}'` TEST_PID_PGM=${TEST_PID_PGM:0:8} ;; 2.*) # Linux 2.x kernels echo `$PS -p $1` >| $$.test.pid TEST_PID=`cat $$.test.pid | awk '/.*/{print $5}'` TEST_PID_PGM=`cat $$.test.pid | awk '/.*/{print $8}'` TEST_PID_PGM=${TEST_PID_PGM:0:8} ;; 1.5*) # Cygwin: Requires procps package installed echo `$PS p $1` >| $$.test.pid TEST_PID=`cat $$.test.pid | awk '/.*/{print $6}'` TEST_PID_PGM=`cat $$.test.pid | awk '/.*/{print $11}'` if [ "$TEST_PID" != "" ]; then # Only do this if PID exists so that there will be a program name TEST_PID_PGM=`basename $TEST_PID_PGM` fi TEST_PID_PGM=${TEST_PID_PGM:0:8} ;; *) # Unknown OS echo "ERROR [$LINENO]$func_name> Unknown OS" | tee -a $IP.logfile exit ;; esac if [ $(($D & 0x40)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Contents of $$.test.pid" >> $IP.logfile echo "`cat $$.test.pid`" >> $IP.logfile fi rm -f $$.test.pid if [ ! -z $TEST_PID ] && [ $TEST_PID -eq $1 ]; then RC_TEST_PID=$ACTIVE return $ACTIVE fi let COUNT=COUNT-1 # Wait a little bit to give the OS some time to clean up. False negatives occur at times. # I'm hoping this will help. sleep 2 done RC_TEST_PID=$DEAD return $DEAD } # test_pid #------------------------------------------------------------------------------ # yes_or_no: Function that uses $1 as a prompt and returns $YES or $NO as an answer function yes_or_no () { local func_name=yes_or_no if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside yes_or_no" >> $IP.logfile; fi while [ 1 -eq 1 ]; do read -a response -p "$1 (yes/no): " case ${response[0]} in yes) return $YES ;; no) return $NO ;; *) echo "Invalid choice: $response"; echo;; esac done } # yes_or_no #------------------------------------------------------------------------------ # rename_files: Function that renames all files to new name passed in $1 function rename_files () { local func_name=rename_files if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside rename_files" >> $IP.logfile; fi # Make sure $IP exists before trying to rename files if [ ! -e $IP.stats ]; then echo "ERROR: Invalid \$IP entered on command line: $IP" return -1 fi # Make sure a new file name was passed if [ "$1" == "" ]; then echo "ERROR: Missing IP or domain name" return -1 else NEW_IP=$1 fi # Only rename files if test isn't currently active. Warn and exit if test is active." get_pt_pid test_pid "$RC_GET_PT_PID" if [ "$RC_TEST_PID" -eq $ACTIVE ]; then echo "ERROR: $IP test still active" echo "Use \"pt $IP -k\" to stop test." echo "Be sure to modify crontab to reflect new name." else # Rename all the files associated with this test yes_or_no "Do you really want to rename $IP.filename to $NEW_IP.filename" RC=$? if [ "$RC" -eq $YES ]; then if [ -e $IP.copy ]; then mv $IP.copy $NEW_IP.copy; fi if [ -e $IP.emails ]; then mv $IP.emails $NEW_IP.emails; fi if [ -e $IP.heartbeat ]; then mv $IP.heartbeat $NEW_IP.heartbeat; fi if [ -e $IP.html ]; then mv $IP.html $NEW_IP.html; fi if [ -e $IP.logfile ]; then mv $IP.logfile $NEW_IP.logfile; fi if [ -e $IP.logfile.1.zip ]; then mv $IP.logfile.1.zip $NEW_IP.logfile.1.zip; fi if [ -e $IP.logfile.2.zip ]; then mv $IP.logfile.2.zip $NEW_IP.logfile.2.zip; fi if [ -e $IP.logfile.3.zip ]; then mv $IP.logfile.3.zip $NEW_IP.logfile.3.zip; fi if [ -e $IP.logfile.4.zip ]; then mv $IP.logfile.4.zip $NEW_IP.logfile.4.zip; fi if [ -e $IP.route ]; then mv $IP.route $NEW_IP.route; fi if [ -e $IP.stats ]; then mv $IP.stats $NEW_IP.stats; fi if [ -e $IP.sums ]; then mv $IP.sums $NEW_IP.sums; fi if [ -e $IP.weeks ]; then mv $IP.weeks $NEW_IP.weeks; fi else echo "Not renaming files" fi fi } # rename_files #------------------------------------------------------------------------------ # log_entry # Function that adds an entry to an existing logfile or creates a new one if the logfile doesn't exist function log_entry () { local func_name=log_entry if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside log_entry" >> $IP.logfile; fi if [ -e $IP.logfile ]; then echo "-------------------- $SCRIPT_VER $SCRIPT_DATE ------------------------------------------" >> $IP.logfile else echo "-------------------- $SCRIPT_VER $SCRIPT_DATE ------------------------------------------" >| $IP.logfile fi cat $IP.stats >> $IP.logfile if [ -e $IP.weeks ]; then cat $IP.weeks >> $IP.logfile fi echo "------------------------------------------------------------------------------" >> $IP.logfile if [ -e $IP.sums ]; then echo "Contents of $IP.sums file:" >> $IP.logfile cat $IP.sums >> $IP.logfile echo "------------------------------------------------------------------------------" >> $IP.logfile fi } # log_entry #------------------------------------------------------------------------------ # send_email # $1 - The email subject, ie. Down or Up function send_email () { local func_name=send_email if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside send_email" >> $IP.logfile; fi local email_time=`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` echo "$email_time - $1: $HOST to $IP. email sent to $EMAIL_ADDRESS. Threshold: $DROPPED_THRESHOLD Ping rate: $PING_RATE" >> $IP.logfile cat $IP.stats >> $IP.logfile if [ -e $IP.weeks ]; then cat $IP.weeks >> $IP.logfile fi get_route $IP $TRACEROUTE_TIMEOUT cat $IP.route >> $IP.logfile echo "$email_time - $1: $HOST to $IP. Threshold: $DROPPED_THRESHOLD Ping rate: $PING_RATE email sent to $EMAIL_ADDRESS." >> $IP.emails if [ "$1" = Down ]; then echo "---------------------------------------- $SCRIPT_VER $SCRIPT_DATE ----- " >> $IP.emails fi if [ $(($D & 0x8)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> $email_time - $1 $HOST to $IP. email sent to $EMAIL_ADDRESS. Threshold: $DROPPED_THRESHOLD Ping rate: $PING_RATE" >> $IP.logfile fi let EMAILS_SENT=EMAILS_SENT+1 write_pt_stats # Create the text of the email that will be sent # At the top of the email we want two lines giving the Up and Down times echo "$email_time - $1" >| /tmp/$$.email if [ "$1" = Down ]; then echo "$LAST_EMAIL - Up" >> /tmp/$$.email else echo "$LAST_EMAIL - Down" >> /tmp/$$.email fi cat $IP.stats >> /tmp/$$.email if [ -e $IP.weeks ]; then cat $IP.weeks >> /tmp/$$.email fi echo "" >> /tmp/$$.email echo "----- traceroute ---------------------------------------------------------------" >> /tmp/$$.email cat $IP.route >> /tmp/$$.email echo "" >> /tmp/$$.email echo "----- email log ---------------------------------------------------------------" >> /tmp/$$.email if [ -e $IP.emails ]; then cat $IP.emails >> /tmp/$$.email fi cat /tmp/$$.email | $MAIL -s "pt: ${1}: $IP from $HOST" $EMAIL_ADDRESS rm -f /tmp/$$.email LAST_EMAIL=$email_time write_pt_stats } # send_email #------------------------------------------------------------------------------ # check_valid_ip function check_valid_ip () { local func_name=check_valid_ip if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside check_valid_ip" >> $IP.logfile; fi if [ $IP = not_entered ]; then echo "ERROR: IP required on command line. Exiting" exit fi } # check_valid_ip #------------------------------------------------------------------------------ # process_command_line function process_command_line () { local func_name=process_command_line if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside process_command_line" >> $IP.logfile; fi while [ $# -ne 0 ] do case $1 in -d) #debug mode # Command line -d overrides environment variable PT_DEBUG_FLAGS if it exists, which overrides debug_flags file if it exists D=$2 if [ "$D" != "0" ] && [ $(($D & 0x80000000)) -eq 0 ]; then echo "[$LINENO]$func_name:$$> Debug flags changed to: $D at `date +%Y\-%b\-%d\ %H\:%M\:%S\ %a`" >> $IP.logfile fi if [ $(($D & 0x200000)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -d" >> $IP.logfile; fi shift ;; -c) #cronjob processing if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -c" >> $IP.logfile; fi CRONJOB=TRUE ;; -ds) #delete statistics if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -ds" >> $IP.logfile; fi DELETE_STATS=TRUE check_valid_ip yes_or_no "Do you really want to delete statistics? " RC=$? if [ "$RC" -eq $YES ]; then echo "Deleting statistics" rm -f $IP.stats rm -f $IP.weeks rm -f $IP.route rm -f $IP.sums else echo "Exiting without deleting statistics" fi exit ;; -dl) #delete logs if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -dl" >> $IP.logfile; fi DELETE_LOGS=TRUE check_valid_ip yes_or_no "Do you really want to delete logfiles? " RC=$? if [ "$RC" -eq $YES ]; then echo "Deleting logfiles" rm -f $IP.logfile rm -f $IP.logfile.*.zip else echo "Exiting without deleting logfiles" fi exit ;; -e) #email address if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -e" >> $IP.logfile; fi EMAIL_ADDRESS=$2 shift ;; -h) #show help if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -h" >> $IP.logfile; fi display_help exit;; -hd) #show debug help if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -hd" >> $IP.logfile; fi display_debug_help exit;; -hourly) #Enable hourly tracking of ping statistics if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -hourly" >> $IP.logfile; fi HOURLY_STATS=TRUE DASH_HOURLY="-hourly" ;; -k) #kill running instance if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -k" >> $IP.logfile; fi KILL_PT=TRUE check_valid_ip get_pt_pid yes_or_no "Do you really want to kill pid ${RC_GET_PT_PID}? " RC=$? if [ "$RC" -eq $YES ]; then echo "Killing pid $RC_GET_PT_PID" kill -9 $RC_GET_PT_PID echo "Killing pid $RC_GET_PT_PID using -k" >> $IP.logfile else echo "Exiting without killing pid" fi exit ;; -m) #make a $IP.copy script for web page updating if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -m" >> $IP.logfile; fi make_copy_script echo "Created: $IP.copy in `pwd`" echo "Be sure to modify it for your particular situation" exit;; -mv) # Rename files associated with this test if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -mv" >> $IP.logfile; fi if [ "$2" == "" ]; then echo "ERROR: You must provide name or IP to rename files." else rename_files $2 fi exit;; -r) #ping rate if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -r" >> $IP.logfile; fi if [ "$2" -le 0 ]; then display_help echo "Error: ping rate must be greater than zero" exit fi PING_RATE=$2 shift ;; -s) #show statistics if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -s" >> $IP.logfile; fi SHOW_STATS=TRUE check_valid_ip cat $IP.stats if [ -e $IP.weeks ]; then cat $IP.weeks fi if [ -e $IP.route ]; then cat $IP.route fi if [ -e $IP.emails ]; then cat $IP.emails fi cat $IP.sums exit ;; -t) #dropout email threshold if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -t" >> $IP.logfile; fi if [ "$2" -le 0 ]; then display_help echo "Error: email threshold must be greater than zero" exit fi DROPPED_THRESHOLD=$2 shift ;; -tto) #traceroute time out value if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -tto" >> $IP.logfile; fi if [ "$2" -lt 0 ]; then display_help echo "Error: traceroute timeout value must be positive number" exit fi TRACEROUTE_TIMEOUT=$2 shift ;; -wr) #web page update rate in minutes if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -wr" >> $IP.logfile; fi if [ "$2" -lt 0 ]; then display_help echo "Error: web page update rate must be positive number" exit fi WEB_PAGE_UPDATE_RATE=$2 shift ;; *) #IP address if [ $(($D & 0x2)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> In case -*" >> $IP.logfile; fi export IP=$1;; esac # Don't shift command line if processing at last argument if [ $# -ne 0 ]; then shift fi done } # process_command_line #------------------------------------------------------------------------------ # web_page. Function that creates a web page and copies it somewhere periodically. # If the file $IP.copy exists then the web page is created and $IP.copy # is called with the name of the web page in $1. The $IP.copy file is unique # and must be self contained to copy the file where it needs to be. This can # be to another drive on the LAN or could be using scp. Use "pt IP -m" to make # a template that can be modified for your needs. function web_page () { local func_name=web_page if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside web_page" >> $IP.logfile; fi # Check if $IP.copy exists and exit if it doesn't if [ ! -e $IP.copy ]; then return; fi # Getting here means $IP.copy exists # Only proceed if web page update counter is zero if [ $WEB_PAGE_UPDATE_CNTR -gt 0 ]; then let WEB_PAGE_UPDATE_CNTR=WEB_PAGE_UPDATE_CNTR-1 return else WEB_PAGE_UPDATE_CNTR=$WEB_PAGE_UPDATE_RATE fi # Getting here means it's time to create a new web page # Determine the current state of the site if [ "$DROPPED_EMAIL_SENT" = "TRUE" ]; then CURRENT_STATE=DOWN else CURRENT_STATE=UP fi # Build the web page cat << EOF >| $IP.html Ping statistics: $HOST to $IP

Ping statistics: $HOST to $IP

Time: `date +%Y\-%b\-%d\ %H\:%M\:%S\ %a`

Current state: $CURRENT_STATE

Overall Statistics since $TEST_STARTED

EOF

# Output the overall statistics
cat $IP.stats >> $IP.html
echo "
" >> $IP.html if [ -e $IP.weeks ]; then # Output the weekly statistics echo "

Weekly Statistics since $TEST_STARTED

" >>$IP.html echo "
" >> $IP.html
	cat $IP.weeks >> $IP.html
	echo "
" >> $IP.html fi if [ -e $IP.route ]; then echo "

traceroute from $HOST to $IP

" >>$IP.html echo "
" >> $IP.html
	cat $IP.route >> $IP.html
	echo "
" >> $IP.html fi if [ -e $IP.emails ]; then echo "

email log of emails sent

" >>$IP.html echo "
" >> $IP.html
	cat $IP.emails >> $IP.html
	echo "
" >> $IP.html fi echo "

Values in the sums array used to calculate average ping times

" >>$IP.html echo "
" >> $IP.html
cat $IP.sums >> $IP.html
echo "
" >> $IP.html echo "

Current users, uptime, and load average

" >>$IP.html echo "
" >> $IP.html
$W >> $IP.html
echo "
" >> $IP.html echo "

Current processes

" >>$IP.html echo "
" >> $IP.html
case $OS_TYPE in
   5.8*) # Solaris 8
      $PS -ef >> $IP.html
      ;;

   2.*) # Linux 2.x kernels
      $PS axf >> $IP.html
      ;;

   1.5*) # Cygwin: Requires procps package installed
      $PS axf >> $IP.html
      ;;

   *) # Unknown OS
      echo "ERROR [$LINENO]$func_name> Unknown OS" | tee -a $IP.logfile
      exit
      ;;
esac
echo "
" >> $IP.html echo "

Current netstat

" >>$IP.html echo "
" >> $IP.html
$NETSTAT >> $IP.html
echo "
" >> $IP.html echo " " >> $IP.html # Use the $IP.copy script in the back ground to transfer the web page where it needs to go. ./$IP.copy $IP.html & } # web_page #------------------------------------------------------------------------------ # make_copy_script: Function to create a template $IP.copy script function make_copy_script () { local func_name=make_copy_script if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside make_copy_script" >> $IP.logfile; fi cat << EOF >| $IP.copy #!/bin/bash #----- ping_test ($SCRIPT_VER $SCRIPT_DATE) -------------------------------------------- # This is the $IP.copy script called by pt to update the website. The name of the # html file is passed in \$1 so that it can be renamed as needed. This might be # required if multiple sites are testing the same destination IP and all the # web pages are in the same directory on the webserver. # # The following environment variables are exported by pt and can be used in this script: # HOST Name of the host running the test # IP The IP address (or domain name) of the site being tested # SCP The full path to the scp program on the host where the test is running # # The copying method will vary depending on how to access the web server. If it # is on the same LAN as the machine running the test, then a simple "cp" command # might work. If the webserver is on a remote machine then it may be necessary # to use "scp" to do the copy using already established public/private RSA key # access. Examples of both are given below. # LAN copy. This simple line copies the file to the correct webserver directory. #cp -f \$1 /var/www/html/ping_test/\${IP}_from_\$HOST.html # SCP copy. Use this line if transferring the file to a remote webserver. The "-F" option # tells scp to use the named ssh configuration file. Below are the settings that might # be in such a file. See manpage for ssh_config(5) for complete descriptions: # # Host loader # HostName linux-beast # Port = 2222 # UserKnownHostsFile = /home/jlarsen/ssh_tunnel/linux-beast/known_hosts # User = jlarsen # IdentityFile = /home/jlarsen/ssh_tunnel/linux-beast/id_rsa.fhc-beast # # Using the settings above and the private key (with no pass phrase) the transfer can be # done by a cron job. #\$SCP -F /path/to/ssh/config/file \$1 loader:/path/to/webserver/directory/\${IP}_from_\$HOST.html 2>/dev/null EOF # Set execute permissions on the $IP.copy script chmod 755 $IP.copy } # make_copy_script #------------------------------------------------------------------------------ # logfile function logfile () { local func_name=logfile if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside logfile" >> $IP.logfile; fi # Compress logfile once a week and save up to $NUM_LOGS compressed logs CURRENT_DOW=`date +%a` CURRENT_HOUR=`date +%H` if [ "$CURRENT_DOW" = "Sun" ] && [ "$CURRENT_HOUR" = "00" ] && [ -e $IP.logfile ]; then if [ -e $IP.logfile.1.zip ]; then LOGFILE_MONTH=`$LS -n $IP.logfile.1.zip | awk '/.*/{print $6}'` LOGFILE_DAY=`$LS -n $IP.logfile.1.zip | awk '/.*/{print $7}'` CURRENT_MONTH=`date +%b` CURRENT_DAY=`date +%d` fix_number $CURRENT_DAY CURRENT_DAY=$? if [ "$CURRENT_DAY" != "$LOGFILE_DAY" ] || [ "$CURRENT_MONTH" != "$LOGFILE_MONTH" ]; then # Rotate the old logfiles local cnt_2=${NUM_LOGS:=4} let local cnt_1=cnt_2-1 while [ $cnt_1 -gt 0 ]; do if [ -e $IP.logfile.$cnt_1.zip ]; then mv $IP.logfile.$cnt_1.zip $IP.logfile.$cnt_2.zip fi let cnt_1=cnt_1-1 let cnt_2=cnt_2-1 done if [ -e $IP.logfile ]; then # Append current stats and email log to the logfile cat $IP.stats >> $IP.logfile if [ -e $IP.weeks ]; then cat $IP.weeks >> $IP.logfile fi if [ -e $IP.emails ]; then cat $IP.emails >> $IP.logfile fi if [ -x $ZIP_PGM ]; then $ZIP_PGM $IP.logfile.1.zip $IP.logfile else # No zip program. Copy file and rename it. cp $IP.logfile $IP.logfile.1.zip touch $IP.logfile.1.zip fi # Remove old logfile else entries just get appended to it rm -f $IP.logfile # Create a new logfile log_entry fi # Send email with weekly stats local email_time=`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` echo "$email_time - $1 - Weekly summary" >| /tmp/$$.email cat $IP.stats >> /tmp/$$.email if [ -e $IP.weeks ]; then cat $IP.weeks >> /tmp/$$.email fi if [ -e $IP.emails ]; then echo "" >> /tmp/$$.email echo "----- email log ---------------------------------------------------------------" >> /tmp/$$.email cat $IP.emails >> /tmp/$$.email fi cat /tmp/$$.email | $MAIL -s "pt: Weekly stats for $IP from $HOST" $EMAIL_ADDRESS rm -f /tmp/$$.email fi else # No $IP.logfile.1.zip so compress logfile and create new one if [ -e $IP.logfile ]; then cat $IP.stats >> $IP.logfile if [ -e $IP.weeks ]; then cat $IP.weeks >> $IP.logfile fi if [ -e $IP.emails ]; then cat $IP.emails >> $IP.logfile fi if [ -x $ZIP_PGM ]; then $ZIP_PGM $IP.logfile.1.zip $IP.logfile else # No zip program. Copy file and rename it. cp $IP.logfile $IP.logfile.1.zip touch $IP.logfile.1.zip fi # Remove old logfile else entries just get appended to it rm -f $IP.logfile # Create a new logfile log_entry fi # Send email with weekly stats local email_time=`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` echo "$email_time - $1 - Weekly summary" >| /tmp/$$.email cat $IP.stats >> /tmp/$$.email if [ -e $IP.weeks ]; then cat $IP.weeks >> /tmp/$$.email fi echo "" >> /tmp/$$.email get_route $IP $TRACEROUTE_TIMEOUT echo "----- traceroute ---------------------------------------------------------------" >> /tmp/$$.email cat $IP.route >> /tmp/$$.email if [ -e $IP.emails ]; then echo "" >> /tmp/$$.email echo "----- email log ---------------------------------------------------------------" >> /tmp/$$.email cat $IP.emails >> /tmp/$$.email fi cat /tmp/$$.email | $MAIL -s "pt: Weekly stats for $IP from $HOST" $EMAIL_ADDRESS rm -f /tmp/$$.email fi fi } # logfile #------------------------------------------------------------------------------ # insert_week: Function that inserts a week to the $IP.weeks file and clears the array function insert_week () { local func_name=insert_week if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside insert_week" >> $IP.logfile; fi # Only insert the week when the old week number is different than the current week WEEK_NUM=`date +%U` if [ "$OLD_WEEK_NUM" -ne "$WEEK_NUM" ]; then # Week numbers are different so need to insert new week into $IP.weeks write_week $IP.weeks.tmp cat $IP.weeks >> $IP.weeks.tmp mv $IP.weeks.tmp $IP.weeks # Initialize the statistics array because a new week is starting WEEK_DATE=`date +%y\-%b\-%d` ARRAY[0]=$WEEK_DATE local array_cnt=1 local row_mod=1 while [ $array_cnt -lt $ARRAY_TOP ]; do row_mod=`expr $array_cnt % $COLUMNS` if [ "$row_mod" -eq 0 ]; then ARRAY[$array_cnt]=$WEEK_DATE else ARRAY[$array_cnt]=0 fi let array_cnt=array_cnt+1 done # Zero out the $IP.sums file that holds the ping time sums for average calculations sums[0]=0 # Day of Week 0 - Sunday sums[1]=0 # Day of Week 1 - Monday sums[2]=0 # Day of Week 2 - Tuesday sums[3]=0 # Day of Week 3 - Wednesday sums[4]=0 # Day of Week 4 - Thursday sums[5]=0 # Day of Week 5 - Friday sums[6]=0 # Day of Week 6 - Saturday sums[7]=0 # Week sum # sums[8] is the overall sum. It doesn't get cleared. # Initialize the HOUR_ARRAY if -hourly is on the command line if [ "$HOURLY_STATS" == "TRUE" ]; then init_hour_array fi fi OLD_WEEK_NUM=$WEEK_NUM write_pt_stats } # insert_week #------------------------------------------------------------------------------ # get_pt_pid: Function that returns the pid for "pt" in RC_GET_PT_PID function get_pt_pid () { local func_name=get_pt_pid if [ -e $IP.stats ]; then RC_GET_PT_PID=`cat $IP.stats | awk '/^Ping Test:.*/{print $7}'` else RC_GET_PT_PID=66666 fi if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside get_pt_pid (pid: $RC_GET_PT_PID)" >> $IP.logfile; fi } # get_pt_pid #------------------------------------------------------------------------------ # fix_number: Function that removes leading zero from seconds and minutes # returned by `date +%M` and `date +%S` # $1 - Number to fix function fix_number () { local func_name=fix_number if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside fix_number (number: $1)" >> $IP.logfile; fi case $1 in 00) return 0;; 01) return 1;; 02) return 2;; 03) return 3;; 04) return 4;; 05) return 5;; 06) return 6;; 07) return 7;; 08) return 8;; 09) return 9;; *) return $1;; esac } # fix_number #------------------------------------------------------------------------------ # sleep_time: Function that adjusts sleep time based on script execution time # and the time it takes the OS to queue the task and wake it up. # The desired ping rate is set on the command line. This function subtracts # all the overhead time from the sleep time so the ping rate is maintained. # This function gets called at the end of the while loop. function sleep_time () { local func_name=sleep_time if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside sleep_time" >> $IP.logfile; fi # Capture the current minute and second value END_MINUTE=`date +%M` END_SECOND=`date +%S` WHILE_LOOP_END=`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` # Need to remove leading zeros from the stored values so math will work fix_number $END_MINUTE END_MINUTE=$? fix_number $END_SECOND END_SECOND=$? fix_number $START_MINUTE START_MINUTE=$? fix_number $START_SECOND START_SECOND=$? # Convert minutes to seconds and add to seconds let START_TIME=START_MINUTE*60 let START_TIME=START_TIME+START_SECOND let END_TIME=END_MINUTE*60 let END_TIME=END_TIME+END_SECOND # If END_TIME is less than START_TIME then END_TIME is in a different # hour and 3600 needs to be added to it so the math works. This limits # ping rate to be at least once an hour. That should never be a problem # because ping rate usually is in the seconds range. if [ $END_TIME -lt $START_TIME ]; then let END_TIME=END_TIME+3600 fi # Now calculate how long it took the script to run let SCRIPT_TIME=END_TIME-START_TIME # Calculate the time it took the OS to queue the job by subtracting the # the predicted start time prediction from the actual start time. If # start time is less than predicted start time then need to add 3600 # to start time because it is in a different hour. if [ $START_TIME -lt $PREDICTED_START ]; then let START_TIME=START_TIME+3600 fi let OS_TIME=START_TIME-PREDICTED_START # Now add to this the time it took the script to run let NON_SLEEP_TIME=SCRIPT_TIME+OS_TIME # If NON_SLEEP_TIME is greater than PING_RATE then don't sleep at all. If # it is less than PING_RATE then subtract NON_SLEEP_TIME from PING_RATE and # sleep for that long before waking up. if [ $NON_SLEEP_TIME -lt $PING_RATE ]; then let SLEEP_TIME=PING_RATE-NON_SLEEP_TIME # Calculate the predicted start time so OS time can be calculated let PREDICTED_START=END_TIME+SLEEP_TIME # sleep sometimes exits early for unknown reasons. This happens a lot on cygwin systems # and occasionally on Linux. It hasn't been observed on Solaris. The following lines # detect, correct and log this event. # Capture the current time as the number of seconds since 1980 or 1970 depending on OS CURRENT_TIME=`date +%s` # Add the sleep time to it to know wakeup time let WAKEUP_TIME=CURRENT_TIME+SLEEP_TIME sleep $SLEEP_TIME # Now check if the time actually elapsed or not CURRENT_TIME=`date +%s` if [ $CURRENT_TIME -lt $WAKEUP_TIME ]; then local count=0 local times="" # Getting here means "sleep" ended early. Sleep the difference. # Stay in this while loop until the time has elapsed while [ $CURRENT_TIME -lt $WAKEUP_TIME ]; do let sleep_time=WAKEUP_TIME-CURRENT_TIME sleep $sleep_time CURRENT_TIME=`date +%s` let count=count+1 times="$times `date +%H\:%M\:%S\ %a`" done # Log event echo "`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` [$LINENO] $func_name: sleep woke up early $count times at: $times" >> $IP.logfile fi else # Need to test if 3600 was added to END_TIME and remove it if it was if [ $END_TIME -gt 3599 ]; then let END_TIME=END_TIME-3600 fi # Use the END_TIME as the PREDICTED_START since not going to sleep at all PREDICTED_START=$END_TIME fi # Output runtime statistics if debugging is turned on if [ $(($D & 0x2000)) -ne 0 ]; then echo "--------------------------------------------------" >> $IP.logfile echo "START_TIME: $START_TIME" >> $IP.logfile echo "WHILE_LOOP_END: $WHILE_LOOP_END" >> $IP.logfile echo "WHILE_LOOP_START: $WHILE_LOOP_START" >> $IP.logfile echo "PING_RATE: $PING_RATE" >> $IP.logfile echo "END_TIME: $END_TIME" >> $IP.logfile echo "OS_TIME: $OS_TIME" >> $IP.logfile echo "SCRIPT_TIME: $SCRIPT_TIME" >> $IP.logfile echo "NON_SLEEP_TIME: $NON_SLEEP_TIME" >> $IP.logfile echo "SLEEP_TIME: $SLEEP_TIME" >> $IP.logfile echo "PREDICTED_START: $PREDICTED_START" >> $IP.logfile fi } # sleep_time #------------------------------------------------------------------------------ # read_file: Function that reads a file line by line into array $LINES[]. # The number of lines in the file (and size of array) is put in $NUM_LINES. # $1 - File to input function read_file () { local func_name=read_file if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside $func_name \$1: $1" >> $IP.logfile; fi # Remove any ^M in case the file is in DOS format which is evil sed -e 's/ //g' < $1 >| /tmp/read_file.$$ # Save the current value of the IFS variable old_ifs=$IFS IFS= # Use the "read" command to load up the array from the passed file NUM_LINES=0 while read LINES[$NUM_LINES] do let NUM_LINES=NUM_LINES+1 done Contents of \$LINES[]" local counter=0 while [ $counter -lt $NUM_LINES ]; do echo "LINES[$counter]: ${LINES[$counter]}" let counter=counter+1 done fi # Cleanup by removing the temporary read_file.$$ rm -f /tmp/read_file.$$ } # read_file #------------------------------------------------------------------------------ # set_working_dir: Function that sets the WORKING_DIR based on $0 function set_working_dir () { # Configure working directory WORKING_DIR=`dirname $0` if [ "$WORKING_DIR" = "." ]; then WORKING_DIR=`pwd` fi cd $WORKING_DIR } # set_working_dir #------------------------------------------------------------------------------ # ping_ip: Function that pings the $IP. It returns 0 if the ping was successful # and returns -1 if the ping failed. If successful, the ping time is stored # in RC_PING_IP. The type of processing performed is OS dependent. The ping # time and ping result are written to $IP.logfile function ping_ip () { local func_name=ping_ip if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside ping_ip" >> $IP.logfile; fi # Capture the time the ping is performed TIME_OF_PING=`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` # Choose the correct ping command based on OS and ping the $IP case $OS_TYPE in 5.8*) # Solaris 8 PING_RESULT=`$PING -s -n $IP 56 1` ;; 2.*) # Linux 2.x kernels PING_RESULT=`$PING -q -c 1 $IP` ;; 1.5*) # Cygwin PING_RESULT=`$PING -s -n $IP 56 1` ;; *) # Unknown OS echo "ERROR [$LINENO]$func_name> Unknown OS" | tee -a $IP.logfile exit esac # Save ping time and ping results to the logfile and to STDOUT if debug enabled OUTPUT="$TIME_OF_PING - $PING_RESULT" echo $OUTPUT >> $IP.logfile # The ping command has different output base on OS type. The ping times are in the # form of 123/123/123 which is min/avg/max ping times. The PING_RESULT is passed # through awk which isolates the field containing the times. As can be seen below, # ping for each OS puts the times in a different field. Add appropriate cases for # each OS supported. The output of the awk script is then piped through sed which # replaces the forward slashes with spaces. The output of sed is then piped through # awk which prints out the second field, which is the average time field. This is # stored in PING_TIME case $OS_TYPE in 5.8*) # Solaris 8 PARSED_TIMES=`echo $PING_RESULT | awk '{ print $30 }'` ;; 2.4.18*) # Mandrake Linux 8.2 PARSED_TIMES=`echo $PING_RESULT | awk '{ print $28 }'` ;; 2.*) # Linux 2.x kernels PARSED_TIMES=`echo $PING_RESULT | awk '{ print $26 }'` ;; 1.5*) # Cygwin PARSED_TIMES=`echo $PING_RESULT | awk '{ print $31 }'` ;; esac PING_TIME=(`echo $PARSED_TIMES | sed -e 's/\// /g' | awk '{print $2}'`) if [ $(($D & 0x1000)) -ne 0 ]; then echo "[$LINENO]$func_name> Value of \$PING_TIME extracted from \$PARSED_TIMES: $PING_TIME" >> $IP.logfile fi # If PING_TIME is empty then the ping failed. Set PING_TIME to -1 and return if [ -z $PING_TIME ]; then PING_TIME=-1 return fi # If the ping was successful then a valid number will be in PING_TIME. # If the ping failed then PING_TIME could have alpha characters in it. # Set PING_TIME to -1 and return if there are alpha characters. ALPHA_FOUND=`echo $PING_TIME | sed -n -e 's/.*[a-z,A-Z].*/alpha/p' ` if [ "$ALPHA_FOUND" == "alpha" ]; then echo "ERROR [$LINENO]$func_name> Alpha characters in \$PING_TIME: $PING_TIME" >> $IP.logfile PING_TIME=-1 return fi # Getting here means a valid ping occurred. The value is already in PING_TIME } # ping_ip #------------------------------------------------------------------------------ # main function main () { local func_name=main if [ $(($D & 0x1)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside main" >> $IP.logfile; fi setup_env # If no arguments are passed then output the help screen and exit if [ $# -eq 0 ]; then display_help exit fi process_command_line $* # There might have been -d on the command line so set debug level again set_debug_level # Show environment variable values if verbose level is high enough if [ $(($D & 0x10)) -ne 0 ]; then display_env fi # Verify that EMAIL_ADDRESS has been changed from the default if [ $EMAIL_ADDRESS == email@address.com ]; then echo "ERROR: You must use -e option to set email address or change value of \$EMAIL_ADDRESS in script." | tee -a $IP.logfile exit fi # Check for IP on command line if [ $IP = not_entered ]; then display_help echo "ERROR: You must provide an IP address or domain name" exit fi # Initialize environment variables by reading $IP.stats file read_pt_stats # cronjob processing must be tested for and executed first if [ $CRONJOB = TRUE ]; then # Add entry to logfile or create new if $IP.logfile doesn't exist log_entry echo "`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a`: cron processing using -c" >>$IP.logfile # Get the PID of the running test get_pt_pid # Initialize a $IP.heartbeat file if it doesn't exist if [ ! -e $IP.heartbeat ]; then echo "RUNNING" >| $IP.heartbeat fi # Kill running process if $IP.heartbeat indicates the thread is stalled. # The cron job writes NOT_RUNNING to $IP.heartbeat. The main while loop # overwrites this with RUNNING. The time between cron runs must be longer # than the ping rate for this to work. HEARTBEAT=`cat $IP.heartbeat` if [ ! "$HEARTBEAT" == "RUNNING" ]; then kill -9 $RC_GET_PT_PID fi # Check if $PID is still alive test_pid $RC_GET_PT_PID if [ "$RC_TEST_PID" -eq $DEAD ]; then # Send an email notifying that the test was down echo "pt: cron: No active pid. Restarting $HOST to $IP" >| /tmp/$$.email cat $IP.stats >> /tmp/$$.email if [ -e $IP.weeks ]; then cat $IP.weeks >> /tmp/$$.email fi echo "" >> /tmp/$$.email echo "----- traceroute ---------------------------------------------------------------" >> /tmp/$$.email get_route $IP $TRACEROUTE_TIMEOUT cat $IP.route >> /tmp/$$.email if [ -e $IP.emails ]; then echo "" >> /tmp/$$.email echo "----- email log ---------------------------------------------------------------" >> /tmp/$$.email cat $IP.emails >> /tmp/$$.email fi cat /tmp/$$.email | $MAIL -s "pt: cron: ${1}: $IP from $HOST - No active pid" $EMAIL_ADDRESS rm -f /tmp/$$.email # Restart the test $0 $IP -d $D -r $PING_RATE -t $DROPPED_THRESHOLD -tto $TRACEROUTE_TIMEOUT $DASH_HOURLY -e $EMAIL_ADDRESS & exit else # Process still running. Do nothing and exit. echo "NOT_RUNNING" >| $IP.heartbeat exit fi fi # These variables don't get changed by the user DROPPED_COUNTER=0 DROPPED_EMAIL_SENT=FALSE # Exit if there is an active PID get_pt_pid test_pid $RC_GET_PT_PID if [ "$RC_TEST_PID" -eq $ACTIVE ]; then echo "$HOST to ${IP}: Active pid $RC_GET_PT_PID found. Exiting" >> $IP.logfile echo "$HOST to ${IP}: Active pid $RC_GET_PT_PID found" echo "Use: pt $IP -k to terminate" exit else # Since the stored PID isn't active, save the current PID as the active PID PID=$$ fi # Add entry to existing logfile or create new if $IP.logfile doesn't exist log_entry # Setup week pointers into the statistics array DOW=`date +%w` let local week_good_idx=8 let local week_dropped_idx=17 let local week_loss_idx=26 let local week_emails_idx=35 let local week_min_idx=44 let local week_avg_idx=53 let local week_max_idx=62 # Calculate web page update rate based on ping rate and force an update let WEB_PAGE_UPDATE_RATE=WEB_PAGE_UPDATE_RATE*60 let WEB_PAGE_UPDATE_RATE=WEB_PAGE_UPDATE_RATE/$PING_RATE WEB_PAGE_UPDATE_CNTR=0 web_page # Need to initialize PREDICTED_START with a valid value before entering while loop START_MINUTE=`date +%M` START_SECOND=`date +%S` fix_number $START_MINUTE START_MINUTE=$? fix_number $START_SECOND START_SECOND=$? let PREDICTED_START=START_MINUTE*60 let PREDICTED_START=PREDICTED_START+START_SECOND # Save pt's pid into a file. The value of $$ is compared to this saved value each time through # the main loop. If another instance of pt somehow gets started, it will overwrite this file # and other instances of pt will exit. This insures only one pt runs at a time. Cygwin seems # to have problems with this more than Solaris and Linux. echo $$ >| $IP.pid # Stay in this loop forever while [ 1 -eq 1 ]; do if [ $(($D & 0x4)) -ne 0 ]; then echo "[$LINENO]$func_name:$$> Inside main while loop" >> $IP.logfile; fi WHILE_LOOP_START=`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` START_MINUTE=`date +%M` START_SECOND=`date +%S` # Verify this process has same pid as in file $IP.stats. Exit if different. SAVED_PID=`cat $IP.pid` if [[ $$ -ne $SAVED_PID ]]; then echo "`date +%Y\-%b\-%d\ %H\:%M\:%S\ %a` - pt: current pid $$ doesn't equal saved pid ${SAVED_PID}. Exiting." | tee -a $IP.logfile exit fi # See if debug level has changed set_debug_level # Write to the heartbeat file so cron can tell the script is still alive echo "RUNNING" >| $IP.heartbeat # Check if it is time to compress and rotate logfile. Do this before any new statistics are gathered # and before "insert_week" is called. "logfile" sends a weekly summary email when the week changes. # Don't want a blank new week inserted at the top. This was happening before moving "logfile" call # to this point. logfile # Increment the total pings let TOTAL_PINGS=TOTAL_PINGS+1 # Perform a ping ping_ip # Get the day of the week and setup indices into the statistics array DOW=`date +%w` insert_week let local day_good_idx=DOW+1 let local day_dropped_idx=DOW+10 let local day_loss_idx=DOW+19 let local day_emails_idx=DOW+28 let local day_min_idx=DOW+37 let local day_avg_idx=DOW+46 let local day_max_idx=DOW+55 # Initialize the hourly indices here if [ "$HOURLY_STATS" == "TRUE" ]; then CURRENT_HOUR=`date +%H` # The leading zero needs to be removed so math will work. I tried using %k which # omits the leading zero, but it puts in a leading space which causes the same problem case $CURRENT_HOUR in 00) CURRENT_HOUR=0;; 01) CURRENT_HOUR=1;; 02) CURRENT_HOUR=2;; 03) CURRENT_HOUR=3;; 04) CURRENT_HOUR=4;; 05) CURRENT_HOUR=5;; 06) CURRENT_HOUR=6;; 07) CURRENT_HOUR=7;; 08) CURRENT_HOUR=8;; 09) CURRENT_HOUR=9;; esac # First calculate the base index into the table. Each hour uses up 27 cells # in the array. Multiply the hour by 27 to get the base index into the array. let local base_index=$CURRENT_HOUR*27 # The "good ping" index is offset from the base index by the $DOW plus 1 let local hour_good_idx=base_index+DOW let hour_good_idx=hour_good_idx+1 # The "dropped ping" index is 9 after the good ping index let local hour_dropped_idx=hour_good_idx+9 # The "loss" index is 18 after the good ping index let local hour_loss_idx=hour_good_idx+18 # The week "good ping" index is offset 8 after the base index let local hour_good_week_idx=base_index+8 # The week "dropped ping" index is 9 after the week "good ping" index let local hour_dropped_week_idx=hour_good_week_idx+9 # The week "loss" index is 18 after the week "good ping" index let local hour_loss_week_idx=hour_good_week_idx+18 fi # If ping test debug is turned on then force the value of PING_TIME if [ $(($D & 0x80)) -ne 0 ]; then if [ ${toggle_cnt:=5} -eq 0 ]; then toggle_cnt=5 if [ ${TEST_PING_STATUS:="ping_good"} = "ping_good" ]; then echo "[$LINENO]$func_name:$$> Forcing \$PING_TIME to -1" >> $IP.logfile TEST_PING_STATUS="ping_bad" PING_TIME=-1 else echo "[$LINENO]$func_name:$$> Using actual \$PING_TIME" >> $IP.logfile TEST_PING_STATUS="ping_good" fi else let toggle_cnt=toggle_cnt-1 if [ "$TEST_PING_STATUS" = "ping_bad" ]; then PING_TIME=-1 fi fi fi # Process the results of the ping if [ "$PING_TIME" == "-1" ]; then # Increment the bad ping counters let DROPPED_PINGS=DROPPED_PINGS+1 ARRAY[$day_dropped_idx]=`expr ${ARRAY[$day_dropped_idx]} + 1` ARRAY[$week_dropped_idx]=`expr ${ARRAY[$week_dropped_idx]} + 1` # Update hourly stats if -hourly was on the command line if [ "$HOURLY_STATS" == "TRUE" ]; then HOUR_ARRAY[$hour_dropped_idx]=`expr ${HOUR_ARRAY[$hour_dropped_idx]} + 1` HOUR_ARRAY[$hour_dropped_week_idx]=`expr ${HOUR_ARRAY[$hour_dropped_week_idx]} + 1` fi else # Increment the good ping counters let GOOD_PINGS=GOOD_PINGS+1 ARRAY[$day_good_idx]=`expr ${ARRAY[$day_good_idx]} + 1` ARRAY[$week_good_idx]=`expr ${ARRAY[$week_good_idx]} + 1` # Update hourly stats if -hourly was on the command line if [ "$HOURLY_STATS" == "TRUE" ]; then HOUR_ARRAY[$hour_good_idx]=`expr ${HOUR_ARRAY[$hour_good_idx]} + 1` HOUR_ARRAY[$hour_good_week_idx]=`expr ${HOUR_ARRAY[$hour_good_week_idx]} + 1` fi # Replace overall minimum ping time if this one was faster fp $PING_TIME -lt $MIN_PING_TIME if [ "$?" != "-1" ]; then if [ "$RC_FP" -eq 0 ]; then MIN_PING_TIME=$PING_TIME MIN_PING_DATE=$TIME_OF_PING fi fi # Replace daily minimum ping time if this one was faster fp ${ARRAY[$day_min_idx]} -eq 0 if [ "$?" != "-1" ]; then if [ "$RC_FP" -eq 0 ]; then ARRAY[$day_min_idx]=10000.0; fi fi fp $PING_TIME -lt ${ARRAY[$day_min_idx]} if [ "$?" != "-1" ]; then if [ "$RC_FP" -eq 0 ]; then ARRAY[$day_min_idx]=$PING_TIME fi fi # Replace weekly minimum ping time if this one was faster fp ${ARRAY[$week_min_idx]} -eq 0 if [ "$?" != "-1" ]; then if [ "$RC_FP" -eq 0 ]; then ARRAY[$week_min_idx]=10000.0; fi fi fp $PING_TIME -lt ${ARRAY[$week_min_idx]} if [ "$?" != "-1" ]; then if [ "$RC_FP" -eq 0 ]; then ARRAY[$week_min_idx]=$PING_TIME fi fi # Replace overall maximum ping time if this one was longer fp $PING_TIME -gt $MAX_PING_TIME if [ "$?" != "-1" ]; then if [ "$RC_FP" -eq 0 ]; then MAX_PING_TIME=$PING_TIME MAX_PING_DATE=$TIME_OF_PING fi fi # Replace daily maximum ping time if this one was faster fp $PING_TIME -gt ${ARRAY[$day_max_idx]} if [ "$?" != "-1" ]; then if [ "$RC_FP" -eq 0 ]; then ARRAY[$day_max_idx]=$PING_TIME fi fi # Replace weekly maximum ping time if this one was faster fp $PING_TIME -gt ${ARRAY[$week_max_idx]} if [ "$?" != "-1" ]; then if [ "$RC_FP" -eq 0 ]; then ARRAY[$week_max_idx]=$PING_TIME fi fi # Add average time to daily sum, calculate the average, and save it to the stats array fp $PING_TIME -add ${sums[$DOW]} if [ "$?" != "-1" ]; then sums[$DOW]=$RC_FP fi fp ${sums[$DOW]} -div ${ARRAY[$day_good_idx]} if [ "$?" != "-1" ]; then ARRAY[$day_avg_idx]=$RC_FP fi # Add average time to weekly sum, calculate the average, and save it to the stats array fp $PING_TIME -add ${sums[7]} if [ "$?" != "-1" ]; then sums[7]=$RC_FP fi fp ${sums[7]} -div ${ARRAY[$week_good_idx]} if [ "$?" != "-1" ]; then ARRAY[$week_avg_idx]=$RC_FP fi # Add average time to overall sum, calculate the average, and save it to overall average $AVG_PING_TIME fp $PING_TIME -add ${sums[8]} if [ "$?" != "-1" ]; then sums[8]=$RC_FP fi fp ${sums[8]} -div $GOOD_PINGS if [ "$?" != "-1" ]; then AVG_PING_TIME=$RC_FP fi fi # Calculate the loss percentages using awk and put the two results into array "answer" answer=(`echo "" | awk '{ printf ("%5.3f %5.3f", \ day_dropped/(day_dropped+day_good)*100, week_dropped/(week_dropped+week_good)*100) }' \ day_dropped=${ARRAY[$day_dropped_idx]} \ day_good=${ARRAY[$day_good_idx]} \ week_dropped=${ARRAY[$week_dropped_idx]} \ week_good=${ARRAY[$week_good_idx]}`) # Store the calculated percentages into the array ARRAY[$day_loss_idx]=${answer[0]} ARRAY[$week_loss_idx]=${answer[1]} # Calculate the overall loss percentage using awk and put the result into $PERCENT_LOSS PERCENT_LOSS=(`echo "" | awk '{ printf "%5.3f", \ total_dropped/total_pings*100 }' \ total_dropped=$DROPPED_PINGS \ total_pings=$TOTAL_PINGS`) # Calculate hourly loss percentages if -hourly is on the command line if [ "$HOURLY_STATS" == "TRUE" ]; then # Calculate the hourly loss value for this day fp ${HOUR_ARRAY[$hour_dropped_idx]} -add ${HOUR_ARRAY[$hour_good_idx]} if [ "$?" != "-1" ]; then fp ${HOUR_ARRAY[$hour_dropped_idx]} -div $RC_FP if [ "$?" != "-1" ]; then fp $RC_FP -mult 100 if [ "$?" != "-1" ]; then HOUR_ARRAY[$hour_loss_idx]=$RC_FP fi fi fi # Calculate the hourly loss value for the week fp ${HOUR_ARRAY[$hour_dropped_week_idx]} -add ${HOUR_ARRAY[$hour_good_week_idx]} if [ "$?" != "-1" ]; then fp ${HOUR_ARRAY[$hour_dropped_week_idx]} -div $RC_FP if [ "$?" != "-1" ]; then fp $RC_FP -mult 100 if [ "$?" != "-1" ]; then HOUR_ARRAY[$hour_loss_week_idx]=$RC_FP fi fi fi fi # Send email after $THRESHOLD dropped pings in a row. Send another email after $THRESHOLD successful pings. if [ "$PING_TIME" == "-1" ]; then # Getting here means the ping failed. # Check if a "down" email needs to be sent let DROPPED_COUNTER=DROPPED_COUNTER+1 if [ $DROPPED_COUNTER -ge $DROPPED_THRESHOLD ] && [ "$DROPPED_EMAIL_SENT" = "FALSE" ]; then DROPPED_COUNTER=$DROPPED_THRESHOLD DROPPED_EMAIL_SENT=TRUE ARRAY[$day_emails_idx]=`expr ${ARRAY[$day_emails_idx]} + 1` ARRAY[$week_emails_idx]=`expr ${ARRAY[$week_emails_idx]} + 1` send_email Down fi else # Getting here means the ping was good. let DROPPED_COUNTER=DROPPED_COUNTER-1 # Don't let dropped counter go below zero if [ $DROPPED_COUNTER -lt 0 ]; then DROPPED_COUNTER=0 fi # If down email has been sent, clear counter, flag, and send an "up" email if [ "$DROPPED_EMAIL_SENT" = "TRUE" ]; then DROPPED_COUNTER=0 DROPPED_EMAIL_SENT=FALSE ARRAY[$day_emails_idx]=`expr ${ARRAY[$day_emails_idx]} + 1` ARRAY[$week_emails_idx]=`expr ${ARRAY[$week_emails_idx]} + 1` send_email Up fi fi # Check if time to create and transfer a new web page web_page # Output the $IP.stats file if debugging is turned on if [ $(($D & 0x100)) -ne 0 ]; then echo "" >> $IP.logfile echo "[$LINENO]$func_name:$$> Contents of $IP.stats" >> $IP.logfile cat $IP.stats >> $IP.logfile fi # Output the $IP.weeks file if debugging is turned on if [ $(($D & 0x200)) -ne 0 ]; then if [ -e $IP.weeks ]; then echo "" >> $IP.logfile echo "[$LINENO]$func_name:$$> Contents of $IP.weeks" >> $IP.logfile cat $IP.weeks >> $IP.logfile fi fi # Update and output the $IP.route file if debugging is turned on if [ $(($D & 0x400)) -ne 0 ]; then get_route $IP $TRACEROUTE_TIMEOUT echo "" >> $IP.logfile echo "[$LINENO]$func_name:$$> Contents of $IP.route" >> $IP.logfile cat $IP.route >> $IP.logfile fi # Output the $IP.sums file if debugging is turned on if [ $(($D & 0x800)) -ne 0 ]; then echo "" >> $IP.logfile echo "[$LINENO]$func_name:$$> Contents of $IP.sums" >> $IP.logfile cat $IP.sums >> $IP.logfile fi # Calculate how long to sleep and sleep that amount sleep_time # Make sure all the files exist. They could have been blown away underneath the script. if [ ! -e $IP.stats ]; then # Reading also creates the file if it is missing echo "[$LINENO]$func_name:$$> WARNING: $IP.stats file is missing! Creating new." >> $IP.logfile echo "[$LINENO]$func_name:$$> WARNING: $IP.stats file is missing! Creating new." | $MAIL -s "pt: $IP.stats missing on $HOST" $EMAIL_ADDRESS read_pt_stats else write_pt_stats fi # Jump back and do it all over again done } # main # Set working directory, get version, set debug level, and then invoke the main function set_working_dir get_version set_debug_level main $*