mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-09 23:34:51 +01:00
[WIP] wifidmd: Improve the wait wifi relaod API
This commit is contained in:
parent
5fd540d5ba
commit
c8b5878332
1 changed files with 360 additions and 146 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue