Merge branch 'amin/wifidm_improve_reload' into 'devel'

wifidmd: Improve the wait wifi relaod API

See merge request feed/iopsys!1971
This commit is contained in:
Amin Ben Romdhane 2025-12-09 16:20:21 +00:00 committed by IOPSYS Dev
commit 5e9e9a8772
No known key found for this signature in database

View file

@ -2,197 +2,411 @@
# Script: bbf_config_reload.sh # Script: bbf_config_reload.sh
# Description: # Description:
# This script reloads WiFi Configs based on input args. # Reloads WiFi configurations based on provided arguments.
# Input args should be one of the below or both with space separate #
# - "wireless"
# - "mapcontroller"
#
# Usage: # 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: # Actions:
# - If both wireless and mapcontroller → Reload mapcontroller (SIGHUP) # - If both "wireless" and "mapcontroller" are specified:
# - If only wireless → Commit wireless config via ubus # → Reload mapcontroller (send SIGHUP)
# - If only mapcontroller → Reload mapcontroller (SIGHUP) # - If only "wireless" is specified:
# - Otherwise → Do nothing # → Commit wireless configuration via ubus
# - If only "mapcontroller" is specified:
# → Reload mapcontroller (send SIGHUP)
# - If no valid arguments are provided:
# → Do nothing
log() { . /lib/functions.sh
echo "${@}"|logger -t bbf.config.wifi.reload -p info
# ------------------------------------------------------
# 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 # Parse AP from mapcontroller config
log "Error: No input provided" # ------------------------------------------------------
exit 1 parse_mapcontroller_ap() {
fi local section="$1"
# Normalize inputs (default to 0 if not set) config_get type "$section" type
wireless=0 [ "$type" = "fronthaul" ] || return 0
mapcontroller=0
for arg in ${input}; do config_get enabled "$section" enabled
if [ "${arg}" == "wireless" ]; then [ "$enabled" != "0" ] || return 0
wireless=1
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 fi
if [ "${arg}" == "mapcontroller" ]; then AP_COUNT=$((AP_COUNT + 1))
mapcontroller=1 }
fi
done
# ------------------------------------------------------
# Match AP from mapcontroller to wireless interface
# ------------------------------------------------------
match_ap_to_wireless_interface() {
local section="$1"
################################################################################ config_get disabled "$section" disabled
# wait_for_wifi_reload [ "$disabled" = "1" ] && return 0
#
# 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
# Check if wifi ubus object exists config_get multi_ap "$section" multi_ap
ubus -t 2 wait_for wifi >/dev/null 2>&1 [ "$multi_ap" = "2" ] || return 0
if [ "$?" -ne 0 ]; then
log "wifi ubus object not available, skipping wait logic" 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 return 0
fi 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 config_get IFNAME "$section" ifname
while [ "${iter}" -lt "${MAX_ITER}" ]; do MATCHED_IFNAME="$IFNAME"
iter=$((iter + 1)) RADIO_DISABLED="$radio_disabled"
start=$(date +%s) }
ubus call wifi status >/dev/null 2>&1
#rc=$?
end=$(date +%s)
elapsed=$((end - start))
#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 for i in $(seq 0 $((AP_COUNT - 1))); do
if [ "${elapsed}" -gt "${THRESH}" ] && [ "${iter}" -ge "${MIN_ITER}" ]; then eval "SSID=\$AP_SSID_$i"
log "Detected long ubus response (${elapsed}s) after ${iter}s → assuming WiFi reload complete" eval "BAND=\$AP_BAND_$i"
return 0 eval "ENCRYPTION=\$AP_ENCRYPTION_$i"
fi eval "KEY=\$AP_KEY_$i"
# Sleep 1s between checks debug "$log_prefix matching AP[$i]: ssid=|$SSID| band=|$BAND| encryption=|$ENCRYPTION| key=|$KEY|"
if [ "${iter}" -lt "${MAX_ITER}" ]; then
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 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 fi
done done
log "Timeout after ${MAX_ITER}s — WiFi reload not confirmed"
return 1
} }
############################################################################### # ------------------------------------------------------
# mark_ap_instances_applied # Verify APs runtime state via ubus
# # ------------------------------------------------------
# Description: verify_aps_runtime_state() {
# Finalizes newly created WiFi AccessPoint instances in the dmmap WiFi log_prefix="verify_aps_runtime_state:"
# 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__)
if [ "${is_new}" = "1" ]; then for i in $(seq 0 $((AP_COUNT - 1))); do
config_sec_name=$(uci -q -c /etc/bbfdm/dmmap get WiFi.${sec}.__section_name__) eval "ifname=\$AP_IFNAME_$i"
case "${config_sec_name}" in eval "ssid=\$AP_SSID_$i"
mapcontroller.*) eval "band=\$AP_BAND_$i"
# New mapcontroller AP instance — remove creation flag only
uci -q -c /etc/bbfdm/dmmap delete WiFi.${sec}.__is_new__
;;
wireless.*) if [ -z "$ifname" ]; then
# New wireless AP instance — skip mapcontroller reload debug "$log_prefix ifname is empty for AP[$i] ssid=$ssid due to radio disabled"
mapcontroller=0 continue
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
fi 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 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() { reload_mapcontroller() {
pid=$(pidof mapcontroller) pid=$(pidof mapcontroller)
if [ -n "$pid" ]; then if [ -n "$pid" ]; then
log "Reloading mapcontroller (PID: $pid)..." info "Reloading mapcontroller (PID: $pid)..."
kill -SIGHUP "$pid" kill -SIGHUP "$pid"
wait_for_wifi_reload sleep 5
validate_mapcontroller_changes
else else
log "Warning: mapcontroller process not found" info "Warning: mapcontroller process not found"
fi fi
} }
# Define function to commit wireless config # ------------------------------------------------------
commit_wireless_config() { # Reload wireless service
log "Committing wireless config..." # ------------------------------------------------------
reload_wireless() {
info "Reloading wireless config..."
ubus call uci commit '{"config":"wireless"}' 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 $DMAP_PATH commit WiFi
if [ "$mapcontroller" -eq 1 ]; then }
# ------------------------------------------------------
# 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 reload_mapcontroller
elif [ "$wireless" -eq 1 ]; then elif [ "$WIRELESS" -eq 1 ]; then
commit_wireless_config reload_wireless
else else
log "No action needed." info "No valid arguments provided."
exit 1 exit 1
fi fi