#! /bin/bash
#
# check_clamav_time update checker plugin for Nagios
# Written by David Diffenbaugh <davediff@nbcs.rutgers.edu>
# Last Modified 9/16/08
# version: 2.0.3
# Usage: ./check_clamav_time -w <mins> -c <mins> [-f filepath] [-L clampath] [-S check_clampath] [-t <secs>] [-v]
#
# Options:
#  -h, --help
#      Print detailed help screen
#   -V, --version
#      Print version information
#   -t, --timeout=INTEGER
#      Seconds before connection times out (default: 10)
#   -v, --verbose
#      Show details for command-line debugging
#   -w, --warning=INTEGER
#      Generate warning state if ClamAV Database out of date by INTEGER
#   -c, --critical=INTEGER
#      Generate critical state if ClamAV Database out of date by INTEGER
#   -f, --filepath=PATH
#      PATH to write state file (default: /var/log/nagios)
#   -L, --clampath=PATH
#      PATH to daily and main ClamAV databases (default: /var/lib/clamav)
#   -S, --check_clampath=PATH
#      PATH to check_clamav_time script (default: /usr/lib64/nagios/plugins)
#   -a, --addtopath=PATH
#      add PATH to this scripts $PATH variable (i.e. /usr/local/bin)
#
# Description:
#
# This plugin will check the installed versions of the daily and main clamav
# databases. The plugin compares these values with the most current
# versions advertised by current.cvd.clamav.net. When it detects an outdated
# version it writes the outdated version to a state file (either
# clamv-outdated-daily or clamav-outdated-main). It also writes to the file
# the time the file is created. If the file exists for criticalthresh
# or warnthresh minutes the plugin will return a CRITICAL or WARNING
# state to nagios.
#
# Examples:
#
# Check for updates and set the warning threshhold to 1080 minutes (18 hours)
# and set the critical threshold to 1440 minutes (24 hours)

# ./check_clamav_time -w 1080 -c 1440 -f /var/log/nagios -L /var/lib/clamav
#

# Note:
#
# This script is meant to be run at a regular interval small in comparison
# to the critical and warning thresholds. If the script is run only once or
# infrequently it may never detect an out of date version. The reason for this
# is because of what the script is reporting. It reports critical or warning
# only when it detects a certain period of time has elapsed since it *first*
# detected that an update was available for clamav but that update had not
# been applied. For example even if your clamav is 2 months out of date, and
# your critical threshold is 24 hours, the script will not return a critical
# state the first time it is run.
#
# Requires: bash cat cut grep egrep kill pgrep rm sleep touch tr host perl sed sigtool clamav

ARGS=$*
VERSION="2.0.3"
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
timeout=10
badarg=0
onearg=0
verbose=0
outdatedtime=0
outdatedmaintime=0
exitstatus=-1
#set default paths
pathtofile="/usr/lib/nagios/plugins"
pathtoclamlib="/var/lib/clamav"
pathtoscript="/usr/lib/nagios/plugins"

usage() {
   echo "Usage: check_clamav_time -w <mins> -c <mins> [-f filepath] [-L clamlib] [-S check_clampath] [-t <secs>] [-v]"
   echo "Options:"
   echo " -h, --help"
   echo "    Print detailed help screen"
   echo " -V, --version"
   echo "    Print version information"
   echo " -t, --timeout=INTEGER"
   echo "    Seconds before connection times out (default: 10)"
   echo " -v, --verbose"
   echo "    Show details for command-line debugging"
   echo " -w, --warning=INTEGER"
   echo "    Generate warning state if ClamAV Database out of date by INTEGER"
   echo " -c, --critical=INTEGER"
   echo "    Generate critical state if ClamAV Database out of date by INTEGER"
   echo " -f, --filepath=PATH"
   echo "    PATH to write state file (default: /var/log/nagios)"
   echo " -L, --clampath=PATH"
   echo "    PATH to daily and main ClamAV databases (default: /var/lib/clamav)"
   echo " -S, --check_clampath=PATH"
   echo "    PATH to check_clamav_time script (default: /usr/lib64/nagios/plugins)"
   echo " -a, --addtopath=PATH"
   echo "    add PATH to this scripts \$PATH variable (i.e. /usr/local/bin)"
   echo
   echo "Example:"
   echo " check_clamav_time -w 1080 -c 1440 -f /var/log/nagios -L /var/lib/clamav"
}

help() {
    version
    echo
    about
    echo
    usage
    echo
}

version() {
   echo "check-clamav nagios plugin version: $VERSION"
   echo "Written by David Diffenbaugh <davediff@nbcs.rutgers.edu>"
}

about()  {
   echo "This plugin will check the installed versions of the daily and main clamav
databases. The plugin compares these values with the most current versions
advertised by current.cvd.clamav.net. When it detects an outdated version it
writes the outdated version to a state file (either clamv-outdated-daily or
clamav-outdated-main). If this file exits unmodified for critical_thresh or
warning_thresh minutes the plugin will return a CRITICAL or WARNING state to
nagios."
}

if [ $# -lt 1 ]; then
    usage
    exit $STATE_CRITICAL
fi

if [ $# -eq 1 ]; then
   onearg=1
fi

#get the command line args
while test -n "$1"; do
    case "$1" in
        --help)
	    if [ $onearg -eq 1 ]; then
               help
               exit $STATE_OK
	    fi
            ;;
        -h)
            if [ $onearg -eq 1 ]; then
               help
               exit $STATE_OK
            fi
            ;;
        --version)
            if [ $onearg -eq 1 ]; then
               version
               exit $STATE_OK
            fi
            ;;
        -V)
            if [ $onearg -eq 1 ]; then
               version
               exit $STATE_OK
            fi
            ;;
        -w)
	    if [ "`echo $2 | sed -e 's/^\(.\).*/\1/'`" == '-' ]; then
               echo "check_clamav_time: -w option requires an integer argument"
               badarg=1
            else
               warnthresh=$2
	       ck=$( echo "$warnthresh" | tr -dc '[:digit:]')
               if [[ ${#warnthresh} -ne ${#ck} || ${#warnthresh} -eq 0 ]]; then
                  echo "check_clamav_time: -w option requires an integer argument"
                  badarg=1
               fi
               shift
            fi
            ;;
	--warning*)
            warnthresh=`echo $1 | sed -e 's/--warning=//'`
            ck=$( echo "$warnthresh" | tr -dc '[:digit:]')
            if [[ ${#warnthresh} -ne ${#ck} || ${#warnthresh} -eq 0 ]]; then
               echo "check_clamav_time: --warning option requires an integer value"
               badarg=1
            fi
	    ;;
        -c)
            if [ "`echo $2 | sed -e 's/^\(.\).*/\1/'`" == '-' ]; then
               echo "check_clamav_time: -c option requires an integer argument"
               badarg=1
            else
               criticalthresh=$2
               ck=$( echo "$criticalthresh" | tr -dc '[:digit:]')
               if [[ ${#criticalthresh} -ne ${#ck} || ${#criticalthresh} -eq 0 ]]; then
                  echo "check_clamav_time: -c option requires an integer argument"
                  badarg=1
               fi
               shift
            fi
            ;;
        --critical*)
            criticalthresh=`echo $1 | sed -e 's/--critical=//'`
            ck=$( echo "$criticalthresh" | tr -dc '[:digit:]')
            if [[ ${#criticalthresh} -ne ${#ck} || ${#criticalthresh} -eq 0 ]]; then
               echo "check_clamav_time: --critical option requires an integer value"
               badarg=1
            fi
            ;;
        -L)
            if [ "`echo $2 | sed -e 's/^\(.\).*/\1/'`" == '-' ]; then
               echo "check_clamav_time: -L option requires a valid path as an argument"
               badarg=1
            else
               pathtoclamlib=$2
               if [ ! -d "$pathtoclamlib" ]; then
                  echo "check_clamav_time: -L option requires a valid path as an argument"
                  badarg=1
               fi
               shift
            fi
            ;;
        --clampath*)
            pathtoclamlib=`echo $1 | sed -e 's/--clampath=//'`
            if [ ! -d "$pathtoclamlib" ]; then
               echo "check_clamav_time: --clampath option requires a valid path"
               badarg=1
            fi
            ;;
        -f)
            if [ "`echo $2 | sed -e 's/^\(.\).*/\1/'`" == '-' ]; then
               echo "check_clamav_time: -f option requires an integer argument"
               badarg=1
            else
               pathtofile=$2
               if [ ! -d "$pathtofile" ]; then
                  echo "check_clamav_time: -f option requires a valid path as an argument"
                  badarg=1
               fi
               shift
            fi
            ;;
        --filepath*)
            pathtofile=`echo $1 | sed -e 's/--filepath=//'`
            if [ ! -d "$pathtofile" ]; then
               echo "check_clamav_time: --filepath option requires a valid path"
               badarg=1
            fi
            ;;
        --verbose)
            verbose=1
            ;;
        -v)
            verbose=1
            ;;
        -t)
            if [ "`echo $2 | sed -e 's/^\(.\).*/\1/'`" == '-' ]; then
               echo "check_clamav_time: -t option requires an integer argument"
               badarg=1
            else
               timeout=$2
               ck=$( echo "$timeout" | tr -dc '[:digit:]')
               if [[ ${#timeout} -ne ${#ck} || ${#timeout} -eq 0 ]]; then
                  echo "check_clamav_time: -t option requires an integer argument"
                  badarg=1
               fi
               shift
            fi
            ;;
        --timeout*)
            timeout=`echo $1 | sed -e 's/--timeout=//'`
            ck=$( echo "$timeout" | tr -dc '[:digit:]')
	    if [[ ${#timeout} -ne ${#ck} || ${#timeout} -eq 0 ]]; then
               echo "check_clamav_time: --timeout option requires an integer value"
               badarg=1
	    fi
            ;;
	-S)
            if [ "`echo $2 | sed -e 's/^\(.\).*/\1/'`" == '-' ]; then
               echo "check_clamav_time: -S option requires a valid path as an argument "
               badarg=1
            else
	       pathtoscript=$2
               if [ ! -d "$pathtoscript" ]; then
                  echo "check_clamav_time: -S option requires a valid path as an argument"
                  badarg=1
               fi
	       shift
            fi
            ;;
        --check_clampath*)
            pathtoscript=`echo $1 | sed -e 's/--check_clampath=//'`
            if [ ! -d "$pathtoscript" ]; then
               echo "check_clamav_time: --check_clampath option requires a valid path"
               badarg=1
            fi
            ;;
	-a)
	    if [ "`echo $2 | sed -e 's/^\(.\).*/\1/'`" == '-' ]; then
               echo "check_clamav_time: -a option requires a valid path as an argument"
               badarg=1
            else
               if [ ! -d "$2" ]; then
                  echo "check_clamav_time: -a option requires a valid path as an argument"
                  badarg=1
               fi
	       path=`echo $PATH | egrep '(^|:)'$2'(:|$)'`
	       if [ "$path" == "" ]; then
	       PATH=$PATH:$2
	       fi
               shift
            fi
            ;;
        --addtopath*)
            addtopath=`echo $1 | sed -e 's/--addtopath=//'`
            if [ ! -d "$addtopath" ]; then
               echo "check_clamav_time: --addtopath option requires a valid path"
               badarg=1
	    else
		path=`echo $PATH | egrep '(^|:)'$addtopath'(:|$)'`
		if [ "$path" == "" ]; then
		PATH=$PATH:$addtopath
		fi
	    fi
            ;;
        *)
            echo "check_clamav_time: Unknown argument: $1"
            badarg=1
            ;;
    esac
    shift
done

if [ "$warnthresh" == "" -o "$criticalthresh" == "" ]; then
   echo "check_clamav_time: missing arguments you must specifiy both -c and -w options"
   badarg=1
fi

if [ $badarg -eq 1 ]; then
   echo "check_clamav_time: Try check_clamav_time --help for usage"
   exit $STATE_UNKNOWN
else
   exitstatus=0
fi

#after assigning arguments check that if defaults were used they exist

if [ ! -d "$pathtoscript" ]; then
   echo "UNKNOWN - check_clamav_time: default check_clamav_time script path /usr/lib64/nagios/plugins/rutgers does not exist"
   exit $STATE_UNKNOWN
fi

if [ ! -d "$pathtofile" ]; then
   echo "UNKNOWN - check_clamav_time: default file path /var/log/nagios does not exist"
   exit $STATE_UNKNOWN
fi

if [ ! -d "$pathtoclamlib" ]; then
   echo "UNKNOWN - check_clamav_time: default clamav library path to /var/lib/clamav does not exist"
   exit $STATE_UNKNOWN
fi


MYPID=$$
PARENT=`pgrep -o check_clamav_time`
if [ "$MYPID" == "$PARENT" ]; then
sleep $timeout && HUNGPID=`pgrep -n check_clamav_time` && [ ! "$HUNGPID" == $! ]  && kill $HUNGPID && touch /tmp/check_clamav_time_hung &
${pathtoscript}/check_clamav_time $ARGS
exitstatus=$?
# need to sleep here to give time for /tmp/check_clamav_time_hung to be written after above is killed
sleep 2
[ -e /tmp/check_clamav_time_hung ] && rm /tmp/check_clamav_time_hung && echo "UNKNOWN- check_clamav_time script timed out" && exit 3
exit $exitstatus
fi

CHILD=`pgrep -n check_clamav_time`
CHILD=$MYPID
if [ "$MYPID" == "$CHILD" ]; then

#check that clamd is actually running

if [ "`pgrep clamd`" == "" ]; then
      echo "CRITICAL - clamd is not running"
      exit $STATE_CRITICAL
fi

#get installed daily version
if [ -e ${pathtoclamlib}/daily.cld ]
then
   dailyversion=`sigtool -i ${pathtoclamlib}/daily.cld`
   if [ $? -ne 0 ]; then
      echo "UNKNOWN - check_clamav_time: installed daily version could not be determined"
      exit $STATE_UNKNOWN
   fi
   dailyversion=`echo "$dailyversion" | grep Version | sed -e 's/Version: //'`
else
   if [ -e ${pathtoclamlib}/daily.cvd ]
   then
      dailyversion=`sigtool -i ${pathtoclamlib}/daily.cvd`
      if [ $? -ne 0 ]; then
         echo "UNKNOWN - check_clamav_time: installed daily version could not be determined"
         exit $STATE_UNKNOWN
      fi
      dailyversion=`echo "$dailyversion" | grep Version | sed -e 's/Version: //'`
   else
      echo "UNKNOWN - check_clamav_time: installed daily version could not be determined"
      exit $STATE_UNKNOWN
   fi
fi

#get installed main version
if [ -e ${pathtoclamlib}/main.cvd ]
then
   mainversion=`sigtool -i ${pathtoclamlib}/main.cvd`
   if [ $? -ne 0 ]; then
      echo "UNKNOWN - check_clamav_time: installed main version could not be determined"
      exit $STATE_UNKNOWN
   fi
   mainversion=`echo "$mainversion" | grep Version | sed -e 's/Version: //'`
else
   if [ -e ${pathtoclamlib}/main.cld ]
   then
      mainversion=`sigtool -i ${pathtoclamlib}/main.cld`
      if [ $? -ne 0 ]; then
         echo "UNKNOWN - check_clamav_time: installed main version could not be determined"
         exit $STATE_UNKNOWN
      fi
      mainversion=`echo "$mainversion" | grep Version | sed -e 's/Version: //'`
   else
      echo "UNKNOWN - check_clamav_time: installed main version could not be determined"
      exit $STATE_UNKNOWN
   fi
fi

if [ "$verbose" -eq 1 ]
then
   echo "Installed daily version is $dailyversion"
   echo "Installed main version is $mainversion"
fi

if [ -e ${pathtofile}/clamav-outdated-daily ]
then
   if [ "$verbose" -eq 1 ]
   then
   echo "${pathtofile}/clamav-outdated-daily exists, it reports daily version: `cat ${pathtofile}/clamav-outdated-daily | grep Version | cut -d':' -f2`"
   fi
   if [ "`cat ${pathtofile}/clamav-outdated-daily | grep Version | cut -d':' -f2`" -eq "$dailyversion" ]
   then
      outdatedfiletime=$(cat ${pathtofile}/clamav-outdated-daily | grep Time | cut -d':' -f2)
      outdatedtime=$(((`perl -e "print time"`- outdatedfiletime)/60))
      if [ "$outdatedtime" -ge "$criticalthresh" ]
      then
         echo "CRITICAL - clamav daily has not been updated in $outdatedtime minutes"
         exit $STATE_CRITICAL
      else
         if [ "$outdatedtime" -ge "$warnthresh" ]
         then
            echo "WARNING - clamav daily has not been updated in $outdatedtime minutes"
            exit $STATE_WARNING
         fi
      fi
      if [ "$verbose" -eq 1 ]
      then
         echo "Installed daily version is still out of date by $outdatedtime minutes"
      fi
   else
      if [ "$verbose" -eq 1 ]
      then
         echo "Removing ${pathtofile}/clamav-outdated-daily"
      fi
      rm ${pathtofile}/clamav-outdated-daily
   fi
else
   if [ "$verbose" -eq 1 ]
   then
      echo "${pathtofile}/clamav-outdated-daily does not exist"
   fi
   currentdailyversion=`host -t txt current.cvd.clamav.net`
   if [ $? -ne 0 ]; then
      echo "UNKNOWN - check_clamav_time: dns text query to current.cvd.clamav.net failed"
      exit $STATE_UNKNOWN
   fi
   currentdailyversion=`echo $currentdailyversion | sed -e 's/^.*text "//' -e 's/"//' | cut -d : -f 3`
   if [ "$verbose" -eq 1 ]
   then
      echo "Clamav current daily version is $currentdailyversion"
   fi
   if [ "$currentdailyversion" -ne "$dailyversion" ]
   then
      if [ "$verbose" -eq 1 ]
      then
         echo "Clamav installed version $dailyversion does not match most current version $currentdailyversion"
         echo "Writing ${pathtofile}/clamav-outdated-daily with daily version $dailyversion"
      fi
      echo "Version:$dailyversion" > ${pathtofile}/clamav-outdated-daily
      echo "Time:`perl -e "print time"`" >> ${pathtofile}/clamav-outdated-daily
   fi
fi

if [ -e ${pathtofile}/clamav-outdated-main ]
then
   if [ "$verbose" -eq 1 ]
   then
      echo "${pathtofile}/clamav-outdated-main exists, it reports main version: `cat ${pathtofile}/clamav-outdated-main | grep Version | cut -d':' -f2`"
   fi
   if [ "`cat ${pathtofile}/clamav-outdated-main | grep Version | cut -d':' -f2`" -eq "$mainversion" ]
   then
      outdatedmainfiletime=$(cat ${pathtofile}/clamav-outdated-main | grep Time | cut -d':' -f2)
      outdatedmaintime=$(((`perl -e "print time"`- outdatedmainfiletime)/60))
      if [ "$outdatedmaintime" -ge "$criticalthresh" ]
      then
         echo "CRITICAL - clamav main has not been updated in $outdatedmaintime minutes"
         exit $STATE_CRITICAL
      else
         if [ "$outdatedmaintime" -ge "$warnthresh" ]
         then
            echo "WARNING - clamav main has not been updated in $outdatedmaintime minutes"
            exit $STATE_WARNING
         fi
      fi
      if [ "$verbose" -eq 1 ]
      then
         echo "Installed main version is still out of date by $outdatedmaintime minutes"
      fi
   else
      echo "Removing ${pathtofile}/clamav-outdated-main"
      rm ${pathtofile}/clamav-outdated-main
   fi
else
   if [ "$verbose" -eq 1 ]
   then
      echo "${pathtofile}/clamav-update-main does not exist"
   fi
   currentmainversion=`host -t txt current.cvd.clamav.net`
   if [ $? -ne 0 ]; then
      echo "UNKNOWN - check_clamav_time: dns text query for current.cvd.clamav.net failed"
      exit $STATE_UNKNOWN
   fi
   currentmainversion=`echo $currentmainversion | sed -e 's/^.*text "//' -e 's/"//' | cut -d : -f 2`
   if [ "$verbose" -eq 1 ]
   then
      echo "Clamav current main version is $currentmainversion"
   fi
   if [ "$currentmainversion" -ne "$mainversion" ]
   then
      if [ "$verbose" -eq 1 ]
      then
         echo "Clamav installed version $mainversion does not match most current version $currentmainversion"
         echo "writing ${pathtofile}/clamav-outdated-main with main version $mainversion"
      fi
      echo "Version:$mainversion" > ${pathtofile}/clamav-outdated-main
      echo "Time:`perl -e "print time"`" >> ${pathtofile}/clamav-outdated-main
   fi
fi

#if not critical or warning echo message and exit
if [ "$outdatedtime" -eq 0 ]
then
   if [ "$outdatedmaintime" -eq 0 ]
   then
      echo "Clamav OK - daily version: $dailyversion up to date, main version: $mainversion up to date"
   else
     echo "Clamav OK - daily version: $dailyversion, main version: $mainversion out of date by $outdatedmaintime minutes"
   fi
else
   if [ "$outdatedmaintime" -eq 0 ]
   then
      echo "Clamav OK - daily version: $dailyversion out of date by $outdatedtime minutes, main version: $mainversion up to date"
   else
      echo "Clamav OK - daily version: $dailyversion out of date by $outdatedtime minutes, main version: $mainversion out of date by $outdatedmaintime minutes"
   fi
fi
exit $exitstatus
fi
