#!/bin/sh /etc/rc.common
# quickstart-scope-context: 1

START=91
STOP=15
USE_PROCD=1
PROG=/usr/bin/quickstart-netpolicy

service_triggers() {
 procd_add_reload_trigger quickstart-netpolicy
 config_load quickstart-netpolicy
 config_foreach add_network_trigger quickstart-netpolicy
}

add_network_trigger() {
 local network enabled
 config_get_bool enabled "$1" enabled 0
 [ "$enabled" -eq 1 ] || return 0
 config_get network "$1" network
 [ "$1" != main ] || network="${network:-lan}"
 [ -z "$network" ] || procd_add_reload_interface_trigger "$network"
}

instance_error() {
 logger -t quickstart-netpolicy "instance $1: $2"
}

# Validate the whole enabled set before opening any instance. Duplicate resources
# invalidate both entries; configuration order must not choose the winning LAN.
collect_instance() {
 local section="$1" enabled network interface port data_dir log_level history dns other
 config_get_bool enabled "$section" enabled 0
 [ "$enabled" -eq 1 ] || return 0
 config_get network "$section" network
 config_get interface "$section" interface
 config_get port "$section" port
 config_get data_dir "$section" data_dir
 config_get log_level "$section" log_level info
 config_get_bool history "$section" traffic_history 0
 config_get_bool dns "$section" dns_monitoring 0
 if [ "$section" = main ]; then
  network="${network:-lan}"
  interface="${interface:-br-lan}"
  port="${port:-8765}"
  data_dir="${data_dir:-/usr/share/quickstart-netpolicy}"
 fi
 case "$section" in ''|*[!A-Za-z0-9_-]*) instance_error "$section" 'invalid instance identity'; return 0;; esac
 case "$network" in ''|*[!A-Za-z0-9_-]*) instance_error "$section" 'invalid network identity'; return 0;; esac
 case "$interface" in ''|*[!A-Za-z0-9_.-]*) instance_error "$section" 'invalid interface'; return 0;; esac
 case "$port" in ''|*[!0-9]*) instance_error "$section" 'port is required'; return 0;; esac
 port="$(expr "$port" + 0)"
 [ "$port" -gt 0 ] && [ "$port" -le 65535 ] || { instance_error "$section" 'invalid port'; return 0; }
 case "$data_dir" in /*) ;; *) instance_error "$section" 'absolute data_dir is required'; return 0;; esac
 case "$data_dir" in *'|'*|*'
'*) instance_error "$section" 'invalid data_dir'; return 0;; esac
 case "$log_level" in trace|debug|info|warn|error) ;; *) instance_error "$section" 'invalid log level'; return 0;; esac
 local canonical
 canonical="$(readlink -f "$data_dir" 2>/dev/null)"
 [ -z "$canonical" ] || data_dir="$canonical"
 local IFS='
'
 for other in $instance_records; do
  local old_section old_network old_interface old_port old_dir rest
  old_section="${other%%|*}"; rest="${other#*|}"
  old_network="${rest%%|*}"; rest="${rest#*|}"
  old_interface="${rest%%|*}"; rest="${rest#*|}"
  old_port="${rest%%|*}"; rest="${rest#*|}"
  old_dir="${rest%%|*}"
  if [ "$network" = "$old_network" ] || [ "$interface" = "$old_interface" ] || [ "$port" = "$old_port" ] || [ "$data_dir" = "$old_dir" ]; then
   invalid_instances="$invalid_instances $section $old_section"
   instance_error "$section" "duplicate network/interface/port/data_dir with $old_section"
  fi
 done
 instance_records="${instance_records}${instance_records:+
}$section|$network|$interface|$port|$data_dir|$log_level|$history|$dns"
}

start_instance() {
 local section="$1" network interface port data_dir log_level history dns status actual
 case " $invalid_instances " in *" $section "*) return 0;; esac
 config_get network "$section" network lan
 config_get interface "$section" interface br-lan
 config_get port "$section" port 8765
 config_get data_dir "$section" data_dir /usr/share/quickstart-netpolicy
 config_get log_level "$section" log_level info
 config_get_bool history "$section" traffic_history 0
 config_get_bool dns "$section" dns_monitoring 0
 status="$(ubus -S call "network.interface.$network" status 2>/dev/null)" || { instance_error "$section" 'network unavailable'; return 0; }
 actual="$(printf '%s' "$status" | jsonfilter -e '@.l3_device')"
 [ "$actual" = "$interface" ] || { instance_error "$section" 'interface does not match logical network'; return 0; }
 [ -d "/sys/class/net/$interface" ] || { instance_error "$section" 'interface unavailable'; return 0; }
 procd_open_instance "$section"
 procd_set_param command "$binary" --iface "$interface" --port "$port" --data-dir "$data_dir" --network-id "$network" --instance-id "$section" --log-level "$log_level" --enable-traffic
 [ "$history" -eq 1 ] && procd_append_param command --traffic-enable-storage
 [ "$dns" -eq 1 ] && procd_append_param command --enable-dns
 procd_set_param netdev "$interface"
 procd_set_param respawn 3600 5 3
 procd_set_param term_timeout 10
 procd_set_param stdout 1
 procd_set_param stderr 1
 procd_set_param limits core="0 0"
 procd_close_instance
}

start_service() {
 local instance_records='' invalid_instances='' record binary="$PROG"
 [ -x "$binary" ] || { instance_error main "missing executable $binary"; return 1; }
 config_load quickstart-netpolicy
 config_foreach collect_instance quickstart-netpolicy
 local IFS='
'
 for record in $instance_records; do start_instance "${record%%|*}"; done
}

# procd compares named instance commands, so unchanged networks keep their
# processes and TC links. Do not implement reload as stop/start.
reload_service() {
 rc_procd start_service
}
