#!/bin/sh

# Note: after calling $ME with --dhowdbs and setting up config file,
# comment out line 165 and test how it works first!
# FIXME: do we need lockfile? It causes more problems than it solves.

. /opt/etc/functions.openrc

CONFIG=/opt/etc/heartbeat.d/ipmimon.conf

update_me

if [ -f "${CONFIG}" ]; then
	.	${CONFIG}
else
	echo "${ME}: config file ${CONFIG} not found!"
	exit 1
fi

[ -d ${IPMI_DB_DIR} ] || mkdir -p ${IPMI_DB_DIR}

IPMIMON_LOCKFILE=/tmp/ipmimon.lock
IPMI_DB_ROOT=${IPMI_DB_DIR-"/opt/var"}
IPMI_DB_DIR="$(realpath ${IPMI_DB_ROOT})/ipmi"
IPMI_DB=${IPMI_DB-"$IPMI_DB_DIR/allsensors"}
IPMI_DB_PREV="${IPMI_DB}.prev"
TEMP_DB=${TEMP_DB-"$IPMI_DB_DIR/temperatures"}
FAN_DB=${FAN_DB-"$IPMI_DB_DIR/fans"}
ATTEMPTS=${ATTEMPTS-"5"}
VERBOSE=""

if [ -f "${IPMIMON_LOCKFILE}" ]; then
	update_me
	echo "${ME}: recursive call!"
	exit 1
fi

touch "${IPMIMON_LOCKFILE}"

if ! (ipmitool sensor | sed -e 's/\ /_/g' -e 's/|/\ /g' -e 's/_//g' > "${IPMI_DB}"); then
	echo "${ME}: error reading IPMI sensors!"
	rm "${LOCKFILE}"
	exit 1
fi

truncate -s 0 "${TEMP_DB}"
truncate -s 0 "${FAN_DB}"

# Parse sensor data line by line, extracting temperatures
# and RPMs to separate databases.

cat "${IPMI_DB}" | while read LN; do
	if echo $LN | grep 'degrees' > /dev/null 2>&1; then
		[ $(echo $LN | awk '{print $2;}') = "na" ] || \
			echo $LN | awk '{print $1 " " int($2);}' >> "${TEMP_DB}"
	elif echo $LN | grep '[Rr][Pp][Mm]' > /dev/null 2>&1; then
		[ $(echo $LN | awk '{print $2;}') = "na" ] || \
			echo $LN | awk '{print $1 " " int($2);}' >> "${FAN_DB}"
	fi
done

if [ $# = 1 -a "$1" = "--showdbs" ]; then
	cat "${TEMP_DB}" | while read LN; do
		echo "TemperatureDB[$LN]"
	done
	cat "${FAN_DB}" | while read LN; do
		echo "FanDB[$LN]"
	done
	rm "${IPMIMON_LOCKFILE}"
	exit 0
fi

# check_bounds()
# Arguments: real value, mincrit, minwarn, maxwarn, maxcrit
# Returns:
#	0: value \in (minwarn,maxwarn)
#	1: value \in (mincrit,maxcrit)
#	2: otherwise

check_bounds() {
	# Hail the C language!
	echo $1 $2 $3 $4 $5 | \
		awk '{exit $1 > $3 && $1 < $4 ? 0 : ($1 > $2 && $1 < $5 ? 1 : 2)}'
}

# Any monitor routine should accept 5 or more arguments:
#  - file name of plain text database of form:
#	Sensor1Name RealSensor1Value [any extra data]
#	Sensor2Name RealSensor2Value [any extra data]
#	...
#	SensorNName RealSensorNValue [any extra data]
#  - four real values (MinCrit,MinWarn,MaxWarn,MaxCrit);
#  - optional names of mandatory sensors.
# Any monitor routine should return:
#  - 0 if all mandatory sensors (if specified) are present and values
#	of either these or all (if mandatory are not specified) sensors
#	are in (MinWarn;MaxWarn);
#  - 1 if previous conditions are met except range is (MinCrit;MaxCrit)
#  - 2 if at least one mandatory sensor was not found or value of
#	at least one (mandatory or autodetected) sensor are out of
#	critical range.

generic_monitor()
{
	check_bounds_verbose()
	{
		local _v _sensor=$1; shift
		check_bounds $@
		_v=$?
		[ ${_v} -gt ${_rv} ] && _rv=${_v}
		case ${_v} in
			1) echo "${ME} warning: value $1 of sensor ${_sensor} is out of range ($3;$4).";;
			2) echo "${ME} critical: value $1 of sensor ${_sensor} is out of range ($2;$5).";;
		esac
	}
	local _dbline _sensor _value
	_rv=0
	local _sensordb=$1; shift
	local _mincrit=$1; shift
	local _minwarn=$1; shift
	local _maxwarn=$1; shift
	local _maxcrit=$1; shift
	update_me
	if [ $# -gt 0 ]; then
		# We have list of mandatory sensors
		for _sensor in $@; do
			_value=$(grep -e ^${_sensor} ${_sensordb})
			if [ $? -ne 0 ]; then
				echo "${ME} critical: sensor ${_sensor} not found in ${_sensordb}."
				_rv=2
			else
				_value=$(echo ${_value} | awk '{print $2;}')
				check_bounds_verbose ${_sensor} ${_value} \
					${_mincrit} ${_minwarn} ${_maxwarn} ${_maxcrit}
			fi
		done
	else
		# We have to analyse all available sensors.
		# The trick used to export new value of _rv from subshell to parent
		# is described in http://heirloom.sourceforge.net/sh/sh.1.html#20
		exec 5<&0 < ${_sensordb}; while read _dbline; do
			_sensor=$(echo ${_dbline} | awk '{print $1;}')
			_value=$(echo ${_dbline} | awk '{print $2;}')
			check_bounds_verbose ${_sensor} ${_value} \
				${_mincrit} ${_minwarn} ${_maxwarn} ${_maxcrit}
		done
		exec <&5 5<&-
	fi
	return ${_rv}
}

generic_monitor ${TEMP_DB} ${T_BOUNDS} ${REQUIRED_TEMPERATURE_SENSORS}
level=$?
generic_monitor ${FAN_DB} ${F_BOUNDS} ${REQUIRED_FANS}
_rv=$?
[ ${_rv} -gt $level ] && level=${_rv}

cat "${TEMP_DB}" > "${TEMP_DB}.prev"
cat "${FAN_DB}" > "${FAN_DB}.prev"

if [ $level -gt 1 ]; then
	update_me
	msg="${ME}: health-critical event(s) detected, shutting system down in 1 minute!"
	echo $msg
	shutdown -hP +1 $msg
fi

rm "${IPMIMON_LOCKFILE}"

exit $level
