#!/usr/bin/bash
#----------------------------------------------------------------------

IFS=$'\n' \
	SITES=("$@")

TIMEOUT=${TIMEOUT:=10}

HTTP_TRIES=${HTTP_TRIES:=2}
HTTP_SAFE=${HTTP_SAFE:=no}
HTTP_OK_STATUS=${HTTP_OK_STATUS:='2[0-9][0-9]|30[1-9]|401|501'}



#----------------------------------------------------------------------
# Help...

case $1 in
	-h|--help)
		echo "Do a status check on URL(s)."
		echo
		echo "Suage:"
		echo "    `basename $0` URL ..."
		echo
		# XXX would be fun to make the docs dynamic...
		echo "Supported URL formats and schemes:"
		echo "    http://<url>"
		echo "    https://<url>"
		echo "        Check if <url> is reachable via http/https."
		echo "    ssh://<url>[:<port>]"
		echo "        Check if <url> has an open port with a listening ssh server."
		echo "    git://<url>[:<port>]"
		echo "        The same as ssh://..."
		echo "    ovpn://<url>[:<port>]"
		echo "        Check if an OpenVPN server is listening on <url>."
		echo "        NOTE: this will not work if 'tls-auth' is enabled but the thing was"
		echo "            already implemented when I found out about this."
		echo "    lxc://<ct-id>:<comman>:<args>"
		echo "        Proxmox-specific set of checks."
		echo
		exit
		;;
	*)
		;;
esac


#----------------------------------------------------------------------

source .pct-helpers


#----------------------------------------------------------------------
# Handlers...

declare -A HANDLERS


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
# Port
#
#	tcp://<addr>:<port>
#	udp://<addr>:<port>
#

HANDLERS[tcp]=check-port
HANDLERS[udp]=check-port

# XXX this seems to be broken...
check-port(){
	local udp=
	if [[ "${1:0:3}" == "udp" ]] ; then
		udp='u'
	fi
	local target=`sed \
			-e 's/^.*\s*\(tcp\|udp\):\/\///' \
			-e 's/^\s*//;s/\s*$//' \
		<<<$1`
	local port=`cut -sd ':' -f 2 <<<$target`
	port=${port:=80}
	target=`cut -d ':' -f 1 <<<$target`

	local res=$(timeout $TIMEOUT nc -vz${udp} $target $port 2>&1)

	if ! [[ $res =~ .*open$ ]] ; then
		echo ERROR
	else
		echo OK
	fi
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
# Proxmox lxc
#
#	lxc://<id>:<cmd>:<value>
#
# supported commands:
#	status:<status>
#	ip:<dev>=<ip>
#	service:<name>=<status>
#

HANDLERS[lxc]=check-lxc

declare -A LXC_HANDLERS

LXC_HANDLERS[status]=check-lxc-status
check-lxc-status(){
	local status=`pct status $1`
	status=${status/status: /}
	[ "$status" = "$2" ] \
		&& echo "OK" \
		|| echo "ERROR"
}

LXC_HANDLERS[ip]=check-lxc-ip
check-lxc-ip(){
	local dev=${2/=*/}
	local ip=${2/*=/}
	local res=$(\
		lxc-attach $1 ip a show dev $dev \
			| awk '/inet / {print $2}')
	[ "$ip" = "$res" ] \
		&& echo "OK" \
		|| echo "ERROR"
}

LXC_HANDLERS[service]=check-lxc-service
check-lxc-service(){
	local service=${2/=*/}
	local status=${2/*=/}
	local res=$(\
		lxc-attach $1 systemctl status $service \
			| awk '/Active: / {print $2}')
	[ "$status" = "$res" ] \
		&& echo "OK" \
		|| echo "ERROR"
}

check-lxc(){
	local target=`sed \
			-e 's/^.*\s*lxc:\/\///' \
			-e 's/^\s*//;s/\s*$//' \
		<<<$1`
	local cmd=`sed 's/^[^:]*://' <<<$target`
	target=${target/:*/}
	local value=`sed 's/^[^:]*://' <<<$cmd`
	cmd=${cmd/:*/}

	if [ -z ${LXC_HANDLERS[$cmd]} ] ; then
			echo "ERROR (unknown command: $cmd)"
			return
	fi

	${LXC_HANDLERS[$cmd]} "$target" "$value"
}



# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
# OpenVPN 
#
# NOTE: this will not work if 'tls-auth' is enabled...

HANDLERS[ovpn]=check-ovpn

check-ovpn(){
	local target=`sed \
			-e 's/^.*\s*ovpn:\/\///' \
			-e 's/^\s*//;s/\s*$//' \
		<<<$1`
	local port=`cut -sd ':' -f 2 <<<$target`
	port=${port:=1194}
	target=`cut -d ':' -f 1 <<<$target`

	local res=$(echo -e "\x38\x01\x00\x00\x00\x00\x00\x00\x00" \
		| timeout $TIMEOUT nc -u $target $port \
		| cat -v)

	if [ -z $res ] ; then
		echo ERROR
	else
		echo OK
	fi
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
# SSH

HANDLERS[ssh]=check-ssh
HANDLERS[git]=check-ssh

check-ssh(){
	local target=`sed \
			-e 's/^.*\s*\(ssh\|git\):\/\///' \
			-e 's/^\s*//;s/\s*$//' \
		<<<$1`
	local port=`cut -sd ':' -f 2 <<<$target`
	port=${port:=22}
	target=`cut -d ':' -f 1 <<<$target`

	# open port...
	response=$(\
		[ $(sleep $TIMEOUT \
				| telnet $target $port 2> /dev/null \
				| grep SSH) ] \
			&& echo "OK" \
			|| echo "ERROR" )

	echo $response
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
# HTTP/HTTPS

HANDLERS[http]=check-http
HANDLERS[https]=check-http

status_pattern="\(${HTTP_OK_STATUS//|/\\|}\)"
check-http(){
	local target=`sed \
			-e 's/^.*\s*\(https\?:\/\/\)/\1/' \
			-e 's/^\s*//;s/\s*$//' \
		<<<$1`

	# http/https
	if [ "$HTTP_SAFE" = "no" ] ; then
		local safe=--no-check-certificate
	else
		local safe
	fi
	local response=$(\
		wget -S --spider -T $TIMEOUT --tries=$HTTP_TRIES $safe $target 2>&1 \
			| awk '/HTTP\// {print $2}' )

	if [ "$(grep "$status_pattern" <<<$response)" ] ; then
		response=OK
	else
		if [ -z $response ] ; then
			response='ERROR: TIMEOUT'
		else
			response="ERROR: $response"
		fi
	fi

	echo $response
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

PROTOCOLS=$(printf "\\|%s" "${!HANDLERS[@]}")
PROTOCOLS=${PROTOCOLS:2}

check(){
	# NOTE: we are preserving whitespace here...
	local comment=`sed \
			-e 's/^\(.*\)\('"$PROTOCOLS"'\):\/\/.*$/\1/' \
			-e 's/\s*$//' \
		<<<$1`
	local target=`sed \
			-e 's/^.*\s*\(\('"$PROTOCOLS"'\):\/\/\)/\1/' \
			-e 's/^\s*//;s/\s*$//' \
		<<<$1`
	local scheme=`sed 's/\('"$PROTOCOLS"'\):\/\/.*/\1/' \
		<<<$target`


	echo "COMMENT=$comment"
	echo "URL=$target"
	# call the checker...
	echo "RESPONSE=$(${HANDLERS[$scheme]} "$target")"
}



#----------------------------------------------------------------------

problems=
for site in ${SITES[@]} ; do
	# skip things we do not recognize...
	if ! [ "$(grep "$PROTOCOLS://" <<<$site)" ] ; then
		echo $site
		continue
	fi

	IFS=$'\n' \
		res=($(check "$site"))

	comment="${res[0]/COMMENT=/}"
	site=${res[1]/URL=/}
	res=${res[2]/RESPONSE=/}

	if [ -z $res ] ; then
		res='Timeout?'
	fi

	! [ -z $comment ] \
		&& comment="$comment "

	echo "${comment}${site} : ${res}"
done

if [ $problems ] ; then
	exit 1
fi



#----------------------------------------------------------------------
# vim:set ts=4 sw=4 :
