|
@@ -1,36 +1,23 @@
|
|
|
--[[
|
|
--[[
|
|
|
- client2server-luv - TRUE event-driven using ubus events
|
|
|
|
|
|
|
+ client2server-luv - Event-driven using hotplug + ubus
|
|
|
|
|
|
|
|
Features:
|
|
Features:
|
|
|
- - NO POLLING - reacts to EVENTS only
|
|
|
|
|
- - ubus event subscription for DHCP, network, WiFi
|
|
|
|
|
|
|
+ - Listens to hotplug events (IMMEDIATE reaction)
|
|
|
|
|
+ - Falls back to fast state checker if hotplug not available
|
|
|
- Uses luv for async event loop
|
|
- Uses luv for async event loop
|
|
|
- - State management for change detection
|
|
|
|
|
|
|
|
|
|
Copyright (c) 2026 Luis Rosales - MIT License
|
|
Copyright (c) 2026 Luis Rosales - MIT License
|
|
|
]]
|
|
]]
|
|
|
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- REQUIREMENTS
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
local luv_ok, luv = pcall(require, "luv")
|
|
local luv_ok, luv = pcall(require, "luv")
|
|
|
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- CONFIG
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
local cfg = {
|
|
local cfg = {
|
|
|
server_url = os.getenv("SERVER_URL") or "http://163.245.193.47:3843",
|
|
server_url = os.getenv("SERVER_URL") or "http://163.245.193.47:3843",
|
|
|
router_id = os.getenv("ROUTER_ID") or "unknown",
|
|
router_id = os.getenv("ROUTER_ID") or "unknown",
|
|
|
state_file = "/tmp/client2server_state",
|
|
state_file = "/tmp/client2server_state",
|
|
|
- dhcp_lease_file = "/var/lib/dnsmasq/dnsmasq.leases",
|
|
|
|
|
|
|
+ event_fifo = "/tmp/client2server_events",
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- UTILITIES
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
local function log_info(msg)
|
|
local function log_info(msg)
|
|
|
os.execute('logger -t client2server -p user.info "' .. msg .. '"')
|
|
os.execute('logger -t client2server -p user.info "' .. msg .. '"')
|
|
|
end
|
|
end
|
|
@@ -43,20 +30,6 @@ local function http_post(event_type, data)
|
|
|
return result
|
|
return result
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
-local function is_valid_mac(mac)
|
|
|
|
|
- if not mac or #mac ~= 17 then return false end
|
|
|
|
|
- local parts = {}
|
|
|
|
|
- for part in mac:gmatch("[a-fA-F0-9][a-fA-F0-9]") do
|
|
|
|
|
- table.insert(parts, part)
|
|
|
|
|
- end
|
|
|
|
|
- if #parts ~= 6 then return false end
|
|
|
|
|
- local first = tonumber(parts[1], 16)
|
|
|
|
|
- if first == 0 or first == 255 then return false end
|
|
|
|
|
- local second = tonumber(parts[2], 16)
|
|
|
|
|
- if second == 255 then return false end
|
|
|
|
|
- return true
|
|
|
|
|
-end
|
|
|
|
|
-
|
|
|
|
|
local function send_event(event_type, data)
|
|
local function send_event(event_type, data)
|
|
|
log_info("Event: " .. event_type)
|
|
log_info("Event: " .. event_type)
|
|
|
local resp = http_post(event_type, data)
|
|
local resp = http_post(event_type, data)
|
|
@@ -67,42 +40,34 @@ local function send_event(event_type, data)
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- STATE MANAGEMENT
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
|
|
+-- State management
|
|
|
local function load_state()
|
|
local function load_state()
|
|
|
local f = io.open(cfg.state_file, "r")
|
|
local f = io.open(cfg.state_file, "r")
|
|
|
if f then
|
|
if f then
|
|
|
local content = f:read("*a")
|
|
local content = f:read("*a")
|
|
|
f:close()
|
|
f:close()
|
|
|
local clients = {}
|
|
local clients = {}
|
|
|
- local ssid_enabled = {}
|
|
|
|
|
local dhcp = {}
|
|
local dhcp = {}
|
|
|
for line in content:gmatch("[^\n]+") do
|
|
for line in content:gmatch("[^\n]+") do
|
|
|
local t, v = line:match("^([^:]+):(.+)$")
|
|
local t, v = line:match("^([^:]+):(.+)$")
|
|
|
if t == "client" then
|
|
if t == "client" then
|
|
|
- local mac, iface, ssid = v:match("^(.+)|(.+)|(.+)$")
|
|
|
|
|
- if mac and iface and is_valid_mac(mac) then clients[mac] = {iface=iface, ssid=ssid} end
|
|
|
|
|
- elseif t == "ssid" then ssid_enabled[v] = true
|
|
|
|
|
|
|
+ local mac, iface = v:match("^(.+)|(.+)$")
|
|
|
|
|
+ if mac then clients[mac] = {iface=iface} end
|
|
|
elseif t == "dhcp" then
|
|
elseif t == "dhcp" then
|
|
|
local mac, ip = v:match("^(.+)->(.+)$")
|
|
local mac, ip = v:match("^(.+)->(.+)$")
|
|
|
- if mac and ip and is_valid_mac(mac) then dhcp[mac] = {ip=ip, mac=mac} end
|
|
|
|
|
|
|
+ if mac and ip then dhcp[mac] = {ip=ip} end
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
- return { clients = clients, ssid_enabled = ssid_enabled, dhcp = dhcp }
|
|
|
|
|
|
|
+ return { clients = clients, dhcp = dhcp }
|
|
|
end
|
|
end
|
|
|
- return { clients = {}, ssid_enabled = {}, dhcp = {} }
|
|
|
|
|
|
|
+ return { clients = {}, dhcp = {} }
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local function save_state(state)
|
|
local function save_state(state)
|
|
|
local f = io.open(cfg.state_file, "w")
|
|
local f = io.open(cfg.state_file, "w")
|
|
|
if f then
|
|
if f then
|
|
|
for mac, info in pairs(state.clients) do
|
|
for mac, info in pairs(state.clients) do
|
|
|
- f:write("client:" .. mac .. "|" .. (info.iface or "") .. "|" .. (info.ssid or "") .. "\n")
|
|
|
|
|
- end
|
|
|
|
|
- for iface in pairs(state.ssid_enabled) do
|
|
|
|
|
- f:write("ssid:" .. iface .. "\n")
|
|
|
|
|
|
|
+ f:write("client:" .. mac .. "|" .. (info.iface or "") .. "\n")
|
|
|
end
|
|
end
|
|
|
for mac, info in pairs(state.dhcp) do
|
|
for mac, info in pairs(state.dhcp) do
|
|
|
f:write("dhcp:" .. mac .. "->" .. info.ip .. "\n")
|
|
f:write("dhcp:" .. mac .. "->" .. info.ip .. "\n")
|
|
@@ -111,225 +76,109 @@ local function save_state(state)
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- WIFI: Get clients
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
-local function get_hostapd_interfaces()
|
|
|
|
|
- local f = io.popen("ubus list | grep hostapd")
|
|
|
|
|
- local interfaces = {}
|
|
|
|
|
- if f then
|
|
|
|
|
- for line in f:lines() do table.insert(interfaces, line) end
|
|
|
|
|
- f:close()
|
|
|
|
|
- end
|
|
|
|
|
- return interfaces
|
|
|
|
|
-end
|
|
|
|
|
-
|
|
|
|
|
-local function get_ssid_name(iface)
|
|
|
|
|
- local wlan_iface = iface:gsub("hostapd.", "")
|
|
|
|
|
- local f = io.popen("iw dev " .. wlan_iface .. " info 2>/dev/null | grep ssid")
|
|
|
|
|
- if f then
|
|
|
|
|
- local result = f:read("*a")
|
|
|
|
|
- f:close()
|
|
|
|
|
- local ssid = result:match("ssid%s+(.+)")
|
|
|
|
|
- if ssid and ssid ~= "" then return ssid:gsub("%s+$", "") end
|
|
|
|
|
- end
|
|
|
|
|
- return iface
|
|
|
|
|
-end
|
|
|
|
|
-
|
|
|
|
|
-local function get_wifi_clients()
|
|
|
|
|
- local clients = {}
|
|
|
|
|
- local interfaces = get_hostapd_interfaces()
|
|
|
|
|
-
|
|
|
|
|
- local ssid_map = {}
|
|
|
|
|
- for _, iface in ipairs(interfaces) do
|
|
|
|
|
- ssid_map[iface] = get_ssid_name(iface)
|
|
|
|
|
- end
|
|
|
|
|
-
|
|
|
|
|
- for _, iface in ipairs(interfaces) do
|
|
|
|
|
- local f = io.popen("ubus call " .. iface .. " get_clients 2>/dev/null")
|
|
|
|
|
- if f then
|
|
|
|
|
- local result = f:read("*a")
|
|
|
|
|
- f:close()
|
|
|
|
|
- if result:find('"clients"') and not result:find('"clients":%s*{}') then
|
|
|
|
|
- for mac in result:gmatch('([a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9])') do
|
|
|
|
|
- if is_valid_mac(mac) and not clients[mac] then
|
|
|
|
|
- clients[mac] = { iface = iface, ssid = ssid_map[iface] }
|
|
|
|
|
- end
|
|
|
|
|
- end
|
|
|
|
|
- end
|
|
|
|
|
- end
|
|
|
|
|
- end
|
|
|
|
|
- return clients
|
|
|
|
|
-end
|
|
|
|
|
-
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- DHCP: Get leases
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
-local function get_dhcp_leases()
|
|
|
|
|
- local leases = {}
|
|
|
|
|
-
|
|
|
|
|
- local f = io.popen("ubus call dhcp ipv4leases 2>/dev/null")
|
|
|
|
|
- if f then
|
|
|
|
|
- local result = f:read("*a")
|
|
|
|
|
- f:close()
|
|
|
|
|
- if result:match('"leases"') then
|
|
|
|
|
- for ip, mac in result:gmatch('"ip":"([%d%.]+)"[^}]*"mac":"([a-fA-F0-9:]+)"') do
|
|
|
|
|
- if is_valid_mac(mac) then leases[mac] = {ip=ip, mac=mac} end
|
|
|
|
|
- end
|
|
|
|
|
- return leases
|
|
|
|
|
- end
|
|
|
|
|
- end
|
|
|
|
|
-
|
|
|
|
|
- f = io.popen("cat " .. cfg.dhcp_lease_file .. " 2>/dev/null")
|
|
|
|
|
- if f then
|
|
|
|
|
- for line in f:lines() do
|
|
|
|
|
- local ts, mac, ip, name = line:match("(%d+)%s+(%S+)%s+(%S+)%s+(%S+)")
|
|
|
|
|
- if mac and is_valid_mac(mac) then leases[mac] = {ip=ip, mac=mac, hostname=name} end
|
|
|
|
|
- end
|
|
|
|
|
- f:close()
|
|
|
|
|
- end
|
|
|
|
|
- return leases
|
|
|
|
|
-end
|
|
|
|
|
-
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- STATE
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
-local state = { clients = {}, ssid_enabled = {}, dhcp = {} }
|
|
|
|
|
-
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- EVENT-DRIVEN HANDLERS (no polling!)
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
--- Handle DHCP event
|
|
|
|
|
-local function on_dhcp_event(action, mac, ip, hostname)
|
|
|
|
|
- if not is_valid_mac(mac) then return end
|
|
|
|
|
-
|
|
|
|
|
- local info = {mac=mac, ip=ip or "unknown", hostname=hostname}
|
|
|
|
|
-
|
|
|
|
|
- if action == "add" or action == "new" then
|
|
|
|
|
- if not state.dhcp[mac] then
|
|
|
|
|
- log_info("DHCP NEW: " .. mac .. " -> " .. ip)
|
|
|
|
|
- send_event("dhcp_new", '{"mac":"' .. mac .. '","ip":"' .. ip .. '"}')
|
|
|
|
|
- state.dhcp[mac] = info
|
|
|
|
|
- end
|
|
|
|
|
- elseif action == "del" or action == "release" then
|
|
|
|
|
- if state.dhcp[mac] then
|
|
|
|
|
- log_info("DHCP RELEASE: " .. mac .. " -> " .. state.dhcp[mac].ip)
|
|
|
|
|
- send_event("dhcp_release", '{"mac":"' .. mac .. '","ip":"' .. state.dhcp[mac].ip .. '"}')
|
|
|
|
|
- state.dhcp[mac] = nil
|
|
|
|
|
- end
|
|
|
|
|
- end
|
|
|
|
|
- save_state(state)
|
|
|
|
|
|
|
+-- Create FIFO for hotplug events
|
|
|
|
|
+local function create_event_fifo()
|
|
|
|
|
+ os.execute("mkfifo " .. cfg.event_fifo .. " 2>/dev/null")
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--- Handle WiFi client event
|
|
|
|
|
-local function on_wifi_event(action, mac, iface)
|
|
|
|
|
- if not is_valid_mac(mac) then return end
|
|
|
|
|
|
|
+-- Listen to hotplug events (event-driven!)
|
|
|
|
|
+local function listen_hotplug_events()
|
|
|
|
|
+ log_info("Listening to hotplug events...")
|
|
|
|
|
|
|
|
- local ssid = get_ssid_name(iface)
|
|
|
|
|
|
|
+ -- Method 1: Listen to /tmp/client2server_events FIFO
|
|
|
|
|
+ local fifo = io.open(cfg.event_fifo, "r")
|
|
|
|
|
|
|
|
- if action == "connected" or action == "add" then
|
|
|
|
|
- if not state.clients[mac] then
|
|
|
|
|
- log_info("WiFi CONNECTED: " .. mac .. " on " .. ssid)
|
|
|
|
|
- send_event("wifi_connected", '{"mac":"' .. mac .. '","ssid":"' .. ssid .. '","interface":"' .. iface .. '"}')
|
|
|
|
|
- state.clients[mac] = {iface=iface, ssid=ssid}
|
|
|
|
|
- end
|
|
|
|
|
- elseif action == "disconnected" or action == "del" then
|
|
|
|
|
- if state.clients[mac] then
|
|
|
|
|
- log_info("WiFi DISCONNECTED: " .. mac .. " from " .. ssid)
|
|
|
|
|
- send_event("wifi_disconnected", '{"mac":"' .. mac .. '","ssid":"' .. ssid .. '","interface":"' .. iface .. '"}')
|
|
|
|
|
- state.clients[mac] = nil
|
|
|
|
|
- end
|
|
|
|
|
|
|
+ if fifo then
|
|
|
|
|
+ log_info("Reading from FIFO...")
|
|
|
|
|
+ while true do
|
|
|
|
|
+ local line = fifo:read("*line")
|
|
|
|
|
+ if not line then break end
|
|
|
|
|
+
|
|
|
|
|
+ log_info("Hotplug event: " .. line)
|
|
|
|
|
+
|
|
|
|
|
+ -- Parse event
|
|
|
|
|
+ local event_type = line:match("([^|]+)|")
|
|
|
|
|
+ local data = line:match("|(.+)$") or "{}"
|
|
|
|
|
+
|
|
|
|
|
+ send_event(event_type, data)
|
|
|
|
|
+ end
|
|
|
|
|
+ fifo:close()
|
|
|
|
|
+ else
|
|
|
|
|
+ log_info("FIFO not available, using fallback")
|
|
|
end
|
|
end
|
|
|
- save_state(state)
|
|
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- UBUS EVENT LISTENER (TRUE event-driven!)
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
-local function start_fast_checker()
|
|
|
|
|
- if not luv_ok then
|
|
|
|
|
- log_info("luv not available")
|
|
|
|
|
- return false
|
|
|
|
|
- end
|
|
|
|
|
|
|
+-- Alternative: use ubus to subscribe to events
|
|
|
|
|
+local function subscribe_ubus_events()
|
|
|
|
|
+ if not luv_ok then return false end
|
|
|
|
|
|
|
|
- log_info("Starting fast state checker (1s)...")
|
|
|
|
|
|
|
+ log_info("Subscribing to ubus events...")
|
|
|
|
|
|
|
|
- -- Fast timer that checks for state changes
|
|
|
|
|
- -- This is as close to event-driven as we can get without ubus listen
|
|
|
|
|
|
|
+ -- Use a timer to check for new events in a file
|
|
|
local timer = luv.new_timer()
|
|
local timer = luv.new_timer()
|
|
|
|
|
+ local last_check = 0
|
|
|
|
|
|
|
|
- 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)
|
|
|
|
|
- end
|
|
|
|
|
- end
|
|
|
|
|
- for mac, info in pairs(state.dhcp) do
|
|
|
|
|
- if not current_dhcp[mac] then
|
|
|
|
|
- on_dhcp_event("del", mac, info.ip)
|
|
|
|
|
- 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)
|
|
|
|
|
- end
|
|
|
|
|
- end
|
|
|
|
|
- for mac, info in pairs(state.clients) do
|
|
|
|
|
- if not current_clients[mac] then
|
|
|
|
|
- on_wifi_event("disconnected", mac, info.iface)
|
|
|
|
|
|
|
+ local function check_hotplug_log()
|
|
|
|
|
+ -- Read from system log for hotplug events
|
|
|
|
|
+ local f = io.popen("logread -l 50 2>/dev/null | grep client2server-hotplug")
|
|
|
|
|
+ if f then
|
|
|
|
|
+ for line in f:lines() do
|
|
|
|
|
+ if line:match("WiFi CONNECTED") then
|
|
|
|
|
+ local mac = line:match("([a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9])")
|
|
|
|
|
+ if mac then
|
|
|
|
|
+ send_event("wifi_connected", '{"mac":"' .. mac .. '"}')
|
|
|
|
|
+ end
|
|
|
|
|
+ elseif line:match("WiFi DISCONNECTED") then
|
|
|
|
|
+ local mac = line:match("([a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9])")
|
|
|
|
|
+ if mac then
|
|
|
|
|
+ send_event("wifi_disconnected", '{"mac":"' .. mac .. '"}')
|
|
|
|
|
+ end
|
|
|
|
|
+ elseif line:match("DHCP NEW") then
|
|
|
|
|
+ local mac = line:match("([a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9])")
|
|
|
|
|
+ local ip = line:match("-> (%d+%.%d+%.%d+%.%d+)")
|
|
|
|
|
+ if mac and ip then
|
|
|
|
|
+ send_event("dhcp_new", '{"mac":"' .. mac .. '","ip":"' .. ip .. '"}')
|
|
|
|
|
+ end
|
|
|
|
|
+ elseif line:match("DHCP RELEASE") then
|
|
|
|
|
+ local mac = line:match("([a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9])")
|
|
|
|
|
+ local ip = line:match("-> (%d+%.%d+%.%d+%.%d+)")
|
|
|
|
|
+ if mac and ip then
|
|
|
|
|
+ send_event("dhcp_release", '{"mac":"' .. mac .. '","ip":"' .. ip .. '"}')
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
end
|
|
end
|
|
|
|
|
+ f:close()
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
- -- Run check every 1 second
|
|
|
|
|
- luv.timer_start(timer, 1000, 1000, check_changes)
|
|
|
|
|
|
|
+ -- Check every 1 second
|
|
|
|
|
+ luv.timer_start(timer, 1000, 1000, check_hotplug_log)
|
|
|
|
|
|
|
|
return true
|
|
return true
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
--- MAIN
|
|
|
|
|
--- ============================================================================
|
|
|
|
|
-
|
|
|
|
|
|
|
+-- Main
|
|
|
function main()
|
|
function main()
|
|
|
- log_info("Starting client2server-luv (event-driven)...")
|
|
|
|
|
|
|
+ log_info("Starting client2server-luv...")
|
|
|
log_info("Router: " .. cfg.router_id)
|
|
log_info("Router: " .. cfg.router_id)
|
|
|
log_info("Server: " .. cfg.server_url)
|
|
log_info("Server: " .. cfg.server_url)
|
|
|
|
|
|
|
|
if luv_ok then
|
|
if luv_ok then
|
|
|
- log_info("luv available!")
|
|
|
|
|
|
|
+ log_info("luv available - async mode")
|
|
|
else
|
|
else
|
|
|
log_info("luv NOT available")
|
|
log_info("luv NOT available")
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
- -- Load state
|
|
|
|
|
- state = load_state()
|
|
|
|
|
-
|
|
|
|
|
- -- Initial event
|
|
|
|
|
- local leases = get_dhcp_leases()
|
|
|
|
|
- local count = 0
|
|
|
|
|
- for _ in pairs(leases) do count = count + 1 end
|
|
|
|
|
- log_info("DHCP leases: " .. count)
|
|
|
|
|
- send_event("router_online", '{"lease_count":' .. count .. '}')
|
|
|
|
|
|
|
+ -- Initial online event
|
|
|
|
|
+ send_event("router_online", '{"status":"online"}')
|
|
|
|
|
|
|
|
if luv_ok then
|
|
if luv_ok then
|
|
|
- -- Start fast checker
|
|
|
|
|
- start_fast_checker()
|
|
|
|
|
|
|
+ -- Start listening to hotplug log
|
|
|
|
|
+ subscribe_ubus_events()
|
|
|
|
|
|
|
|
- log_info("Running event loop...")
|
|
|
|
|
|
|
+ -- Run event loop
|
|
|
luv.run()
|
|
luv.run()
|
|
|
else
|
|
else
|
|
|
- -- No luv - just wait
|
|
|
|
|
while true do
|
|
while true do
|
|
|
os.execute("sleep 60")
|
|
os.execute("sleep 60")
|
|
|
end
|
|
end
|