#!/bin/sh /etc/rc.common

# shellcheck shell=ash

# shellcheck disable=SC2034
START=82
STOP=28
USE_PROCD=1
UPSMON_C=/var/etc/nut/upsmon.conf
PIDFILE=/var/run/upsmon.pid

# Upstream NUT event type names (in the order listed in
# https://networkupstools.org/docs/man/upsmon.conf.html)
NUT_EVENT_TYPES=" ONLINE ONBATT LOWBATT FSD COMMOK COMMBAD SHUTDOWN REPLBATT NOCOMM NOPARENT CAL NOTCAL OFF NOTOFF BYPASS NOTBYPASS ECO NOTECO OVER NOTOVER TRIM NOTTRIM BOOST NOTBOOST OTHER NOTOTHER SUSPEND_STARTING SUSPEND_FINISHED "

nut_get_notifications() {
	local event="$1"
	local defaultnotify="$2"

	if [ "${NUT_EVENT_TYPES#*" $event "}" != "${NUT_EVENT_TYPES}" ]; then
		config_get val "$event" message
		[ -n "$val" ] && echo "NOTIFYMSG $event \"$val\"" >>"$UPSMON_C"
		config_get val "$event" flag "$defaultnotify"
		[ -n "$val" ] && echo "NOTIFYFLAG $event $val" >>"$UPSMON_C"
	else
		logger -t nut-monitor -s "$event is not a valid NUT message event type"
	fi
}

nut_upsmon_conf() {
	local cfg="$1"
	local RUNAS val optval defaultnotify

	echo "# Config file automatically generated from UCI config" >"$UPSMON_C"

	config_get RUNAS "$cfg" runas "nutmon"
	[ -n "$RUNAS" ] && echo "RUN_AS_USER $RUNAS" >>"$UPSMON_C"
	runas="$RUNAS"

	config_get val "$cfg" deadtime 15
	echo "DEADTIME $val" >>"$UPSMON_C"

	config_get val "$cfg" finaldelay 5
	echo "FINALDELAY $val" >>"$UPSMON_C"

	config_get val "$cfg" hostsync 15
	echo "HOSTSYNC $val" >>"$UPSMON_C"

	config_get val "$cfg" minsupplies 1
	echo "MINSUPPLIES $val" >>"$UPSMON_C"

	config_get val "$cfg" nocommwarntime 300
	echo "NOCOMMWARNTIME $val" >>"$UPSMON_C"

	config_get val "$cfg" pollfaillogthrottlemax -1
	echo "POLLFAIL_LOG_THROTTLE_MAX $val" >>"$UPSMON_C"

	config_get val "$cfg" notifycmd
	[ -n "$val" ] && echo "NOTIFYCMD \"$val\"" >>"$UPSMON_C"

	config_get val "$cfg" pollfreq 5
	echo "POLLFREQ $val" >>"$UPSMON_C"

	config_get val "$cfg" pollfreqalert 5
	echo "POLLFREQALERT $val" >>"$UPSMON_C"

	echo "POWERDOWNFLAG /var/run/killpower" >>"$UPSMON_C"

	config_get val "$cfg" offduration 30
	echo "OFFDURATION $val" >>"$UPSMON_C"

	config_get val "$cfg" overduration -1
	echo "OVERDURATION $val" >>"$UPSMON_C"

	config_get val "$cfg" oblbduration 0
	echo "OBLBDURATION $val" >>"$UPSMON_C"

	config_get val "$cfg" rbwarntime 43200
	echo "RBWARNTIME $val" >>"$UPSMON_C"

	config_get val "$cfg" alarmcritical 1
	echo "ALARMCRITICAL $val" >>"$UPSMON_C"

	config_get val "$cfg" shutdownexit 0
	echo "SHUTDOWNEXIT $val" >>"$UPSMON_C"

	config_get val "$cfg" shutdowncmd "/usr/sbin/nutshutdown"
	echo "SHUTDOWNCMD \"$val\"" >>"$UPSMON_C"

	config_get val "$cfg" certpath
	if [ -n "$val" ]; then echo "CERTPATH $val" >>"$UPSMON_C"; fi

	config_get_bool val "$cfg" certverify 0
	if [ -n "$val" ]; then echo "CERTVERIFY $val" >>"$UPSMON_C"; fi

	config_get_bool val "$cfg" forcessl 0
	if [ -n "$val" ]; then echo "FORCESSL $val" >>"$UPSMON_C"; fi

	# debugmin must be a positive integer or zero
	config_get val "$cfg" debugmin 0
	case "$val" in
		''|*[!0-9]*) ;;
		*) echo "DEBUG_MIN $val" >>"$UPSMON_C" ;;
	esac

	config_get val "$cfg" defaultnotify "SYSLOG"
	defaultnotify="$val"
	config_foreach nut_get_notifications notifications "$val"

	# Otherwise the default is WALL+SYSLOG
	for event in $NUT_EVENT_TYPES; do
		if ! grep -q "NOTIFYFLAG $event" "$UPSMON_C"; then
			echo "NOTIFYFLAG $event $defaultnotify" >>"$UPSMON_C"
		fi
	done

	havemon=1
}

nut_upsmon_add() {
	local cfg="$1"
	local type="$2"
	local upsname
	local hostname
	local port
	local powervalue
	local username
	local password
	local system

	config_get upsname "$cfg" upsname
	config_get hostname "$cfg" hostname localhost
	config_get port "$cfg" port
	config_get powervalue "$cfg" powervalue 1
	config_get username "$cfg" username
	config_get password "$cfg" password
	config_get type "$cfg" type "${type:-secondary}"

	system="$upsname@$hostname"
	if [ -n "$port" ]; then
		system="$system:$port"
	fi
	echo "MONITOR $system $powervalue $username $password $type" >>"$UPSMON_C"

	havems=1
}

build_config() {
	# shellcheck disable=SC2174
	mkdir -m 0750 -p "$(dirname "$UPSMON_C")"

	config_load nut_monitor
	config_foreach nut_upsmon_conf upsmon
	config_foreach nut_upsmon_add primary primary
	config_foreach nut_upsmon_add secondary secondary
	config_foreach nut_upsmon_add monitor
	# master and slave are legacy section names. Prefer monitor.
	config_foreach nut_upsmon_add master primary
	config_foreach nut_upsmon_add slave secondary

	[ ! -s /var/etc/nut/nut.conf ] && {
		echo "MODE=netclient" >>/var/etc/nut/nut.conf
		chmod 640 /var/etc/nut/nut.conf
		chgrp "$(id -gn "${runas:-nutmon}")" /var/etc/nut/nut.conf
	}

	[ -s "$UPSMON_C" ] && chmod 640 "$UPSMON_C"
	[ -s "$UPSMON_C" ] && chgrp "$(id -gn "${runas:-nutmon}")" "$UPSMON_C"
}

interface_triggers() {
	local action="$1"
	local triggerlist trigger

	config_get triggerlist "upsmon" triggerlist

	# shellcheck disable=SC1091
	. "${IPKG_INSTROOT}"/lib/functions/network.sh

	if [ -n "$triggerlist" ]; then
		for trigger in $triggerlist; do
			if [ "$action" = "add_trigger" ]; then
				procd_add_interface_trigger "interface.*" "$trigger" /etc/init.d/nut-monitor restart
			else
				network_is_up "$trigger" && return 0
			fi
		done
	else
		if [ "$action" = "add_trigger" ]; then
			procd_add_raw_trigger "interface.*.up" 2000 /etc/init.d/nut-monitor restart
		else
			ubus call network.device status | grep -q '"up": true' && return 0
		fi
	fi
	[ "$action" = "add_trigger" ] || return 1
}

start_service() {
	local runas=nutmon
	local havemon havems
	build_config

	[ "$havemon" != 1 ] && return 1
	[ "$havems" != 1 ] && return 1
	interface_triggers "check_interface_up" || return 0

	procd_open_instance "upsmon"
	procd_set_param respawn 10 20 6
	procd_set_param stderr 1
	procd_set_param stdout 0
	procd_set_param env NUT_QUIET_INIT_UPSNOTIFY=true
	procd_set_param reload_signal HUP
	procd_set_param command /usr/sbin/upsmon -FF
	procd_close_instance

	return 0
}

# pkill is not available, and pgrep does not accept --signal
pgrepkill() {
	local pids

	[ $# -eq 2 ] || return 1

	pids="$(pgrep "$1" 2>/dev/null)" || return 0

	for pid in $pids; do
		kill -"$2" "$pid"
	done
}

reload_service() {
	local should_stop

	# shellcheck disable=SC1091
	. /lib/functions/procd.sh

	build_config

	should_stop=0

	[ "$havemon" = 1 ] || should_stop=1
	[ "$havems" = 1 ] || should_stop=1
	interface_triggers "check_interface_up" || should_stop=1

	if [ "$should_stop" = "0" ]; then
		if procd_running nut-monitor upsmon; then
			if [ -s "$PIDFILE" ]; then
				upsmon -c reload 2>&1 | logger -t nut-monitor
				# We don't care about the exit code
				return
			elif pgrep upsmon >/dev/null 2>/dev/null; then
				procd_send_signal nut-monitor upsmon HUP 2>&1 | logger -t nut-monitor
				# We don't care about the exit code
				return
			fi
		else
			/etc/init.d/nut-monitor start
		fi
	else
		if procd_running nut-monitor upsmon; then
			if [ -s "$PIDFILE" ]; then
				upsmon -c stop 2>&1 | logger -t nut-monitor
			else
				pgrepkill upsmon TERM >/dev/null 2>/dev/null
			fi
			procd_kill nut-monitor upsmon 2>/dev/null | logger -t nut-monitor
		fi
	fi
}

stop_service() {
	if [ -s "$PIDFILE" ]; then
		upsmon -c stop 2>&1 | logger -t nut-monitor
		procd_kill nut-monitor 2>/dev/null | logger -t nut-monitor
	else
		pgrepkill upsmon TERM >/dev/null 2>/dev/null
		procd_kill nut-monitor 2>/dev/null | logger -t nut-monitor
	fi
}

service_triggers() {
	config_load nut_monitor
	interface_triggers "add_trigger"
	procd_add_reload_trigger "nut_monitor"
}
