#!/opt/lnxall_app/bin/lua

-- Created by jiaqiang.ye@lnxall.com
-- Simple WiFi configuration script
-- 2026/01/21

local uci           = require "uci"
local invoker       = require "invoker"
local gfmt          = string.format

local g_wifiwan     = "wifiwan"
local g_etccfg      = nil
local g_ssid        = nil
local g_metric      = 30
local g_passwd      = nil
local g_actions     = {}
local g_lockfd      = nil

local function is_imx6ul()
	local osr = invoker.readfile("/etc/os-release", nil, 8192)
	if not osr then return false end
	if not string.find(osr, "buster", 1, true) then return false end

	local cpuinfo = invoker.readfile("/proc/cpuinfo", nil, 8192)
	if not cpuinfo then return false end
	if not string.find(cpuinfo, "ARMv7", 1, true) then return false end
	if not string.find(cpuinfo, "i.MX6 Ultralite", 1, true) then return false end
	return true
end

local function update_imx6ul_nmcfg(isap)
	local apcfg = [[
[main]
plugins=ifupdown,keyfile

[ifupdown]
managed=true

[keyfile]
unmanaged-devices=interface-name:wlan0;interface-name:wlan1;interface-name:wlan2
]]
	if not isap then
		apcfg = [[
[main]
plugins=ifupdown,keyfile

[ifupdown]
managed=true

[keyfile]
]]
	end

	local cfgpath = "/etc/NetworkManager/NetworkManager.conf"
	local oldcfg = invoker.readfile(cfgpath, nil, 32768)
	if oldcfg == apcfg then return true end

	local pcfg = io.open(cfgpath, "wb")
	if pcfg then
		pcfg:write(apcfg)
		pcfg:close(); pcfg = nil
		io.stdout:write("Restarting NetworkManager service...\n")
		io.stdout:flush()
		invoker.invoke(0, "systemctl", "restart", "NetworkManager")
		return true
	end
	return false
end

local function wificfg_help()
	io.stderr:write("Usage: wificfg dis # 禁用WiFi功能\n")
	io.stderr:write("       wificfg sta|ap --ssid SSID --metric METRIC [--passwd PASSWORD]\n")
	io.stderr:flush()
end

local function goodstr(s, cha)
	if type(s) ~= "string" then return false end
	local len = string.len(s)
	if len == 0 then return false end
	if cha and string.find(s, cha, 1, true) then
		io.stderr:write(gfmt("Error, invalid character found in %s: %s\n", s, cha))
		io.stderr:flush()
		return false
	end
	return len
end

local function enable_wifiwan(enable, metric, which)
	if not which then which = "wifiwan" end
	local netw = uci.cursor(g_etccfg)
	local netcfg = netw:get('network', which)
	if netcfg ~= "interface" then
		local ncfg = gfmt("%s/network", g_etccfg)
		io.stderr:write(gfmt("Warning, network not found in %s: %s\n", ncfg, which))
		io.stderr:flush()

		local fp = io.open(ncfg, "ab")
		if fp then
			fp:write(gfmt([[

config interface 'wifiwan'
	option proto 'dhcp'
	option device 'wlan0'
	option metric '%d'
	option disabled '%s'
]], metric, enable and "0" or "1"))
			fp:close(); fp = nil
		end
		return true
	end

	local commit = nil
	local dis = netw:get('network', which, 'disabled')
	local met = netw:get('network', which, 'metric')
	if enable and tonumber(met) ~= metric then
		commit = true
		netw:set("network", which, "metric", tostring(metric))
	end

	if enable and dis ~= "0" then
		commit = true
		netw:set("network", which, "disabled", "0")
	end

	if dis ~= "1" and not enable then
		commit = true
		netw:set("network", which, "disabled", "1")
	end

	if commit then
		io.stdout:write("Updating network for wifiwan...\n")
		io.stdout:flush()
		netw:commit('network')
	end
	netw:close(); netw = nil
	return true
end

g_actions["sta"] = function ()
	if not g_ssid then
		wificfg_help()
		return false
	end

	local enc, key = "none", ""
	if g_passwd then
		enc, key = "psk2", gfmt("\toption key '%s'\n", g_passwd)
	end

	local fmtstr = [[
config wifi-iface 'radio0'
	option network 'wifiwan'
	option mode 'sta'
	option ssid '%s'
	option encryption '%s'
%s	option disabled '0'

config wifi-iface 'radio1'
	option network 'lan'
	option mode 'ap'
	option ssid 'lnxall_def_SN'
	option encryption 'psk2'
	option key 'lnxall123'
	option disabled '1'
]]

	local wcfg = gfmt(fmtstr, g_ssid, enc, key)
	local whdl = io.open(gfmt("%s/wireless", g_etccfg), "wb")
	if whdl then
		whdl:write(wcfg)
		whdl:close(); whdl = nil
	end
	if is_imx6ul() then update_imx6ul_nmcfg(false) end
	return enable_wifiwan(true, g_metric)
end

g_actions["ap"] = function ()
	if not g_ssid then
		wificfg_help()
		return false
	end

	local enc, key = "none", ""
	if g_passwd then
		enc, key = "psk2", gfmt("\toption key '%s'\n", g_passwd)
	end

	local fmtstr = [[
config wifi-iface 'radio0'
	option network 'wifiwan'
	option mode 'sta'
	option ssid 'DUMMYSSID'
	option encryption 'psk2'
	option key '20260121'
	option disabled '1'

config wifi-iface 'radio1'
	option network 'lan'
	option mode 'ap'
	option ssid '%s'
	option encryption '%s'
%s	option disabled '0'
]]

	local wcfg = gfmt(fmtstr, g_ssid, enc, key)
	local whdl = io.open(gfmt("%s/wireless", g_etccfg), "wb")
	if whdl then
		whdl:write(wcfg)
		whdl:close(); whdl = nil
	end
	if is_imx6ul() then update_imx6ul_nmcfg(true) end
	return enable_wifiwan(false, g_metric)
end

g_actions["dis"] = function ()
	local wcfg = [[
config wifi-iface 'radio0'
	option network 'wifiwan'
	option mode 'sta'
	option ssid 'DUMMYSSID'
	option encryption 'psk2'
	option key '20260121'
	option disabled '1'

config wifi-iface 'radio1'
	option network 'lan'
	option mode 'ap'
	option ssid 'DUMMYSSID'
	option encryption 'psk2'
	option key '20260121'
	option disabled '1'
]]

	local whdl = io.open(gfmt("%s/wireless", g_etccfg), "wb")
	if whdl then
		whdl:write(wcfg)
		whdl:close(); whdl = nil
	end
	return enable_wifiwan(false)
end

local function mainfunc()
	local cfgpath = "/etc/config"
	if invoker.statfile(cfgpath) ~= "directory" then
		io.stderr:write(gfmt("Error, configuration directory not found: %s\n", cfgpath))
		io.stderr:flush()
		return false
	end
	g_etccfg = cfgpath

	local arg1, arg2, action = arg[1], nil, nil
	if not arg1 or #arg1 == 0 then
		action = "WHAT-THE-FUCK"
	else
		action = arg1
	end
	local action = g_actions[action]
	if not action then
		io.stderr:write("Error, invalid command-line arguments.\n")
		io.stderr:flush()
		wificfg_help()
		return false
	end

	local argc = 0
	while true do
		argc = argc + 2
		arg1, arg2 = arg[argc], arg[argc + 1]
		if not (arg1 and arg2) then break end

		if arg1 == "--ssid" and goodstr(arg2, "'") then
			g_ssid = arg2
		elseif arg1 == "--metric" and goodstr(arg2) then
			local metric = tonumber(arg2)
			if type(metric) == "number" and metric >= 0 then
				g_metric = math.floor(metric)
			end
		elseif arg1 == "--passwd" and goodstr(arg2, "'") then
			g_passwd = arg2
		end
	end
	return action()
end

local function netifd_check()
	local path_0 = "/opt/lnxall_app/bin/netifd"
	local path_1 = "/opt/lnxall_app/sbin/netifd"
	if not (invoker.statfile(path_0) or invoker.statfile(path_1)) then
		return false
	end

	local netifd, iflags = false, invoker.OUTPUT + invoker.NOSTDIO
	local okay, output = invoker.invoke(iflags, "systemctl", "status", "NetworkManager")
	if okay ~= 0 then
		netifd = true
	elseif type(output) ~= "string" then
		netifd = true
	elseif string.find(output, "service is masked", 1, true) then
		netifd = true
	else
		okay, output = invoker.invoke(iflags, "pidof", "netifd")
		if okay == 0 and type(output) == "string" and #output >= 1 then
			netifd = true
		end
	end

	if not netifd then return false end
	local args, idx = { [1] = "/lib/dyiot/bin/netifd-wificfg", }, 0
	while true do
		idx = idx + 1
		local argn = arg[idx]
		if not argn then break end
		args[idx + 1] = argn
	end
	io.stdout:write(gfmt("INFO: netifd running, invoking: %s\n", args[1]))
	io.stdout:flush()
	if g_lockfd then
		invoker.closefd(g_lockfd)
		g_lockfd = nil
	end
	invoker.invoke(invoker.NOFORK, args)
	io.stderr:write(gfmt("Error, failed to run scripit: %s\n", args[1]))
	io.stderr:flush()
	os.exit(1)
end

g_lockfd = invoker.waitlock('/tmp/.netuci-lock')
if g_lockfd then invoker.cloexec(g_lockfd, true) end
invoker.setenv("PATH", "/opt/lnxall_app/bin:/opt/lnxall_app/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin")
netifd_check()
os.exit(mainfunc() and 0 or 1)
