|
@@ -251,70 +251,75 @@ end
|
|
|
-- UBUS EVENT LISTENER (TRUE event-driven!)
|
|
-- UBUS EVENT LISTENER (TRUE event-driven!)
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
-local function start_ubus_listener()
|
|
|
|
|
|
|
+local function start_ubus_listen()
|
|
|
if not luv_ok then
|
|
if not luv_ok then
|
|
|
- log_info("luv not available - cannot listen to ubus events")
|
|
|
|
|
|
|
+ log_info("luv not available")
|
|
|
return false
|
|
return false
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
- log_info("Starting ubus event listener...")
|
|
|
|
|
|
|
+ log_info("Starting ubus listen (event-driven)...")
|
|
|
|
|
|
|
|
- -- Use ubus listen in a subprocess to get events
|
|
|
|
|
- -- This is event-driven - we get notified immediately when something changes
|
|
|
|
|
- local function listen_ubus_events()
|
|
|
|
|
- log_info("Listening to ubus events...")
|
|
|
|
|
|
|
+ -- Use ubus listen in background process - this is TRUE event-driven!
|
|
|
|
|
+ -- ubus listen blocks and emits events when they happen
|
|
|
|
|
+ local function ubus_listen_loop()
|
|
|
|
|
+ -- Spawn ubus listen as background process
|
|
|
|
|
+ -- It will emit JSON events to stdout when DHCP/WiFi events happen
|
|
|
|
|
+ local pipe = io.popen("ubus listen dhcp ipv4leases network.interface.* hostapd.* 2>&1", "r")
|
|
|
|
|
|
|
|
- -- Listen for DHCP, network, and WiFi events
|
|
|
|
|
- local f = io.popen("ubus listen dhcp ipv4leases network.interface.* 2>/dev/null &")
|
|
|
|
|
- if f then f:close() end
|
|
|
|
|
|
|
+ if not pipe then
|
|
|
|
|
+ log_info("ubus listen failed")
|
|
|
|
|
+ return false
|
|
|
|
|
+ end
|
|
|
|
|
|
|
|
- -- Alternative: use netlink for immediate notifications
|
|
|
|
|
- -- This is truly event-driven via netlink socket
|
|
|
|
|
- local timer = luv.new_timer()
|
|
|
|
|
|
|
+ log_info("ubus listen started - waiting for events...")
|
|
|
|
|
|
|
|
- -- Check for changes by comparing state (event-driven trigger)
|
|
|
|
|
- local function check_changes()
|
|
|
|
|
- -- Check DHCP
|
|
|
|
|
- local current_dhcp = get_dhcp_leases()
|
|
|
|
|
- for mac, info in pairs(current_dhcp) do
|
|
|
|
|
- if not state.dhcp[mac] then
|
|
|
|
|
- on_dhcp_event("add", mac, info.ip, info.hostname)
|
|
|
|
|
|
|
+ -- Read events as they come (blocking but event-driven)
|
|
|
|
|
+ while true do
|
|
|
|
|
+ local line = pipe:read("*line")
|
|
|
|
|
+ if not line then break end
|
|
|
|
|
+
|
|
|
|
|
+ log_info("ubus event: " .. line:sub(1, 200))
|
|
|
|
|
+
|
|
|
|
|
+ -- Parse and handle event
|
|
|
|
|
+ local event_type = line:match('"type":"([^"]+)"') or "unknown"
|
|
|
|
|
+
|
|
|
|
|
+ if event_type == "ipv4leases" or event_type == "dhcp" then
|
|
|
|
|
+ -- DHCP event - check for changes
|
|
|
|
|
+ local current_dhcp = get_dhcp_leases()
|
|
|
|
|
+ for mac, info in pairs(current_dhcp) do
|
|
|
|
|
+ if not state.dhcp[mac] then
|
|
|
|
|
+ on_dhcp_event("add", mac, info.ip, info.hostname)
|
|
|
|
|
+ end
|
|
|
end
|
|
end
|
|
|
- end
|
|
|
|
|
- for mac, info in pairs(state.dhcp) do
|
|
|
|
|
- if not current_dhcp[mac] then
|
|
|
|
|
- on_dhcp_event("del", mac, info.ip)
|
|
|
|
|
|
|
+ for mac, info in pairs(state.dhcp) do
|
|
|
|
|
+ if not current_dhcp[mac] then
|
|
|
|
|
+ on_dhcp_event("del", mac, info.ip)
|
|
|
|
|
+ end
|
|
|
end
|
|
end
|
|
|
- end
|
|
|
|
|
-
|
|
|
|
|
- -- Check WiFi
|
|
|
|
|
- local current_clients = get_wifi_clients()
|
|
|
|
|
- for mac, info in pairs(current_clients) do
|
|
|
|
|
- if not state.clients[mac] then
|
|
|
|
|
- on_wifi_event("connected", mac, info.iface)
|
|
|
|
|
|
|
+ elseif event_type:match("hostapd") then
|
|
|
|
|
+ -- WiFi event
|
|
|
|
|
+ local current_clients = get_wifi_clients()
|
|
|
|
|
+ for mac, info in pairs(current_clients) do
|
|
|
|
|
+ if not state.clients[mac] then
|
|
|
|
|
+ on_wifi_event("connected", mac, info.iface)
|
|
|
|
|
+ end
|
|
|
end
|
|
end
|
|
|
- end
|
|
|
|
|
- for mac, info in pairs(state.clients) do
|
|
|
|
|
- if not current_clients[mac] then
|
|
|
|
|
- on_wifi_event("disconnected", mac, info.iface)
|
|
|
|
|
|
|
+ for mac, info in pairs(state.clients) do
|
|
|
|
|
+ if not current_clients[mac] then
|
|
|
|
|
+ on_wifi_event("disconnected", mac, info.iface)
|
|
|
|
|
+ end
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
- -- Start a fast timer that checks for changes
|
|
|
|
|
- -- When state changes, we trigger immediately (no polling, just state diff)
|
|
|
|
|
- luv.timer_start(timer, 1000, 1000, function()
|
|
|
|
|
- check_changes()
|
|
|
|
|
- end)
|
|
|
|
|
-
|
|
|
|
|
- return true
|
|
|
|
|
|
|
+ pipe:close()
|
|
|
|
|
+ log_info("ubus listen ended")
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
- local ok, err = pcall(listen_ubus_events)
|
|
|
|
|
- if not ok then
|
|
|
|
|
- log_info("ubus listener error: " .. tostring(err))
|
|
|
|
|
- return false
|
|
|
|
|
- end
|
|
|
|
|
|
|
+ -- Run ubus listen in background thread
|
|
|
|
|
+ luv.new_task(function()
|
|
|
|
|
+ ubus_listen_loop()
|
|
|
|
|
+ end)
|
|
|
|
|
|
|
|
return true
|
|
return true
|
|
|
end
|
|
end
|
|
@@ -346,19 +351,10 @@ function main()
|
|
|
|
|
|
|
|
if luv_ok then
|
|
if luv_ok then
|
|
|
-- Start event-driven listeners
|
|
-- Start event-driven listeners
|
|
|
- local started = start_ubus_listener()
|
|
|
|
|
|
|
+ start_ubus_listen()
|
|
|
|
|
|
|
|
- if started then
|
|
|
|
|
- log_info("Event listeners started - running event loop")
|
|
|
|
|
- luv.run()
|
|
|
|
|
- else
|
|
|
|
|
- log_info("Using fallback event loop")
|
|
|
|
|
- local timer = luv.new_timer()
|
|
|
|
|
- luv.timer_start(timer, 1000, 1000, function()
|
|
|
|
|
- -- Just keep alive
|
|
|
|
|
- end)
|
|
|
|
|
- luv.run()
|
|
|
|
|
- end
|
|
|
|
|
|
|
+ log_info("Running event loop - waiting for events...")
|
|
|
|
|
+ luv.run()
|
|
|
else
|
|
else
|
|
|
-- No luv - just wait
|
|
-- No luv - just wait
|
|
|
while true do
|
|
while true do
|