#!/bin/sh
# /etc/hotplug.d/iface/99-shater
#
# netifd wipes `ip rule` / `ip route` on `network reload` and on interface
# churn, so our policy routing must be re-persisted on every ifup/ifdown.
# `shaterd reconcile` is idempotent (it SIGHUPs the running daemon, which
# re-reads UCI and re-applies under its config-hash gate), so re-running it here
# is safe and cheap. If the daemon is not running, `shaterd reconcile` is a
# no-op (fork-storm guard) — it never cold-starts an engine.
#
# Fully inert unless the Shater stack is enabled AND the main service is live
# (ACTIVE_FLAG raised by /etc/init.d/shater start, cleared by stop). Guarding
# on the flag — not just on UCI — means an admin `stop` sticks: a WAN flap can
# never resurrect interception behind a deliberately stopped daemon.

[ -x /usr/bin/shaterd ] || exit 0

case "$ACTION" in
	ifup|ifdown) ;;
	*) exit 0 ;;
esac

# Only act when explicitly enabled AND the service is meant to be running.
en=$(uci -q get shater.globals.enabled) || exit 0
[ "$en" = "1" ] || exit 0
[ -f /var/run/shater.active ] || exit 0

# Coalesce interface-flap storms: each reconcile is a full re-read + re-apply,
# real CPU on small routers. The FIRST event in a burst schedules one reconcile
# 2s out (absorbing the burst's rule-wipes); followers in that window exit —
# their wipes are covered by the pending pass. The marker is released BEFORE
# reconciling so an event landing mid-reconcile schedules a fresh pass and no
# wipe is ever left unrepaired.
pending=/var/run/shater/hotplug.pending
mkdir -p /var/run/shater
mkdir "$pending" 2>/dev/null || exit 0
sleep 2
rmdir "$pending" 2>/dev/null

/usr/bin/shaterd reconcile >/dev/null 2>&1

exit 0
