wifidmd: 1.4.11: improve wait mechanism during Wi-Fi reload

This commit is contained in:
Amin Ben Romdhane 2026-01-09 12:57:21 +00:00 committed by IOPSYS Dev
parent 44c6ff558a
commit 2426bac7c5
No known key found for this signature in database
2 changed files with 302 additions and 176 deletions

View file

@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=wifidmd
PKG_VERSION:=1.4.10
PKG_VERSION:=1.4.11
LOCAL_DEV:=0
ifneq ($(LOCAL_DEV),1)

View file

@ -2,197 +2,323 @@
# 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
. /usr/share/libubox/jshn.sh
MAPCONTROLLER=0
WIRELESS=0
MAX_RETRIES=15
info() {
echo "${@}" | logger -t bbf.config.wifi.reload -p info
}
input="$@"
debug() {
echo "${@}"
}
# Validate input
if [ -z "$input" ]; then
log "Error: No input provided"
exit 1
fi
json_add_aps() {
json_add_object
json_add_string ssid "$1"
json_add_string band "$2"
json_add_string encryption "$3"
json_add_string key "$4"
json_add_string ifname "$5"
json_close_object
}
# Normalize inputs (default to 0 if not set)
wireless=0
mapcontroller=0
parse_mapcontroller_ap() {
local section="$1"
for arg in ${input}; do
if [ "${arg}" == "wireless" ]; then
wireless=1
config_get type "$section" type "fronthaul"
[ "$type" = "fronthaul" ] || return 0
config_get enabled "$section" enabled 1
[ "$enabled" = "1" ] || 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 "none"
config_get key "$section" key
json_add_aps "$ssid" "$band" "$encryption" "$key" ""
debug "mapcontroller AP added: ssid=|$ssid| band=|$band| encryption=|$encryption| key=|$key|"
}
parse_wireless_ap() {
local section="$1"
config_get disabled "$section" disabled 0
[ "$disabled" = "1" ] && return 0
config_get multi_ap "$section" multi_ap 0
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 0
[ "$radio_disabled" = "1" ] && return 0
config_get encryption "$section" encryption "none"
config_get key "$section" key
for try in $(seq 1 "$MAX_RETRIES"); do
config_get ifname "$section" ifname
[ -n "$ifname" ] && break
debug "FAIL: no ifname found for ssid=|$ssid| band=|$band| (try $try/$MAX_RETRIES)"
sleep 1
config_load wireless
done
if [ -z "$ifname" ]; then
info "FAIL: could not match wireless AP with ssid=|$ssid| band=|$band| after $MAX_RETRIES tries"
return O
fi
if [ "${arg}" == "mapcontroller" ]; then
mapcontroller=1
json_add_aps "$ssid" "$band" "$encryption" "$key" "$ifname"
debug "wireless AP added: ssid=|$ssid| band=|$band| ifname=|$ifname|"
}
match_ap_to_wireless_interface() {
local section="$1"
config_get disabled "$section" disabled 0
[ "$disabled" = "1" ] && return 0
config_get multi_ap "$section" multi_ap 0
[ "$multi_ap" = "2" ] || return 0
config_get ssid "$section" ssid
[ "$ssid" = "$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
[ "$band" = "$BAND" ] || return 0
config_get radio_disabled "$device" disabled 0
[ "$radio_disabled" = "1" ] && RADIO_DISABLED=1 && return 0
config_get key "$section" key
[ -n "$KEY" ] && [ "$key" != "$KEY" ] && return 0
config_get encryption "$section" encryption "none"
[ -n "$ENCRYPTION" ] && [ "$encryption" != "$ENCRYPTION" ] && return 0
config_get MATCHED_IFNAME "$section" ifname
}
match_aps_to_wireless_interfaces() {
config_load wireless
json_get_keys indices
for i in $indices; do
json_select "$i"
json_get_var SSID ssid
json_get_var BAND band
json_get_var ENCRYPTION encryption
json_get_var KEY key
debug "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
json_add_string ifname "$MATCHED_IFNAME"
debug "matched AP[$i]: ssid=|$SSID| band=|$BAND| -> ifname=|$MATCHED_IFNAME|"
break
fi
[ "$RADIO_DISABLED" = "1" ] && break
sleep 1
config_load wireless
done
if [ -z "$MATCHED_IFNAME" ] && [ "$RADIO_DISABLED" != "1" ]; then
info "FAIL: no match for AP[$i]: ssid=|$SSID| band=|$BAND| after $MAX_RETRIES tries"
fi
json_select ..
done
}
verify_aps_runtime_state() {
json_get_keys indices
for i in $indices; do
json_select "$i"
json_get_var ifname ifname
json_get_var ssid ssid
json_get_var band band
[ -z "$ifname" ] && json_select .. && continue
debug "validating runtime AP[$i]: ssid=|$ssid| band=|$band| ifname=|$ifname|"
case "$band" in
2) bandstr="2.4GHz" ;;
5) bandstr="5GHz" ;;
6) bandstr="6GHz" ;;
*) json_select .. && 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 "FAIL: empty ubus response for wifi.ap.$ifname status (try $try/$MAX_RETRIES)"
sleep 2
continue
fi
ubus_ssid="$(echo "$json" | jsonfilter -q -e '@.ssid')"
ubus_band="$(echo "$json" | jsonfilter -q -e '@.band')"
[ "$ubus_ssid" = "$ssid" -a "$ubus_band" = "$bandstr" ] && {
ok=1
break
}
sleep 2
done
[ "$ok" -ne 1 ] && info "FAIL: runtime validation failed for AP[$i]: ssid=|$ssid| band=|$band| ifname=|$ifname|"
json_select ..
debug "runtime validation completed for AP[$i]: ssid=|$ssid| band=|$band| ifname=|$ifname|"
done
}
validate_mapcontroller_changes() {
json_init
json_add_array aps
config_load mapcontroller
config_foreach parse_mapcontroller_ap ap
match_aps_to_wireless_interfaces
verify_aps_runtime_state
json_cleanup
}
validate_wireless_changes() {
json_init
json_add_array aps
config_load wireless
config_foreach parse_wireless_ap wifi-iface
verify_aps_runtime_state
json_cleanup
}
reload_mapcontroller() {
pid="$(pidof mapcontroller)"
if [ -n "$pid" ]; then
info "Reloading mapcontroller (PID: $pid)..."
kill -SIGHUP "$pid"
sleep 5
validate_mapcontroller_changes
else
info "Warning: mapcontroller process not found"
fi
}
reload_wireless() {
info "Reloading wireless config..."
ubus call uci commit '{"config":"wireless"}'
sleep 5
validate_wireless_changes
}
dmmap_commit_new_aps() {
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
$DMAP_PATH commit WiFi
}
for arg in "$@"; do
case "$arg" in
wireless) WIRELESS=1 ;;
mapcontroller) MAPCONTROLLER=1 ;;
*)
info "Unknown option: $arg"
exit 1
;;
esac
done
dmmap_commit_new_aps
################################################################################
# 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
# 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"
return 0
fi
#log "Waiting for WiFi reload (min ${MIN_ITER}s, max ${MAX_ITER}s)..."
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))
#log "wait_for_wifi_reload: iter=${iter}, rc=${rc}, elapsed=${elapsed}s"
# 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
# Sleep 1s between checks
if [ "${iter}" -lt "${MAX_ITER}" ]; then
sleep 1
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__)
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__
;;
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
fi
done
uci -q -c /etc/bbfdm/dmmap commit WiFi
}
# Define function to reload mapcontroller
reload_mapcontroller() {
pid=$(pidof mapcontroller)
if [ -n "$pid" ]; then
log "Reloading mapcontroller (PID: $pid)..."
kill -SIGHUP "$pid"
wait_for_wifi_reload
else
log "Warning: mapcontroller process not found"
fi
}
# Define function to commit wireless config
commit_wireless_config() {
log "Committing wireless config..."
ubus call uci commit '{"config":"wireless"}'
wait_for_wifi_reload
}
# Finalize newly created AP AccessPoint instances
mark_ap_instances_applied
# Apply logic based on flags
if [ "$mapcontroller" -eq 1 ]; then
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