|
@@ -1,6 +1,6 @@
|
|
|
--[[
|
|
--[[
|
|
|
client2server-luv - Lua WebSocket Event Forwarder for OpenWrt with luv async
|
|
client2server-luv - Lua WebSocket Event Forwarder for OpenWrt with luv async
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
Features:
|
|
Features:
|
|
|
- WebSocket connection to central server
|
|
- WebSocket connection to central server
|
|
|
- Auto-reconnect on disconnect
|
|
- Auto-reconnect on disconnect
|
|
@@ -8,7 +8,7 @@
|
|
|
- DHCP/WiFi/Interface event tracking
|
|
- DHCP/WiFi/Interface event tracking
|
|
|
- ASYNC monitoring via luv (libuv bindings)
|
|
- ASYNC monitoring via luv (libuv bindings)
|
|
|
- Parallel polling for 50+ devices
|
|
- Parallel polling for 50+ devices
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
Copyright (c) 2026 Luis Rosales - MIT License
|
|
Copyright (c) 2026 Luis Rosales - MIT License
|
|
|
]]
|
|
]]
|
|
|
|
|
|
|
@@ -112,7 +112,7 @@ local buffer = {
|
|
|
function buffer.load()
|
|
function buffer.load()
|
|
|
local f = io.open(cfg.buffer_file, "r")
|
|
local f = io.open(cfg.buffer_file, "r")
|
|
|
if not f then return end
|
|
if not f then return end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
for line in f:lines() do
|
|
for line in f:lines() do
|
|
|
if line and line ~= "" then
|
|
if line and line ~= "" then
|
|
|
table.insert(buffer.events, line)
|
|
table.insert(buffer.events, line)
|
|
@@ -125,7 +125,7 @@ end
|
|
|
function buffer.save()
|
|
function buffer.save()
|
|
|
local f = io.open(cfg.buffer_file, "w")
|
|
local f = io.open(cfg.buffer_file, "w")
|
|
|
if not f then return end
|
|
if not f then return end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
for _, event in ipairs(buffer.events) do
|
|
for _, event in ipairs(buffer.events) do
|
|
|
f:write(event .. "\n")
|
|
f:write(event .. "\n")
|
|
|
end
|
|
end
|
|
@@ -135,22 +135,22 @@ end
|
|
|
|
|
|
|
|
function buffer.add(json_event)
|
|
function buffer.add(json_event)
|
|
|
table.insert(buffer.events, json_event)
|
|
table.insert(buffer.events, json_event)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Limit buffer size
|
|
-- Limit buffer size
|
|
|
while #buffer.events > cfg.max_buffer do
|
|
while #buffer.events > cfg.max_buffer do
|
|
|
table.remove(buffer.events, 1)
|
|
table.remove(buffer.events, 1)
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
buffer.dirty = true
|
|
buffer.dirty = true
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
function buffer.flush(send_fn)
|
|
function buffer.flush(send_fn)
|
|
|
if #buffer.events == 0 then return end
|
|
if #buffer.events == 0 then return end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local to_send = buffer.events
|
|
local to_send = buffer.events
|
|
|
buffer.events = {}
|
|
buffer.events = {}
|
|
|
buffer.dirty = false
|
|
buffer.dirty = false
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
for _, event in ipairs(to_send) do
|
|
for _, event in ipairs(to_send) do
|
|
|
local ok, err = pcall(send_fn, event)
|
|
local ok, err = pcall(send_fn, event)
|
|
|
if not ok or err then
|
|
if not ok or err then
|
|
@@ -158,7 +158,7 @@ function buffer.flush(send_fn)
|
|
|
table.insert(buffer.events, event)
|
|
table.insert(buffer.events, event)
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
buffer.save()
|
|
buffer.save()
|
|
|
end
|
|
end
|
|
|
|
|
|
|
@@ -227,19 +227,19 @@ local function poll_single_device(device_id, device_ip)
|
|
|
'ubus call network.interface.%s status 2>/dev/null',
|
|
'ubus call network.interface.%s status 2>/dev/null',
|
|
|
device_id
|
|
device_id
|
|
|
)
|
|
)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local f = io.popen(cmd)
|
|
local f = io.popen(cmd)
|
|
|
if not f then
|
|
if not f then
|
|
|
return { status = "error", ip = device_ip }
|
|
return { status = "error", ip = device_ip }
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local result = f:read("*all")
|
|
local result = f:read("*all")
|
|
|
f:close()
|
|
f:close()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local parsed = json_decode(result)
|
|
local parsed = json_decode(result)
|
|
|
parsed.status = "online"
|
|
parsed.status = "online"
|
|
|
parsed.ip = device_ip
|
|
parsed.ip = device_ip
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return parsed
|
|
return parsed
|
|
|
end
|
|
end
|
|
|
|
|
|
|
@@ -252,23 +252,23 @@ local function poll_devices_parallel(device_list)
|
|
|
end
|
|
end
|
|
|
return
|
|
return
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
poll_results = {}
|
|
poll_results = {}
|
|
|
poll_count = 0
|
|
poll_count = 0
|
|
|
poll_total = #device_list
|
|
poll_total = #device_list
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log("info", "Starting parallel poll of " .. poll_total .. " devices")
|
|
log("info", "Starting parallel poll of " .. poll_total .. " devices")
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Poll each device in parallel using luv async
|
|
-- Poll each device in parallel using luv async
|
|
|
for _, dev in ipairs(device_list) do
|
|
for _, dev in ipairs(device_list) do
|
|
|
local device_id = dev.id
|
|
local device_id = dev.id
|
|
|
local device_ip = dev.ip
|
|
local device_ip = dev.ip
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Run in async task
|
|
-- Run in async task
|
|
|
luv.new_task(function()
|
|
luv.new_task(function()
|
|
|
local result = poll_single_device(device_id, device_ip)
|
|
local result = poll_single_device(device_id, device_ip)
|
|
|
poll_results[device_id] = result
|
|
poll_results[device_id] = result
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
poll_count = poll_count + 1
|
|
poll_count = poll_count + 1
|
|
|
if poll_count == poll_total then
|
|
if poll_count == poll_total then
|
|
|
log("info", "All " .. poll_total .. " devices polled")
|
|
log("info", "All " .. poll_total .. " devices polled")
|
|
@@ -287,28 +287,28 @@ local function monitor_wifi_events()
|
|
|
log("warn", "luv not available for WiFi monitoring")
|
|
log("warn", "luv not available for WiFi monitoring")
|
|
|
return
|
|
return
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Use luv to watch ubus for wireless events
|
|
-- Use luv to watch ubus for wireless events
|
|
|
-- Note: ubus doesn't support event subscription directly,
|
|
-- Note: ubus doesn't support event subscription directly,
|
|
|
-- so we poll hostapd status periodically
|
|
-- so we poll hostapd status periodically
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local timer = luv.new_timer()
|
|
local timer = luv.new_timer()
|
|
|
local last_clients = {}
|
|
local last_clients = {}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
luv.timer_start(timer, 10000, 10000, function()
|
|
luv.timer_start(timer, 10000, 10000, function()
|
|
|
-- Poll wireless clients
|
|
-- Poll wireless clients
|
|
|
local f = io.popen("ubus call hostapd.wlan0-1 get_clients 2>/dev/null")
|
|
local f = io.popen("ubus call hostapd.wlan0-1 get_clients 2>/dev/null")
|
|
|
if f then
|
|
if f then
|
|
|
local result = f:read("*all")
|
|
local result = f:read("*all")
|
|
|
f:close()
|
|
f:close()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if result and result ~= "" then
|
|
if result and result ~= "" then
|
|
|
-- Parse clients and detect changes
|
|
-- Parse clients and detect changes
|
|
|
local current_clients = {}
|
|
local current_clients = {}
|
|
|
for mac in result:gmatch('"([^"]+)":') do
|
|
for mac in result:gmatch('"([^"]+)":') do
|
|
|
current_clients[mac] = true
|
|
current_clients[mac] = true
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Detect new connections
|
|
-- Detect new connections
|
|
|
for mac, _ in pairs(current_clients) do
|
|
for mac, _ in pairs(current_clients) do
|
|
|
if not last_clients[mac] then
|
|
if not last_clients[mac] then
|
|
@@ -320,7 +320,7 @@ local function monitor_wifi_events()
|
|
|
buffer.add(ev)
|
|
buffer.add(ev)
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Detect disconnections
|
|
-- Detect disconnections
|
|
|
for mac, _ in pairs(last_clients) do
|
|
for mac, _ in pairs(last_clients) do
|
|
|
if not current_clients[mac] then
|
|
if not current_clients[mac] then
|
|
@@ -332,12 +332,12 @@ local function monitor_wifi_events()
|
|
|
buffer.add(ev)
|
|
buffer.add(ev)
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
last_clients = current_clients
|
|
last_clients = current_clients
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
end)
|
|
end)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log("info", "WiFi event monitor started")
|
|
log("info", "WiFi event monitor started")
|
|
|
end
|
|
end
|
|
|
|
|
|
|
@@ -351,20 +351,20 @@ local function monitor_dhcp_leases()
|
|
|
monitor_dhcp_sequential()
|
|
monitor_dhcp_sequential()
|
|
|
return
|
|
return
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local lease_file = "/var/lib/dnsmasq/dnsmasq.leases"
|
|
local lease_file = "/var/lib/dnsmasq/dnsmasq.leases"
|
|
|
local old_leases = {}
|
|
local old_leases = {}
|
|
|
local last_mtime = 0
|
|
local last_mtime = 0
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Use luv fs_event to watch file changes
|
|
-- Use luv fs_event to watch file changes
|
|
|
local fs_event = luv.new_fs_event()
|
|
local fs_event = luv.new_fs_event()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
luv.fs_event_start(fs_event, lease_file, function(err)
|
|
luv.fs_event_start(fs_event, lease_file, function(err)
|
|
|
if err then
|
|
if err then
|
|
|
log("err", "DHCP fs_event error: " .. err)
|
|
log("err", "DHCP fs_event error: " .. err)
|
|
|
return
|
|
return
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- File changed, process leases
|
|
-- File changed, process leases
|
|
|
process_leases(old_leases, function(mac, ip, hostname, action)
|
|
process_leases(old_leases, function(mac, ip, hostname, action)
|
|
|
local ev = build_event("dhcp_lease", {
|
|
local ev = build_event("dhcp_lease", {
|
|
@@ -376,10 +376,10 @@ local function monitor_dhcp_leases()
|
|
|
log("info", "DHCP: " .. action .. " - " .. mac .. " -> " .. ip)
|
|
log("info", "DHCP: " .. action .. " - " .. mac .. " -> " .. ip)
|
|
|
buffer.add(ev)
|
|
buffer.add(ev)
|
|
|
end)
|
|
end)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
old_leases = read_leases()
|
|
old_leases = read_leases()
|
|
|
end)
|
|
end)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log("info", "DHCP lease monitor started")
|
|
log("info", "DHCP lease monitor started")
|
|
|
end
|
|
end
|
|
|
|
|
|
|
@@ -388,7 +388,7 @@ local function read_leases()
|
|
|
local leases = {}
|
|
local leases = {}
|
|
|
local f = io.open("/var/lib/dnsmasq/dnsmasq.leases", "r")
|
|
local f = io.open("/var/lib/dnsmasq/dnsmasq.leases", "r")
|
|
|
if not f then return leases end
|
|
if not f then return leases end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
for line in f:lines() do
|
|
for line in f:lines() do
|
|
|
local ts, mac, ip, name = line:match("(%d+)%s+(%S+)%s+(%S+)%s+(%S+)")
|
|
local ts, mac, ip, name = line:match("(%d+)%s+(%S+)%s+(%S+)%s+(%S+)")
|
|
|
if mac then
|
|
if mac then
|
|
@@ -402,14 +402,14 @@ end
|
|
|
-- Helper: process leases and detect changes
|
|
-- Helper: process leases and detect changes
|
|
|
local function process_leases(old_leases, callback)
|
|
local function process_leases(old_leases, callback)
|
|
|
local leases = read_leases()
|
|
local leases = read_leases()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- New leases
|
|
-- New leases
|
|
|
for mac, info in pairs(leases) do
|
|
for mac, info in pairs(leases) do
|
|
|
if not old_leases[mac] then
|
|
if not old_leases[mac] then
|
|
|
callback(mac, info.ip, info.hostname, "new")
|
|
callback(mac, info.ip, info.hostname, "new")
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Expired leases
|
|
-- Expired leases
|
|
|
for mac, info in pairs(old_leases) do
|
|
for mac, info in pairs(old_leases) do
|
|
|
if not leases[mac] then
|
|
if not leases[mac] then
|
|
@@ -421,7 +421,7 @@ end
|
|
|
-- Fallback: sequential DHCP monitoring
|
|
-- Fallback: sequential DHCP monitoring
|
|
|
local function monitor_dhcp_sequential()
|
|
local function monitor_dhcp_sequential()
|
|
|
local old_leases = {}
|
|
local old_leases = {}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
while true do
|
|
while true do
|
|
|
process_leases(old_leases, function(mac, ip, hostname, action)
|
|
process_leases(old_leases, function(mac, ip, hostname, action)
|
|
|
local ev = build_event("dhcp_lease", {
|
|
local ev = build_event("dhcp_lease", {
|
|
@@ -433,7 +433,7 @@ local function monitor_dhcp_sequential()
|
|
|
log("info", "DHCP: " .. action .. " - " .. mac .. " -> " .. ip)
|
|
log("info", "DHCP: " .. action .. " - " .. mac .. " -> " .. ip)
|
|
|
buffer.add(ev)
|
|
buffer.add(ev)
|
|
|
end)
|
|
end)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
old_leases = read_leases()
|
|
old_leases = read_leases()
|
|
|
os.execute("sleep " .. cfg.dhcp_poll_interval)
|
|
os.execute("sleep " .. cfg.dhcp_poll_interval)
|
|
|
end
|
|
end
|
|
@@ -451,17 +451,17 @@ local function monitor_wan()
|
|
|
monitor_wan_sequential()
|
|
monitor_wan_sequential()
|
|
|
return
|
|
return
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local timer = luv.new_timer()
|
|
local timer = luv.new_timer()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
luv.timer_start(timer, cfg.wan_poll_interval * 1000, cfg.wan_poll_interval * 1000, function()
|
|
luv.timer_start(timer, cfg.wan_poll_interval * 1000, cfg.wan_poll_interval * 1000, function()
|
|
|
local f = io.popen("ubus call network.interface.wan status 2>/dev/null")
|
|
local f = io.popen("ubus call network.interface.wan status 2>/dev/null")
|
|
|
if f then
|
|
if f then
|
|
|
local status = f:read("*all")
|
|
local status = f:read("*all")
|
|
|
f:close()
|
|
f:close()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local is_up = status:match('"up":%s*true') ~= nil
|
|
local is_up = status:match('"up":%s*true') ~= nil
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if is_up ~= last_wan_state then
|
|
if is_up ~= last_wan_state then
|
|
|
local ev = build_event("wan_status", {
|
|
local ev = build_event("wan_status", {
|
|
|
up = is_up
|
|
up = is_up
|
|
@@ -472,7 +472,7 @@ local function monitor_wan()
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
end)
|
|
end)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log("info", "WAN monitor started")
|
|
log("info", "WAN monitor started")
|
|
|
end
|
|
end
|
|
|
|
|
|
|
@@ -483,9 +483,9 @@ local function monitor_wan_sequential()
|
|
|
if f then
|
|
if f then
|
|
|
local status = f:read("*all")
|
|
local status = f:read("*all")
|
|
|
f:close()
|
|
f:close()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local is_up = status:match('"up":%s*true') ~= nil
|
|
local is_up = status:match('"up":%s*true') ~= nil
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if is_up ~= last_wan_state then
|
|
if is_up ~= last_wan_state then
|
|
|
local ev = build_event("wan_status", {
|
|
local ev = build_event("wan_status", {
|
|
|
up = is_up
|
|
up = is_up
|
|
@@ -495,7 +495,7 @@ local function monitor_wan_sequential()
|
|
|
last_wan_state = is_up
|
|
last_wan_state = is_up
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
os.execute("sleep " .. cfg.wan_poll_interval)
|
|
os.execute("sleep " .. cfg.wan_poll_interval)
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
@@ -511,15 +511,15 @@ local function poll_network_status()
|
|
|
if f then f:close() end
|
|
if f then f:close() end
|
|
|
return
|
|
return
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local timer = luv.new_timer()
|
|
local timer = luv.new_timer()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
luv.timer_start(timer, cfg.poll_interval * 1000, cfg.poll_interval * 1000, function()
|
|
luv.timer_start(timer, cfg.poll_interval * 1000, cfg.poll_interval * 1000, function()
|
|
|
local f = io.popen("ubus call network getStatus 2>/dev/null")
|
|
local f = io.popen("ubus call network getStatus 2>/dev/null")
|
|
|
if f then
|
|
if f then
|
|
|
local status = f:read("*all")
|
|
local status = f:read("*all")
|
|
|
f:close()
|
|
f:close()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if status and status ~= "" then
|
|
if status and status ~= "" then
|
|
|
local ev = build_event("network_status", {
|
|
local ev = build_event("network_status", {
|
|
|
status = status
|
|
status = status
|
|
@@ -528,7 +528,7 @@ local function poll_network_status()
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
end)
|
|
end)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log("info", "Network status poller started")
|
|
log("info", "Network status poller started")
|
|
|
end
|
|
end
|
|
|
|
|
|
|
@@ -540,16 +540,16 @@ local function main()
|
|
|
log("info", "client2server-luv starting...")
|
|
log("info", "client2server-luv starting...")
|
|
|
log("info", "Router: " .. cfg.router_id)
|
|
log("info", "Router: " .. cfg.router_id)
|
|
|
log("info", "Server: " .. cfg.url)
|
|
log("info", "Server: " .. cfg.url)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if luv_ok then
|
|
if luv_ok then
|
|
|
log("info", "luv available - using async mode")
|
|
log("info", "luv available - using async mode")
|
|
|
else
|
|
else
|
|
|
log("warn", "luv NOT available - using blocking mode")
|
|
log("warn", "luv NOT available - using blocking mode")
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Load buffered events
|
|
-- Load buffered events
|
|
|
buffer.load()
|
|
buffer.load()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Save PID
|
|
-- Save PID
|
|
|
local pf = io.open("/var/run/client2server-luv.pid", "w")
|
|
local pf = io.open("/var/run/client2server-luv.pid", "w")
|
|
|
if pf then
|
|
if pf then
|
|
@@ -559,10 +559,10 @@ local function main()
|
|
|
pf:write(pid:gsub("%s+", ""))
|
|
pf:write(pid:gsub("%s+", ""))
|
|
|
pf:close()
|
|
pf:close()
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local sock = nil
|
|
local sock = nil
|
|
|
local retries = 0
|
|
local retries = 0
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if luv_ok then
|
|
if luv_ok then
|
|
|
-- Use luv event loop
|
|
-- Use luv event loop
|
|
|
-- luv.run() starts the event loop. The callbacks we registered
|
|
-- luv.run() starts the event loop. The callbacks we registered
|
|
@@ -573,50 +573,46 @@ local function main()
|
|
|
monitor_dhcp_leases()
|
|
monitor_dhcp_leases()
|
|
|
monitor_wan()
|
|
monitor_wan()
|
|
|
poll_network_status()
|
|
poll_network_status()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Keep the event loop running
|
|
-- Keep the event loop running
|
|
|
local idle = luv.new_idle()
|
|
local idle = luv.new_idle()
|
|
|
luv.idle_start(idle, function()
|
|
luv.idle_start(idle, function()
|
|
|
-- Idle work - just keep loop alive
|
|
-- Idle work - just keep loop alive
|
|
|
end)
|
|
end)
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Start monitors first, then run the event loop
|
|
-- Start monitors first, then run the event loop
|
|
|
start_monitors()
|
|
start_monitors()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- luv.run() - start the event loop (no arguments)
|
|
-- luv.run() - start the event loop (no arguments)
|
|
|
- local ok, err = pcall(luv.run)
|
|
|
|
|
|
|
+ local ok, err = pcall(function() luv.run() end)
|
|
|
if not ok then
|
|
if not ok then
|
|
|
log("err", "luv.run error: " .. tostring(err))
|
|
log("err", "luv.run error: " .. tostring(err))
|
|
|
- -- Fallback: sequential mode
|
|
|
|
|
- while true do
|
|
|
|
|
- os.execute("sleep 60")
|
|
|
|
|
- end
|
|
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
- -- WebSocket connection and keepalive
|
|
|
|
|
|
|
+
|
|
|
|
|
+ -- WebSocket connection and keepalive
|
|
|
while true do
|
|
while true do
|
|
|
log("info", "Connecting to server...")
|
|
log("info", "Connecting to server...")
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if ws_client then
|
|
if ws_client then
|
|
|
sock = ws.connect(cfg.url)
|
|
sock = ws.connect(cfg.url)
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if sock then
|
|
if sock then
|
|
|
log("info", "Connected!")
|
|
log("info", "Connected!")
|
|
|
retries = 0
|
|
retries = 0
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Flush buffer
|
|
-- Flush buffer
|
|
|
buffer.flush(function(data)
|
|
buffer.flush(function(data)
|
|
|
return ws.send(sock, data)
|
|
return ws.send(sock, data)
|
|
|
end)
|
|
end)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Keep alive loop
|
|
-- Keep alive loop
|
|
|
local loop_count = 0
|
|
local loop_count = 0
|
|
|
while ws.connected(sock) and loop_count < (cfg.ping_interval / 5) do
|
|
while ws.connected(sock) and loop_count < (cfg.ping_interval / 5) do
|
|
|
luv.sleep(5000)
|
|
luv.sleep(5000)
|
|
|
loop_count = loop_count + 1
|
|
loop_count = loop_count + 1
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Periodic flush
|
|
-- Periodic flush
|
|
|
buffer.flush(function(data)
|
|
buffer.flush(function(data)
|
|
|
return ws.send(sock, data)
|
|
return ws.send(sock, data)
|
|
@@ -626,14 +622,14 @@ local function main()
|
|
|
log("err", "Connection failed")
|
|
log("err", "Connection failed")
|
|
|
retries = retries + 1
|
|
retries = retries + 1
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Cleanup and reconnect
|
|
-- Cleanup and reconnect
|
|
|
ws.close(sock)
|
|
ws.close(sock)
|
|
|
sock = nil
|
|
sock = nil
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Save buffer on disconnect
|
|
-- Save buffer on disconnect
|
|
|
buffer.save()
|
|
buffer.save()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- Delay before reconnect
|
|
-- Delay before reconnect
|
|
|
luv.sleep(cfg.reconnect_delay * 1000)
|
|
luv.sleep(cfg.reconnect_delay * 1000)
|
|
|
end
|
|
end
|
|
@@ -644,27 +640,27 @@ local function main()
|
|
|
-- Sequential monitoring
|
|
-- Sequential monitoring
|
|
|
monitor_dhcp_sequential()
|
|
monitor_dhcp_sequential()
|
|
|
monitor_wan_sequential()
|
|
monitor_wan_sequential()
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
-- WebSocket connection
|
|
-- WebSocket connection
|
|
|
log("info", "Connecting to server...")
|
|
log("info", "Connecting to server...")
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if ws_client then
|
|
if ws_client then
|
|
|
sock = ws.connect(cfg.url)
|
|
sock = ws.connect(cfg.url)
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if sock then
|
|
if sock then
|
|
|
log("info", "Connected!")
|
|
log("info", "Connected!")
|
|
|
retries = 0
|
|
retries = 0
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
buffer.flush(function(data)
|
|
buffer.flush(function(data)
|
|
|
return ws.send(sock, data)
|
|
return ws.send(sock, data)
|
|
|
end)
|
|
end)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
local loop_count = 0
|
|
local loop_count = 0
|
|
|
while ws.connected(sock) and loop_count < (cfg.ping_interval / 5) do
|
|
while ws.connected(sock) and loop_count < (cfg.ping_interval / 5) do
|
|
|
os.execute("sleep 5")
|
|
os.execute("sleep 5")
|
|
|
loop_count = loop_count + 1
|
|
loop_count = loop_count + 1
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
buffer.flush(function(data)
|
|
buffer.flush(function(data)
|
|
|
return ws.send(sock, data)
|
|
return ws.send(sock, data)
|
|
|
end)
|
|
end)
|
|
@@ -673,7 +669,7 @@ local function main()
|
|
|
log("err", "Connection failed")
|
|
log("err", "Connection failed")
|
|
|
retries = retries + 1
|
|
retries = retries + 1
|
|
|
end
|
|
end
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
ws.close(sock)
|
|
ws.close(sock)
|
|
|
buffer.save()
|
|
buffer.save()
|
|
|
os.execute("sleep " .. cfg.reconnect_delay)
|
|
os.execute("sleep " .. cfg.reconnect_delay)
|