#!/bin/sh echo "Content-type: text/plain" echo "" # Configuration CACHE_FILE="/tmp/wifi_iface_cache.map" DATE_FILE="/tmp/wifi_last_scan.date" LOCK_DIR="/tmp/wifi_scan.lock" # PID-based temp file to ensure zero collision between concurrent writes MY_TMP="/tmp/wifi_cache.$$.tmp" TODAY=$(date +%Y-%m-%d) perform_scan() { # Initialize our private temp file : > "$MY_TMP" # Format: label|phy_prefix|exact_ssid cat </dev/null | grep -q "ESSID: \"$ssid\""; then echo "${label}:${iface}" >> "$MY_TMP" break fi done done # Move our private file to the global cache (Atomic Move) mv "$MY_TMP" "$CACHE_FILE" echo "$TODAY" > "$DATE_FILE" } # --- Atomic Lock & Cache Logic --- needs_refresh=0 if [ ! -f "$CACHE_FILE" ] || [ ! -f "$DATE_FILE" ] || [ "$(cat "$DATE_FILE" 2>/dev/null)" != "$TODAY" ]; then needs_refresh=1 fi if [ "$needs_refresh" -eq 1 ]; then # Try to acquire the lock if mkdir "$LOCK_DIR" 2>/dev/null; then # This process is the designated "Scanner" perform_scan rmdir "$LOCK_DIR" else # Another process is already scanning. # Wait up to 2 seconds for them to finish so we don't return empty results. counter=0 while [ -d "$LOCK_DIR" ] && [ $counter -lt 10 ]; do sleep 0.2 counter=$((counter + 1)) done fi fi # --- Execution --- # Always read from the final CACHE_FILE if [ -s "$CACHE_FILE" ]; then while IFS=: read -r label iface; do # iwinfo "$iface" assoclist | grep 'ms ago' | awk -v lbl="$label" '{print lbl " " $1;}' ubus call hostapd."$iface" get_clients 2>/dev/null | grep -oE "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}" | awk -v lbl="$label" '{print lbl " " toupper($1);}' done < "$CACHE_FILE" fi # Cleanup: if this process's temp file exists (due to a failure), remove it rm -f "$MY_TMP"