#!/bin/sh

# Nagios plugin to check remote host's time difference via SNMP
# Copyright (c) 2010, John Morrissey <jwm@horde.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


# Nagios plugin to monitor the time difference between a remote host (via
# the SNMP iso.3.6.1.2.1.25.1.2.0/HOST-RESOURCES-MIB::hrSystemDate.0 OID)
# and the local host.
#
# Depends on GNU date(1) and a net-snmp-like snmpget(1), with output like:
#   HOST-RESOURCES-MIB::hrSystemDate.0 = STRING: 2010-2-27,14:50:36.0,+0:0

set -e

snmp_version=2c
warning_thresh=15
critical_thresh=120

usage() {
	echo "Usage: $(basename "$0") -C COMMUNITY -H HOSTADDRESS [-v SNMP_VERSION]"
	echo '                       [-c CRITICAL] [-w WARNING]'
	echo
	echo ' -C COMMUNITY'
	echo '     SNMP community string to use.'
	echo ' -H HOSTADDRESS'
	echo '     Hostname or IP address to check.'
	echo ' -v VERSION'
	echo "     SNMP version to use, defaults to $snmp_version."
	echo ' -c CRITICAL'
	echo "     Time difference to trigger critical alerts, defaults to $critical_thresh."
	echo ' -w WARNING'
	echo "     Time difference to trigger warnings, defaults to $warning_thresh."
	exit 3
}

isdigit()
{
	if [ $# -eq 0 ]; then
		return 1
	fi
	local arg
	for arg in "$@"; do
		case $arg in
		*[!0-9]* | "")
			return 1
			;;
		esac
	done 
	return 0
}

OPTIND=1
while getopts :c:C:H:v:w: ch; do
	case $ch in
	c)
		if [ -z "$OPTARG" ] || ! isdigit "$OPTARG"; then
			echo "-$ch: numeric argument required" 1>&2
			usage
			exit 1
		fi
		critical=$OPTARG
		;;
	C)
		if [ -z "$OPTARG" ]; then
			echo "-$ch: argument required" 1>&2
			usage
			exit 1
		fi
		community=$OPTARG
		;;
	H)
		if [ -z "$OPTARG" ]; then
			echo "-$ch: argument required" 1>&2
			usage
			exit 1
		fi
		host=$OPTARG
		;;
	v)
		if [ -z "$OPTARG" ]; then
			echo "-$ch: argument required" 1>&2
			usage
			exit 1
		fi
		case $OPTARG in
		1|2c)
			snmp_version=$OPTARG
			;;
		*)
			echo "-$ch: supported SNMP versions are 1 and 2c" 1>&2
			;;
		esac
		;;
	w)
		if [ -z "$OPTARG" ] || ! isdigit "$OPTARG"; then
			echo "-$ch: numeric argument required" 1>&2
			usage
			exit 1
		fi
		warning=$OPTARG
		;;
	*)
		echo "-$OPTARG: invalid argument" 1>&2
		usage
		exit 1
		;;
	esac
done

if [ -z "$host" ] ; then
	echo 'a host (-H) is a required.' 1>&2
	usage
	exit 1
fi
if [ -z "$community" ] ; then
	echo 'an SNMP community (-C) is a required.' 1>&2
	usage
	exit 1
fi

if ! sysdate=$(snmpget -v "$snmp_version" -c "$community" "$host" \
	HOST-RESOURCES-MIB::hrSystemDate.0 | sed 's/.* = STRING: //'); then

	echo 'Unable to retrieve system time via SNMP.'
	exit 3
fi

local_time_t=$(date +%s)

# Maybe I'm copping out a little by relying on GNU date(1)'s parsing,
# but it seems better than having to get elbow-deep parsing it ourselves.
if ! echo "$sysdate" | grep -q '^[0-9]\{4\}-[0-9]\{1,2\}-[0-9]\{1,2\},[0-9]\{1,2\}:[0-9]\{1,2\}:[0-9]\{1,2\}.[0-9]\{1,\},[-+][0-9]\{1,2\}:[0-9]\{1,2\}'; then
	echo "Invalid date format: '$sysdate'"
	exit 3
fi
remote_time_t=$(date -d "$(echo "$sysdate" | sed -e 's/,/ /' -e 's/,//')" +%s)

delta=$(($local_time_t - $remote_time_t))
if [ "$delta" -lt 0 ]; then
	delta=$((-$delta))
fi

if [ "$delta" -ge "$critical_thresh" ]; then
	echo "Time difference is more than $critical_thresh seconds: $delta|diff=$delta"
	exit 2
fi

if [ "$delta" -ge "$warning_thresh" ]; then
	echo "Time difference is more than $warning_thresh seconds: $delta|diff=$delta"
	exit 1
fi

echo "Time difference is less than $warning_thresh seconds: $delta|diff=$delta"
exit 0
