diff --git a/wifidmd/files/etc/wifidmd/bbf_config_reload.sh b/wifidmd/files/etc/wifidmd/bbf_config_reload.sh index 24f672dac..5384277ee 100755 --- a/wifidmd/files/etc/wifidmd/bbf_config_reload.sh +++ b/wifidmd/files/etc/wifidmd/bbf_config_reload.sh @@ -2,197 +2,411 @@ # Script: bbf_config_reload.sh # Description: -# This script reloads WiFi Configs based on input args. -# Input args should be one of the below or both with space separate -# - "wireless" -# - "mapcontroller" -# +# Reloads WiFi configurations based on provided arguments. +# # Usage: -# sh bbf_config_reload.sh wireless mapcontroller -# +# sh bbf_config_reload.sh [wireless] [mapcontroller] +# +# Input arguments: +# wireless Reload only wireless configuration +# mapcontroller Reload only mapcontroller configuration +# # Actions: -# - If both wireless and mapcontroller → Reload mapcontroller (SIGHUP) -# - If only wireless → Commit wireless config via ubus -# - If only mapcontroller → Reload mapcontroller (SIGHUP) -# - Otherwise → Do nothing +# - If both "wireless" and "mapcontroller" are specified: +# → Reload mapcontroller (send SIGHUP) +# - If only "wireless" is specified: +# → Commit wireless configuration via ubus +# - If only "mapcontroller" is specified: +# → Reload mapcontroller (send SIGHUP) +# - If no valid arguments are provided: +# → Do nothing -log() { - echo "${@}"|logger -t bbf.config.wifi.reload -p info +. /lib/functions.sh + +# ------------------------------------------------------ +# Global variables +# ------------------------------------------------------ +MAPCONTROLLER=0 +WIRELESS=0 +MAX_RETRIES=15 +AP_COUNT=0 + +# ------------------------------------------------------ +# Info log +# ------------------------------------------------------ +info() { + echo "${@}" | logger -t bbf.config.wifi.reload -p info } -input="$@" +# ------------------------------------------------------ +# Debug log +# ------------------------------------------------------ +debug() { + echo "${@}" +} -# Validate input -if [ -z "$input" ]; then - log "Error: No input provided" - exit 1 -fi +# ------------------------------------------------------ +# Parse AP from mapcontroller config +# ------------------------------------------------------ +parse_mapcontroller_ap() { + local section="$1" -# Normalize inputs (default to 0 if not set) -wireless=0 -mapcontroller=0 + config_get type "$section" type + [ "$type" = "fronthaul" ] || return 0 -for arg in ${input}; do - if [ "${arg}" == "wireless" ]; then - wireless=1 + config_get enabled "$section" enabled + [ "$enabled" != "0" ] || return 0 + + config_get ssid "$section" ssid + [ -n "$ssid" ] || return 0 + + config_get band "$section" band + [ -n "$band" ] || return 0 + + case "$band" in + 2|5|6) ;; + *) return 0 ;; + esac + + config_get encryption "$section" encryption + config_get key "$section" key + + eval "AP_SSID_${AP_COUNT}='${ssid}'" + eval "AP_BAND_${AP_COUNT}='${band}'" + eval "AP_ENCRYPTION_${AP_COUNT}='${encryption}'" + eval "AP_KEY_${AP_COUNT}='${key}'" + + debug "parse_mapcontroller_ap: AP[$AP_COUNT] ssid=|$ssid| band=|$band| encryption=|$encryption| key=|$key|" + + AP_COUNT=$((AP_COUNT + 1)) +} + +# ------------------------------------------------------ +# Parse wireless APs (multi_ap, enabled, ssid, band, key, encryption) +# ------------------------------------------------------ +parse_wireless_ap() { + log_prefix="parse_wireless_ap:" + local section="$1" + local matched_ifname="" + + config_get disabled "$section" disabled + [ "$disabled" = "1" ] && return 0 + + config_get multi_ap "$section" multi_ap + case "$multi_ap" in + 1|2) return 0 ;; + esac + + config_get ssid "$section" ssid + [ -n "$ssid" ] || return 0 + + config_get device "$section" device + [ -n "$device" ] || return 0 + + config_get band "$device" band + case "$band" in + 2g) band="2" ;; + 5g) band="5" ;; + 6g) band="6" ;; + *) return 0 ;; + esac + + config_get radio_disabled "$device" disabled + [ "$radio_disabled" = "1" ] && return 0 + + config_get encryption "$section" encryption + config_get key "$section" key + + eval "AP_SSID_${AP_COUNT}='${ssid}'" + eval "AP_BAND_${AP_COUNT}='${band}'" + eval "AP_KEY_${AP_COUNT}='${key}'" + eval "AP_ENCRYPTION_${AP_COUNT}='${encryption}'" + + debug "$log_prefix AP[$AP_COUNT] iface=$section ssid=|$ssid| band=|$band| encryption=|$encryption| key=|$key|" + + for try in $(seq 1 "$MAX_RETRIES"); do + config_get matched_ifname "$section" ifname + + if [ -n "$matched_ifname" ]; then + debug "$log_prefix matched AP[$AP_COUNT] → ifname=$matched_ifname" + eval "AP_IFNAME_${AP_COUNT}='${matched_ifname}'" + break + fi + + debug "$log_prefix no ifname for AP[$AP_COUNT] ssid=$ssid (try $try/$MAX_RETRIES)" + sleep 1 + config_load wireless + done + + eval "tmp_ifname=\$AP_IFNAME_${AP_COUNT}" + if [ -z "$tmp_ifname" ]; then + info "$log_prefix FAIL: could not match AP[$AP_COUNT] ssid=$ssid after $MAX_RETRIES tries" fi - if [ "${arg}" == "mapcontroller" ]; then - mapcontroller=1 - fi -done + AP_COUNT=$((AP_COUNT + 1)) +} +# ------------------------------------------------------ +# Match AP from mapcontroller to wireless interface +# ------------------------------------------------------ +match_ap_to_wireless_interface() { + local section="$1" -################################################################################ -# wait_for_wifi_reload -# -# Description: -# Currently, there is no direct way to determine when WiFi reload -# operations have fully completed. The ubus API does not provide any -# event or flag indicating that WiFi services have finished reloading. -# -# To work around this, we use a timing-based heuristic: -# - We repeatedly run `ubus call wifi status` every 1 second. -# - Each call is timed (using `date +%s` before and after). -# - Normally, this command returns almost instantly (<1s). -# - However, during a WiFi reload, the call may take longer -# (several seconds) while the subsystem is busy. -# - Once we detect that `ubus call wifi status` takes longer than -# 2 seconds to return, we assume the reload has been applied -# and completed successfully. -# -# Additional wait constraints: -# - Minimum wait: 5 seconds (to ensure reload started). -# - Maximum wait: 15 seconds (to prevent indefinite blocking). -# - If the threshold is not met within 15s, we log a timeout warning. -################################################################################ -wait_for_wifi_reload() { - MAX_ITER=15 - MIN_ITER=5 - THRESH=2 # seconds + config_get disabled "$section" disabled + [ "$disabled" = "1" ] && return 0 - # Check if wifi ubus object exists - ubus -t 2 wait_for wifi >/dev/null 2>&1 - if [ "$?" -ne 0 ]; then - log "wifi ubus object not available, skipping wait logic" + config_get multi_ap "$section" multi_ap + [ "$multi_ap" = "2" ] || return 0 + + config_get w_ssid "$section" ssid + [ "$w_ssid" = "$SSID" ] || return 0 + + config_get device "$section" device + [ -n "$device" ] || return 0 + + config_get ap_band "$device" band + case "$ap_band" in + 2g) ap_band="2" ;; + 5g) ap_band="5" ;; + 6g) ap_band="6" ;; + *) return 0 ;; + esac + [ "$ap_band" = "$BAND" ] || return 0 + + config_get radio_disabled "$device" disabled + [ "$radio_disabled" = "1" ] && return 0 + + config_get w_key "$section" key + if [ -n "$KEY" ] && [ "$w_key" != "$KEY" ]; then return 0 fi - #log "Waiting for WiFi reload (min ${MIN_ITER}s, max ${MAX_ITER}s)..." + config_get w_enc "$section" encryption + if [ -n "$ENCRYPTION" ] && [ "$w_enc" != "$ENCRYPTION" ]; then + return 0 + fi - iter=0 - while [ "${iter}" -lt "${MAX_ITER}" ]; do - iter=$((iter + 1)) - start=$(date +%s) - ubus call wifi status >/dev/null 2>&1 - #rc=$? - end=$(date +%s) - elapsed=$((end - start)) + config_get IFNAME "$section" ifname + MATCHED_IFNAME="$IFNAME" + RADIO_DISABLED="$radio_disabled" +} - #log "wait_for_wifi_reload: iter=${iter}, rc=${rc}, elapsed=${elapsed}s" +# ------------------------------------------------------ +# Iterate over stored APs and match them to wireless interfaces +# ------------------------------------------------------ +match_aps_to_wireless_interfaces() { + log_prefix="match_aps_to_wireless_interfaces:" + config_load wireless - # If ubus took >2s and we've waited at least MIN_ITER → assume reload done - if [ "${elapsed}" -gt "${THRESH}" ] && [ "${iter}" -ge "${MIN_ITER}" ]; then - log "Detected long ubus response (${elapsed}s) after ${iter}s → assuming WiFi reload complete" - return 0 - fi + for i in $(seq 0 $((AP_COUNT - 1))); do + eval "SSID=\$AP_SSID_$i" + eval "BAND=\$AP_BAND_$i" + eval "ENCRYPTION=\$AP_ENCRYPTION_$i" + eval "KEY=\$AP_KEY_$i" - # Sleep 1s between checks - if [ "${iter}" -lt "${MAX_ITER}" ]; then + debug "$log_prefix matching AP[$i]: ssid=|$SSID| band=|$BAND| encryption=|$ENCRYPTION| key=|$KEY|" + + MATCHED_IFNAME="" + RADIO_DISABLED="0" + + for try in $(seq 1 "$MAX_RETRIES"); do + config_foreach match_ap_to_wireless_interface wifi-iface + + if [ -n "$MATCHED_IFNAME" ]; then + debug "$log_prefix matched AP[$i] → ifname=$MATCHED_IFNAME" + eval "AP_IFNAME_$i=\$MATCHED_IFNAME" + break + fi + + if [ "$RADIO_DISABLED" = "1" ]; then + debug "$log_prefix radio is disabled, no need to retry" + break + fi + + debug "$log_prefix no match for AP[$i] ssid=$SSID (try $try/$MAX_RETRIES)" sleep 1 + config_load wireless + done + + eval "tmp_ifname=\$AP_IFNAME_$i" + if [ -z "$tmp_ifname" ] && [ "$RADIO_DISABLED" != "1" ]; then + info "$log_prefix FAIL: could not match AP[$i] ssid=$SSID after $MAX_RETRIES tries" fi done - - log "Timeout after ${MAX_ITER}s — WiFi reload not confirmed" - return 1 } -############################################################################### -# mark_ap_instances_applied -# -# Description: -# Finalizes newly created WiFi AccessPoint instances in the dmmap WiFi -# configuration. -# -# An AccessPoint instance is considered a *new instance* when the option -# __is_new__ exists and its value is "1". -# -# New instance handling logic: -# -# • The AccessPoint's controller-side section name is obtained from -# the __section_name__ option (e.g., "mapcontroller.xxx" or "wireless.xxx"). -# -# • For new instances where __section_name__ begins with "mapcontroller.": -# - Remove __is_new__ -# - No additional actions required -# -# • For new instances where __section_name__ begins with "wireless.": -# - Set mapcontroller=0 (indicates mapcontroller reload is not required) -# - Remove __is_new__ -# -# • For all other prefixes: -# - Remove __is_new__ only -# -# After all new instances are processed, the dmmap WiFi configuration is committed. -############################################################################### -mark_ap_instances_applied() { - for sec in $(uci -q -c /etc/bbfdm/dmmap show WiFi | grep "=AccessPoint" | cut -d. -f2 | cut -d= -f1); do - is_new=$(uci -q -c /etc/bbfdm/dmmap get WiFi.${sec}.__is_new__) +# ------------------------------------------------------ +# Verify APs runtime state via ubus +# ------------------------------------------------------ +verify_aps_runtime_state() { + log_prefix="verify_aps_runtime_state:" - if [ "${is_new}" = "1" ]; then - config_sec_name=$(uci -q -c /etc/bbfdm/dmmap get WiFi.${sec}.__section_name__) - case "${config_sec_name}" in - mapcontroller.*) - # New mapcontroller AP instance — remove creation flag only - uci -q -c /etc/bbfdm/dmmap delete WiFi.${sec}.__is_new__ - ;; + for i in $(seq 0 $((AP_COUNT - 1))); do + eval "ifname=\$AP_IFNAME_$i" + eval "ssid=\$AP_SSID_$i" + eval "band=\$AP_BAND_$i" - wireless.*) - # New wireless AP instance — skip mapcontroller reload - mapcontroller=0 - uci -q -c /etc/bbfdm/dmmap delete WiFi.${sec}.__is_new__ - ;; - - *) - # Other AP instance types — remove creation flag only - uci -q -c /etc/bbfdm/dmmap delete WiFi.${sec}.__is_new__ - ;; - esac + if [ -z "$ifname" ]; then + debug "$log_prefix ifname is empty for AP[$i] ssid=$ssid due to radio disabled" + continue fi + + debug "$log_prefix validating runtime for IFNAME=|$ifname| ssid=|$ssid| band=|$band|" + + case "$band" in + 2) expected_ubus_band="2.4GHz" ;; + 5) expected_ubus_band="5GHz" ;; + 6) expected_ubus_band="6GHz" ;; + *) + debug "$log_prefix unknown band '$band' for AP[$i]" + continue + ;; + esac + + ok=0 + for try in $(seq 1 "$MAX_RETRIES"); do + json="$(ubus call wifi.ap."$ifname" status 2>/dev/null)" + + if [ -z "$json" ]; then + debug "$log_prefix empty ubus response for $ifname (try $try/$MAX_RETRIES)" + sleep 2 + continue + fi + + ubus_ssid="$(echo "$json" | jsonfilter -q -e '@.ssid')" + ubus_band="$(echo "$json" | jsonfilter -q -e '@.band')" + + if [ "$ubus_ssid" != "$ssid" ]; then + debug "$log_prefix ubus ssid mismatch for $ifname: expected=$ssid got=$ubus_ssid (try $try)" + sleep 2 + continue + fi + + if [ "$ubus_band" != "$expected_ubus_band" ]; then + debug "$log_prefix ubus band mismatch for $ifname: expected=$expected_ubus_band got=$ubus_band (try $try)" + sleep 2 + continue + fi + + ok=1 + break + done + + if [ "$ok" -ne 1 ]; then + info "$log_prefix FAIL: runtime validation failed for $ifname" + continue + fi + + debug "$log_prefix IFACE=$ifname runtime validated" done - uci -q -c /etc/bbfdm/dmmap commit WiFi + debug "$log_prefix runtime validation completed" } -# Define function to reload mapcontroller +# ------------------------------------------------------ +# Validate mapcontroller config changes +# ------------------------------------------------------ +validate_mapcontroller_changes() { + AP_COUNT=0 + + config_load mapcontroller + config_foreach parse_mapcontroller_ap ap + + if [ "$AP_COUNT" -eq 0 ]; then + debug "reload_mapcontroller: no AP found in mapcontroller" + return 0 + fi + + match_aps_to_wireless_interfaces + verify_aps_runtime_state +} + +# ------------------------------------------------------ +# Validate wireless config changes +# ------------------------------------------------------ +validate_wireless_changes() { + AP_COUNT=0 + + config_load wireless + config_foreach parse_wireless_ap wifi-iface + + if [ "$AP_COUNT" -eq 0 ]; then + debug "reload_wireless: no AP found in wireless" + return 0 + fi + + verify_aps_runtime_state +} + +# ------------------------------------------------------ +# Reload mapcontroller service +# ------------------------------------------------------ reload_mapcontroller() { pid=$(pidof mapcontroller) if [ -n "$pid" ]; then - log "Reloading mapcontroller (PID: $pid)..." + info "Reloading mapcontroller (PID: $pid)..." kill -SIGHUP "$pid" - wait_for_wifi_reload + sleep 5 + validate_mapcontroller_changes else - log "Warning: mapcontroller process not found" - fi + info "Warning: mapcontroller process not found" + fi } -# Define function to commit wireless config -commit_wireless_config() { - log "Committing wireless config..." +# ------------------------------------------------------ +# Reload wireless service +# ------------------------------------------------------ +reload_wireless() { + info "Reloading wireless config..." ubus call uci commit '{"config":"wireless"}' - wait_for_wifi_reload + sleep 5 + validate_wireless_changes } -# Finalize newly created AP AccessPoint instances -mark_ap_instances_applied +# ------------------------------------------------------ +# Finalize Access Point instances +# ------------------------------------------------------ +finalize_ap_instances() { + DMAP_PATH="uci -q -c /etc/bbfdm/dmmap" + for sec in $($DMAP_PATH show WiFi | grep "=AccessPoint" | cut -d. -f2 | cut -d= -f1); do + is_new=$($DMAP_PATH get WiFi.${sec}.__is_new__) + [ "$is_new" = "1" ] || continue + + sec_name=$($DMAP_PATH get WiFi.${sec}.__section_name__) + case "$sec_name" in + wireless.*) MAPCONTROLLER=0 ;; + esac + $DMAP_PATH delete WiFi.${sec}.__is_new__ + done -# Apply logic based on flags -if [ "$mapcontroller" -eq 1 ]; then + $DMAP_PATH commit WiFi +} + +# ------------------------------------------------------ +# Main argument parser +# ------------------------------------------------------ +for arg in "$@"; do + case "$arg" in + wireless) WIRELESS=1 ;; + mapcontroller) MAPCONTROLLER=1 ;; + *) + info "Unknown option: $arg" + exit 1 + ;; + esac +done + +# ------------------------------------------------------ +# Action logic +# ------------------------------------------------------ +finalize_ap_instances + +if [ "$MAPCONTROLLER" -eq 1 ]; then reload_mapcontroller -elif [ "$wireless" -eq 1 ]; then - commit_wireless_config +elif [ "$WIRELESS" -eq 1 ]; then + reload_wireless else - log "No action needed." + info "No valid arguments provided." exit 1 fi