mirror of
				https://github.com/Ysurac/openmptcprouter-feeds.git
				synced 2025-03-09 15:40:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			472 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Lua
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			472 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Lua
		
	
	
		
			Executable file
		
	
	
	
	
#!/usr/bin/env lua
 | 
						|
 | 
						|
local json = require "luci.jsonc"
 | 
						|
local fs   = require "nixio.fs"
 | 
						|
 | 
						|
local function readfile(path)
 | 
						|
	local s = fs.readfile(path)
 | 
						|
	return s and (s:gsub("^%s+", ""):gsub("%s+$", ""))
 | 
						|
end
 | 
						|
 | 
						|
local methods = {
 | 
						|
	getInitList = {
 | 
						|
		args = { name = "name" },
 | 
						|
		call = function(args)
 | 
						|
			local sys = require "luci.sys"
 | 
						|
			local _, name, scripts = nil, nil, {}
 | 
						|
			for _, name in ipairs(args.name and { args.name } or sys.init.names()) do
 | 
						|
				local index = sys.init.index(name)
 | 
						|
				if index then
 | 
						|
					scripts[name] = { index = index, enabled = sys.init.enabled(name) }
 | 
						|
				else
 | 
						|
					return { error = "No such init script" }
 | 
						|
				end
 | 
						|
			end
 | 
						|
			return scripts
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	setInitAction = {
 | 
						|
		args = { name = "name", action = "action" },
 | 
						|
		call = function(args)
 | 
						|
			local sys = require "luci.sys"
 | 
						|
			if type(sys.init[args.action]) ~= "function" then
 | 
						|
				return { error = "Invalid action" }
 | 
						|
			end
 | 
						|
			return { result = sys.init[args.action](args.name) }
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getLocaltime = {
 | 
						|
		call = function(args)
 | 
						|
			return { result = os.time() }
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	setLocaltime = {
 | 
						|
		args = { localtime = 0 },
 | 
						|
		call = function(args)
 | 
						|
			local sys = require "luci.sys"
 | 
						|
			local date = os.date("*t", args.localtime)
 | 
						|
			if date then
 | 
						|
				sys.call("date -s '%04d-%02d-%02d %02d:%02d:%02d' >/dev/null" %{ date.year, date.month, date.day, date.hour, date.min, date.sec })
 | 
						|
				sys.call("/etc/init.d/sysfixtime restart >/dev/null")
 | 
						|
			end
 | 
						|
			return { result = args.localtime }
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getTimezones = {
 | 
						|
		call = function(args)
 | 
						|
			local util  = require "luci.util"
 | 
						|
			local zones = require "luci.sys.zoneinfo"
 | 
						|
 | 
						|
			local tz = readfile("/etc/TZ")
 | 
						|
			local res = util.ubus("uci", "get", {
 | 
						|
				config = "system",
 | 
						|
				section = "@system[0]",
 | 
						|
				option = "zonename"
 | 
						|
			})
 | 
						|
 | 
						|
			local result = {}
 | 
						|
			local _, zone
 | 
						|
			for _, zone in ipairs(zones.TZ) do
 | 
						|
				result[zone[1]] = {
 | 
						|
					tzstring = zone[2],
 | 
						|
					active = (res and res.value == zone[1]) and true or nil
 | 
						|
				}
 | 
						|
			end
 | 
						|
			return result
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getLEDs = {
 | 
						|
		call = function()
 | 
						|
			local iter   = fs.dir("/sys/class/leds")
 | 
						|
			local result = { }
 | 
						|
 | 
						|
			if iter then
 | 
						|
				local led
 | 
						|
				for led in iter do
 | 
						|
					local m, s
 | 
						|
 | 
						|
					result[led] = { triggers = {} }
 | 
						|
 | 
						|
					s = readfile("/sys/class/leds/"..led.."/trigger")
 | 
						|
					for s in (s or ""):gmatch("%S+") do
 | 
						|
						m = s:match("^%[(.+)%]$")
 | 
						|
						result[led].triggers[#result[led].triggers+1] = m or s
 | 
						|
						result[led].active_trigger = m or result[led].active_trigger
 | 
						|
					end
 | 
						|
 | 
						|
					s = readfile("/sys/class/leds/"..led.."/brightness")
 | 
						|
					if s then
 | 
						|
						result[led].brightness = tonumber(s)
 | 
						|
					end
 | 
						|
 | 
						|
					s = readfile("/sys/class/leds/"..led.."/max_brightness")
 | 
						|
					if s then
 | 
						|
						result[led].max_brightness = tonumber(s)
 | 
						|
					end
 | 
						|
				end
 | 
						|
			end
 | 
						|
 | 
						|
			return result
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getUSBDevices = {
 | 
						|
		call = function()
 | 
						|
			local fs     = require "nixio.fs"
 | 
						|
			local iter   = fs.glob("/sys/bus/usb/devices/[0-9]*/manufacturer")
 | 
						|
			local result = { }
 | 
						|
 | 
						|
			if iter then
 | 
						|
				result.devices = {}
 | 
						|
 | 
						|
				local p
 | 
						|
				for p in iter do
 | 
						|
					local id = p:match("/([^/]+)/manufacturer$")
 | 
						|
 | 
						|
					result.devices[#result.devices+1] = {
 | 
						|
						id      = id,
 | 
						|
						vid     = readfile("/sys/bus/usb/devices/"..id.."/idVendor"),
 | 
						|
						pid     = readfile("/sys/bus/usb/devices/"..id.."/idProduct"),
 | 
						|
						vendor  = readfile("/sys/bus/usb/devices/"..id.."/manufacturer"),
 | 
						|
						product = readfile("/sys/bus/usb/devices/"..id.."/product"),
 | 
						|
						speed   = tonumber((readfile("/sys/bus/usb/devices/"..id.."/product")))
 | 
						|
					}
 | 
						|
				end
 | 
						|
			end
 | 
						|
 | 
						|
			iter = fs.glob("/sys/bus/usb/devices/*/*-port[0-9]*")
 | 
						|
 | 
						|
			if iter then
 | 
						|
				result.ports = {}
 | 
						|
 | 
						|
				local p
 | 
						|
				for p in iter do
 | 
						|
					local port = p:match("([^/]+)$")
 | 
						|
					local link = fs.readlink(p.."/device")
 | 
						|
 | 
						|
					result.ports[#result.ports+1] = {
 | 
						|
						port   = port,
 | 
						|
						device = link and fs.basename(link)
 | 
						|
					}
 | 
						|
				end
 | 
						|
			end
 | 
						|
 | 
						|
			return result
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getIfaddrs = {
 | 
						|
		call = function()
 | 
						|
			return { result = nixio.getifaddrs() }
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getHostHints = {
 | 
						|
		call = function()
 | 
						|
			local sys = require "luci.sys"
 | 
						|
			return sys.net.host_hints()
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getDUIDHints = {
 | 
						|
		call = function()
 | 
						|
			local fp = io.open('/var/hosts/odhcpd')
 | 
						|
			local result = { }
 | 
						|
			if fp then
 | 
						|
				for line in fp:lines() do
 | 
						|
					local dev, duid, name = string.match(line, '# (%S+)%s+(%S+)%s+%d+%s+(%S+)')
 | 
						|
					if dev and duid and name then
 | 
						|
						result[duid] = {
 | 
						|
							name = (name ~= "-") and name or nil,
 | 
						|
							device = dev
 | 
						|
						}
 | 
						|
					end
 | 
						|
				end
 | 
						|
				fp:close()
 | 
						|
			end
 | 
						|
			return result
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getDHCPLeases = {
 | 
						|
		args = { family = 0 },
 | 
						|
		call = function(args)
 | 
						|
			local s = require "luci.tools.status"
 | 
						|
 | 
						|
			if args.family == 4 then
 | 
						|
				return { dhcp_leases = s.dhcp_leases() }
 | 
						|
			elseif args.family == 6 then
 | 
						|
				return { dhcp6_leases = s.dhcp6_leases() }
 | 
						|
			else
 | 
						|
				return {
 | 
						|
					dhcp_leases = s.dhcp_leases(),
 | 
						|
					dhcp6_leases = s.dhcp6_leases()
 | 
						|
				}
 | 
						|
			end
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getNetworkDevices = {
 | 
						|
		call = function(args)
 | 
						|
			local dir = fs.dir("/sys/class/net")
 | 
						|
			local result = { }
 | 
						|
			if dir then
 | 
						|
				local dev
 | 
						|
				for dev in dir do
 | 
						|
					if not result[dev] then
 | 
						|
						result[dev] = { name = dev }
 | 
						|
					end
 | 
						|
 | 
						|
					if fs.access("/sys/class/net/"..dev.."/master") then
 | 
						|
						local brname = fs.basename(fs.readlink("/sys/class/net/"..dev.."/master"))
 | 
						|
						if not result[brname] then
 | 
						|
							result[brname] = { name = brname }
 | 
						|
						end
 | 
						|
 | 
						|
						if not result[brname].ports then
 | 
						|
							result[brname].ports = { }
 | 
						|
						end
 | 
						|
 | 
						|
						result[brname].ports[#result[brname].ports+1] = dev
 | 
						|
					elseif fs.access("/sys/class/net/"..dev.."/bridge") then
 | 
						|
						if not result[dev].ports then
 | 
						|
							result[dev].ports = { }
 | 
						|
						end
 | 
						|
 | 
						|
						result[dev].id = readfile("/sys/class/net/"..dev.."/bridge/bridge_id")
 | 
						|
						result[dev].stp = (readfile("/sys/class/net/"..dev.."/bridge/stp_state") ~= "0")
 | 
						|
						result[dev].bridge = true
 | 
						|
					end
 | 
						|
 | 
						|
					local opr = readfile("/sys/class/net/"..dev.."/operstate")
 | 
						|
 | 
						|
					result[dev].up = (opr == "up" or opr == "unknown")
 | 
						|
					result[dev].type = tonumber(readfile("/sys/class/net/"..dev.."/type"))
 | 
						|
					result[dev].name = dev
 | 
						|
 | 
						|
					local mtu = tonumber(readfile("/sys/class/net/"..dev.."/mtu"))
 | 
						|
					if mtu and mtu > 0 then
 | 
						|
						result[dev].mtu = mtu
 | 
						|
					end
 | 
						|
 | 
						|
					local qlen = tonumber(readfile("/sys/class/net/"..dev.."/tx_queue_len"))
 | 
						|
					if qlen and qlen > 0 then
 | 
						|
						result[dev].qlen = qlen
 | 
						|
					end
 | 
						|
 | 
						|
					local master = fs.readlink("/sys/class/net/"..dev.."/master")
 | 
						|
					if master then
 | 
						|
						result[dev].master = fs.basename(master)
 | 
						|
					end
 | 
						|
 | 
						|
					local mac = readfile("/sys/class/net/"..dev.."/address")
 | 
						|
					if mac and #mac == 17 then
 | 
						|
						result[dev].mac = mac
 | 
						|
					end
 | 
						|
				end
 | 
						|
			end
 | 
						|
			return result
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getBoardJSON = {
 | 
						|
		call = function(args)
 | 
						|
			local jsc = require "luci.jsonc"
 | 
						|
			return jsc.parse(fs.readfile("/etc/board.json") or "")
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getConntrackHelpers = {
 | 
						|
		call = function()
 | 
						|
			local ok, fd = pcall(io.open, "/usr/share/fw3/helpers.conf", "r")
 | 
						|
			local rv = {}
 | 
						|
 | 
						|
			if ok then
 | 
						|
				local entry
 | 
						|
 | 
						|
				while true do
 | 
						|
					local line = fd:read("*l")
 | 
						|
					if not line then
 | 
						|
						break
 | 
						|
					end
 | 
						|
 | 
						|
					if line:match("^%s*config%s") then
 | 
						|
						if entry then
 | 
						|
							rv[#rv+1] = entry
 | 
						|
						end
 | 
						|
						entry = {}
 | 
						|
					else
 | 
						|
						local opt, val = line:match("^%s*option%s+(%S+)%s+(%S.*)$")
 | 
						|
						if opt and val then
 | 
						|
							opt = opt:gsub("^'(.+)'$", "%1"):gsub('^"(.+)"$', "%1")
 | 
						|
							val = val:gsub("^'(.+)'$", "%1"):gsub('^"(.+)"$', "%1")
 | 
						|
							entry[opt] = val
 | 
						|
						end
 | 
						|
					end
 | 
						|
				end
 | 
						|
 | 
						|
				if entry then
 | 
						|
					rv[#rv+1] = entry
 | 
						|
				end
 | 
						|
 | 
						|
				fd:close()
 | 
						|
			end
 | 
						|
 | 
						|
			return { result = rv }
 | 
						|
		end
 | 
						|
	},
 | 
						|
 | 
						|
	getFeatures = {
 | 
						|
		call = function()
 | 
						|
			local fs = require "nixio.fs"
 | 
						|
			local rv = {}
 | 
						|
			local ok, fd
 | 
						|
 | 
						|
			rv.firewall      = fs.access("/sbin/fw3")
 | 
						|
			rv.opkg          = fs.access("/bin/opkg")
 | 
						|
			rv.offloading    = fs.access("/sys/module/xt_FLOWOFFLOAD/refcnt")
 | 
						|
			rv.br2684ctl     = fs.access("/usr/sbin/br2684ctl")
 | 
						|
			rv.swconfig      = fs.access("/sbin/swconfig")
 | 
						|
			rv.odhcpd        = fs.access("/usr/sbin/odhcpd")
 | 
						|
			rv.zram          = fs.access("/sys/class/zram-control")
 | 
						|
			rv.sysntpd       = fs.readlink("/usr/sbin/ntpd") and true
 | 
						|
 | 
						|
			local wifi_features = { "eap", "11n", "11ac", "11r", "11w", "acs", "sae", "owe", "suiteb192" }
 | 
						|
 | 
						|
			if fs.access("/usr/sbin/hostapd") then
 | 
						|
				rv.hostapd = {}
 | 
						|
 | 
						|
				local _, feature
 | 
						|
				for _, feature in ipairs(wifi_features) do
 | 
						|
					rv.hostapd[feature] =
 | 
						|
						(os.execute(string.format("/usr/sbin/hostapd -v%s >/dev/null 2>/dev/null", feature)) == 0)
 | 
						|
				end
 | 
						|
			end
 | 
						|
 | 
						|
			if fs.access("/usr/sbin/wpa_supplicant") then
 | 
						|
				rv.wpasupplicant = {}
 | 
						|
 | 
						|
				local _, feature
 | 
						|
				for _, feature in ipairs(wifi_features) do
 | 
						|
					rv.wpasupplicant[feature] =
 | 
						|
						(os.execute(string.format("/usr/sbin/wpa_supplicant -v%s >/dev/null 2>/dev/null", feature)) == 0)
 | 
						|
				end
 | 
						|
			end
 | 
						|
 | 
						|
			ok, fd = pcall(io.popen, "dnsmasq --version 2>/dev/null")
 | 
						|
			if ok then
 | 
						|
				rv.dnsmasq = {}
 | 
						|
 | 
						|
				while true do
 | 
						|
					local line = fd:read("*l")
 | 
						|
					if not line then
 | 
						|
						break
 | 
						|
					end
 | 
						|
 | 
						|
					local opts = line:match("^Compile time options: (.+)$")
 | 
						|
					if opts then
 | 
						|
						local opt
 | 
						|
						for opt in opts:gmatch("%S+") do
 | 
						|
							local no = opt:match("^no%-(%S+)$")
 | 
						|
							rv.dnsmasq[string.lower(no or opt)] = not no
 | 
						|
						end
 | 
						|
						break
 | 
						|
					end
 | 
						|
				end
 | 
						|
 | 
						|
				fd:close()
 | 
						|
			end
 | 
						|
 | 
						|
			ok, fd = pcall(io.popen, "ipset --help 2>/dev/null")
 | 
						|
			if ok then
 | 
						|
				rv.ipset = {}
 | 
						|
 | 
						|
				local sets = false
 | 
						|
 | 
						|
				while true do
 | 
						|
					local line = fd:read("*l")
 | 
						|
					if not line then
 | 
						|
						break
 | 
						|
					elseif line:match("^Supported set types:") then
 | 
						|
						sets = true
 | 
						|
					elseif sets then
 | 
						|
						local set, ver = line:match("^%s+(%S+)%s+(%d+)")
 | 
						|
						if set and not rv.ipset[set] then
 | 
						|
							rv.ipset[set] = tonumber(ver)
 | 
						|
						end
 | 
						|
					end
 | 
						|
				end
 | 
						|
 | 
						|
				fd:close()
 | 
						|
			end
 | 
						|
 | 
						|
			return rv
 | 
						|
		end
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
local function parseInput()
 | 
						|
	local parse = json.new()
 | 
						|
	local done, err
 | 
						|
 | 
						|
	while true do
 | 
						|
		local chunk = io.read(4096)
 | 
						|
		if not chunk then
 | 
						|
			break
 | 
						|
		elseif not done and not err then
 | 
						|
			done, err = parse:parse(chunk)
 | 
						|
		end
 | 
						|
	end
 | 
						|
 | 
						|
	if not done then
 | 
						|
		print(json.stringify({ error = err or "Incomplete input" }))
 | 
						|
		os.exit(1)
 | 
						|
	end
 | 
						|
 | 
						|
	return parse:get()
 | 
						|
end
 | 
						|
 | 
						|
local function validateArgs(func, uargs)
 | 
						|
	local method = methods[func]
 | 
						|
	if not method then
 | 
						|
		print(json.stringify({ error = "Method not found" }))
 | 
						|
		os.exit(1)
 | 
						|
	end
 | 
						|
 | 
						|
	if type(uargs) ~= "table" then
 | 
						|
		print(json.stringify({ error = "Invalid arguments" }))
 | 
						|
		os.exit(1)
 | 
						|
	end
 | 
						|
 | 
						|
	uargs.ubus_rpc_session = nil
 | 
						|
 | 
						|
	local k, v
 | 
						|
	local margs = method.args or {}
 | 
						|
	for k, v in pairs(uargs) do
 | 
						|
		if margs[k] == nil or
 | 
						|
		   (v ~= nil and type(v) ~= type(margs[k]))
 | 
						|
		then
 | 
						|
			print(json.stringify({ error = "Invalid arguments" }))
 | 
						|
			os.exit(1)
 | 
						|
		end
 | 
						|
	end
 | 
						|
 | 
						|
	return method
 | 
						|
end
 | 
						|
 | 
						|
if arg[1] == "list" then
 | 
						|
	local _, method, rv = nil, nil, {}
 | 
						|
	for _, method in pairs(methods) do rv[_] = method.args or {} end
 | 
						|
	print((json.stringify(rv):gsub(":%[%]", ":{}")))
 | 
						|
elseif arg[1] == "call" then
 | 
						|
	local args = parseInput()
 | 
						|
	local method = validateArgs(arg[2], args)
 | 
						|
	local result, code = method.call(args)
 | 
						|
	print((json.stringify(result):gsub("^%[%]$", "{}")))
 | 
						|
	os.exit(code or 0)
 | 
						|
end
 |