#!/bin/bash
#
# 2004.03.19 - Jeff Vier
# This script is released under the GNU Public License.
#
# This plugin wraps the Nagios plugn 'check_http' to do multiple URL checks
#
# Your check command (in nagios/etc/checkcommands.cfg) should look something like this:
# ---
# define command {
#	command_name                    check_http_many
#	command_line                    $USER1$/check_http_many $ARG1$
#	}
# ---
# The $ARG1$ will be the path to a file of URLs
# You can pile on any normal check_http arguments, too.  AFTER the $ARG1$ only!
#
#
# Then, of course, add a service definition (typically in nagios/etc/services.cfg):
# ---
# define service {
#	use			generic-service
#	host_name		serverA
#	service_description	Check Many URLs
#	check_command		check_http_many!/path/to/file.txt
#	}
# ---
#
# Then stop/start nagios and you're done.
#
# Set this to whatever you want.
DEFAULT_BAD_URL_FILE="/usr/local/nagios/var/bad_url_log"

# Usage block:
function usageblock
{
if [ ! -f "$1" ] && [ -n "$1" ] && [ "$1" != "-h" ] && [ "$1" != "--help" ]; then
	echo -e "\nI'm sorry, I couldn't find the file [$1]"
fi
echo "
Usage:
	${0} [/path/url-file.txt] <-A | -B | any check_http args>

Options:
  -A, --checkall
	Check all URLS (don't exit on failure)
  -B[FILE], --bad-url-file=[FILE]
	Output file (generally for event handler) - Requires -A/--checkall argument.
	Default: $DEFAULT_BAD_URL_FILE
	Note: NO SPACE after -B
  -h, --help
	This usage block
  <any check_http argument>
	Run '${0#_*} --help' for help with check-http arguments.

Notes:
	- Your url-file should be plain text, one url per line (no length limit beside system limitations)
	- If you want to use 'relative' URLs ('/file.html') in your url-file, you must supply a -H <hostname> option
	  (example: ${0} /usr/local/nagios/etc/server1-urls.txt -H server1.blah.com)
	- URLs n your url-file need to either start with \"http://\" or \"/\" or it will fail.
	- You can comment out URLs in your url-file with a # as the *first* character on the line.
	- Processing is halted upon the first bad URL; the return code is based on the result of that check (warn/crit/unknown) and bad URL is displayed."
exit 3
}
# end usage block:



function result_parser {
# if result is non-zero, end processing (unless --checkall/-A was passed)
if [ $RES -ne 0 ]; then
	if [ -z "$CHECK_ALL" ]; then
		RET="Line $((URL_COUNT+1)): [$url] - $RET"
		echo $RET
		exit $RES
	else
		RET="Line $((URL_COUNT+1)): [$url] - $RET"
		# Trap the first Bad Result for returning via Nagios
		if [ -z "$BAD_RET" ] || [ -z "$BAD_RES" ]; then
			BAD_RET=$RET
			BAD_RES=$RES
			# clear this so they don't pile up.
			rm -f $BAD_URL_FILE
		fi
		# push the bad result to the $BAD_URL_FILE
		echo $RET>>$BAD_URL_FILE
		# Result code should be the worst result of the lot
		if [ "$BAD_RES" -lt "$RES" ]; then
			BAD_RES=$RES
		fi
		let BAD_URL_COUNT=$BAD_URL_COUNT+1
	fi
fi
}


# derives the plugins directory from $0
NAGIOSPLUGSDIR=${0%/*}
# Assumes check_http is in the same directory as this plugin is
CHECK_HTTP=$NAGIOSPLUGSDIR/check_http
# Exit as 'unknown' state if no check_http is found
if [ ! -f "$CHECK_HTTP" ]; then
	echo "Error: $CHECK_HTTP not found"
	exit 3
fi

# if $1 is null, "-h", "--help", or points to a non-existant file, show usage.
if [ -z "$1" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ] || [ ! -f "$1" ]; then
	usageblock
fi

# OK=0
# WARNING=1
# CRITICAL=2
# UNKNOWN=3

# cycle through the arguments past the first one (first one has to be the file with the URLs)
ARGS=($@)
j=1
while [ $j -lt $# ]; do
	# if --help or -H appears anywhere in the arguments, show usage
	if [ "${ARGS[$j]}" = "--help" ] || [ "${ARGS[$j]}" = "-h" ]; then
		usageblock
	elif [ "${ARGS[$j]}" = "--checkall" ] || [ "${ARGS[$j]}" = "-A" ]; then
		CHECK_ALL=1
		if [ "${ARGS[$j]}" = "--bad-url-file=" ];then
			BAD_URL_FILE=${ARGS[$j]:15:10000}
		elif [ "${ARGS[$j]}" = "-B" ]; then
			BAD_URL_FILE=${ARGS[$j]:2:10000}
		else
			BAD_URL_FILE=$DEFAULT_BAD_URL_FILE
		fi
	else
		CHECK_COMMAND="$CHECK_COMMAND ${ARGS[$j]}"
	fi
	let j=$j+1
done

# set up done, ready to run the command...
# default result code (result code so far)
RES=$?
if [ $RES -ne 0 ]; then
	# returned text if some failure happened before now
	RET="Something pre-check failed."
else
	# reset URL_COUNT
	URL_COUNT=0
	BAD_URL_COUNT=0
	{
	while read url; do
		# Skip comments
		if [ "${url:0:1}" = "#" ];then continue; fi
		# handle (chop-up) http:// URLs
		if [ "${url:0:7}" = "http://" ]; then
			# Since this is a full URL, strip any -H/--hostname from the arg list
			NOHOST_CHECK_COMMAND=$(echo $CHECK_COMMAND | sed 's/ -H [^[:blank:]]*//g' | sed 's/ --hostname=[^[:blank:]]*//g')
			# Establish the hostname and the length thereof
			HOST=$(echo ${url:7:10000} | awk -F/ '{print $1}')	# Hmm.  vim doesn't like ${var:n} syntax.  It's fine, though. I promise.
			HOSTLEN=$(echo http://$HOST|wc -c)
			# And, finally the rest of the URL is the URI we pass
			URI=${url:${HOSTLEN}:10000}
			# Execute the check:
			RET=$($CHECK_HTTP $NOHOST_CHECK_COMMAND -H $HOST -u ${URI:=/})
			RES=$?
			result_parser
		# handle https:// URLs
		elif [ "${url:0:8}" = "https://" ]; then
			# No -S or --ssl with https:// URL?  we'll temporarily add it to the CHECK_COMMAND
			if [ -z "$(echo $CHECK_COMMAND | sed 's/.*\(-S\).*/\1/g')" ] && [ -z "$(echo $CHECK_COMMAND | sed 's/.*\(--ssl\).*/\1/g')" ];then
				SAVE_CHECK_COMMAND=$CHECK_COMMAND
				CHECK_COMMAND="$CHECK_COMMAND -S"
			fi
			# Since this is a full URL, strip any -H/--hostname from the arg list
			NOHOST_CHECK_COMMAND=$(echo $CHECK_COMMAND | sed 's/ -H [^[:blank:]]*//g' | sed 's/ --hostname=[^[:blank:]]*//g')
			# Reset the CHECK_COMMAND
			CHECK_COMMAND=$SAVE_CHECK_COMMAND
			# Establish the hostname and the length thereof
			HOST=$(echo ${url:8:10000} | awk -F/ '{print $1}')
			HOSTLEN=$(echo https://$HOST|wc -c)
			# And, finally the rest of the URL is the URI we pass
			URI=${url:${HOSTLEN}:10000}
			# Execute the check:
			RET=$($CHECK_HTTP $NOHOST_CHECK_COMMAND -H $HOST -u ${URI:=/})
			RES=$?
			result_parser
		# handle relative paths
		elif [ "${url:0:1}" = "/" ];then
			# No -H (or --hostaddress=) with relative paths?  Bad admin!
			if [ -z "$(echo $CHECK_COMMAND | sed 's/.* \(-H [^[:blank:]]*\).*/\1/g')" ] && [ -z "$(echo $CHECK_COMMAND | sed 's/.*\(--hostname=[^[:blank:]]*\).*/\1/g')" ];then
				RET="No -H/--hostname arg supplied for relative URL checking"
				RES=3
				result_parser
			fi
			RET=$($CHECK_HTTP $CHECK_COMMAND -u $url)
		else
			RET="Incorrect URL format."
			RES=3
			result_parser
		fi
		let URL_COUNT=$URL_COUNT+1
	done
	} < $1
	if [ "$RES" -eq "0" ];then
		RET="OK - $URL_COUNT URLs checked without failure"
	fi
fi

if [ -z "$BAD_RET" ]; then
	# echo back the $RET (displayed in Nagios)
	echo $RET
	# exit with result code (determines ok/warn/crit/unk)
	exit $RES
else
	# echo back the $BAD_RET (displayed in Nagios)
	RET="$BAD_RET"
	if [ "$BAD_URL_COUNT" -gt 1 ];then
		RET="$RET (+$((BAD_URL_COUNT-1)) more error"
		# pluralizer
		if [ "$BAD_URL_COUNT" -gt "2" ];then RET="${RET}s"; fi
		RET="$RET - email to follow)"
	else
		rm -f $BAD_URL_FILE
	fi
	echo $RET
	# exit with $BAD_RES result code (determines ok/warn/crit/unk)
	exit $BAD_RES
fi
