--[[ @object-name: system @object-desc: 系统操作 --]] local utils = require "oui.utils" local rpc = require "oui.rpc" local fs = require "oui.fs" local sys = require "oui.system" local ubus = require "oui.ubus" local uci = require "uci" local cjson = require "cjson" local lfactory = require "lfactory" --local iwinfo = require "oui.iwinfo" local ok, gwifi = pcall(require, "gl.wifi") if not ok then gwifi = {} function gwifi.disable_guest_during_scan_wifi() return false end end local M = {} local function get_tzoffset() local f = io.popen("date +'%z'") local tzoffset = f:read() f:close() return tzoffset end --[[ @method-type: call @method-name: get_status @method-desc: 获取系统网络,服务,资源使用等状态 @out object system 包含系统的基本状态信息 @out number system.mode 系统的运行模式(0:路由模式,1:wds桥模式,2:relay桥模式,3:mesh模式,4:ap模式,6:passthrough模式)[默认是0,不是以上的运行模式返回5] @out number system.timestamp 系统的时间戳 @out string system.tzoffset 路由器当前时区偏移量 @out number system.uptime 系统的运行时间(秒) @out array system.load_average 系统负载情况,固定返回 包含三个小数的数组,用小数表示CPU的使用率,依次表示1/5/15分钟的平均负载值 @out number system.memory_total 系统的总内存,单位是byte @out number system.memory_free 系统的空闲内存,单位是byte @out number system.memory_buff_cache 系统的buffered+cached, 单位为byte @out number system.flash_total 系统的总磁盘空间,单位是byte @out number system.flash_free 系统未使用的磁盘空间,单位是byte @out number system.flash_app 系统APP使用的磁盘空间,单位是byte @out string system.lan_ip LAN IP @out string system.lan_netmask LAN 子网掩码 @out string system.guest_ip GUEST IP @out string system.guest_netmask GUEST 子网掩码 @out bool system.ipv6_enabled 系统是否使能IPV6 @out bool system.ddns_enabled 系统是否使能ddns @out bool system.netnat_enabled 系统是否使能hwnat(硬件加速) @out object ?system.mcu mcu. @out number ?system.mcu.temperature 电量计温度. @out number ?system.mcu.charge_percent 电量百分比. @out number ?system.mcu.charging_status 充电状态.[0 是不在充电, 1是正在充电] @out number ?system.mcu.charge_cnt 电池充电循环次数. @out number ?system.cpu.temperature CPU核心温度. @out array network 包含系统的网络通断情况 @out string network.interface 网络接口名称 @out bool network.up 网络是否启动成功 @out bool network.online 是否可访问外网 @out number network.balance 预配置的网络使用率,用小数表示,1表示100%使用,0表示未使用 @out array service 包含系统的服务使用情况,主要包括VPN,AdguardHome及其他安全服务的启用情况[没有该服务会返回一个空的name] @out string service.name 服务名 @out number service.status 服务开启状态【0 未开启 | 1 连接成功 | 2 开启但连接未成功】 @out number ?service.group_id ovpnclient 或 wgclient 的 group id @out number ?service.client_id ovpnclient的client_id @out number ?service.peer_id wgclient 的 peer_id @out array wifi 包含系统的wifi基本状态 @out string wifi.ssid WIFI的SSID @out string wifi.name 接口名称,标识wifi的唯一性 @out string wifi.band WiFi 频段("2G" 或者 "5G") @out number wifi.channel WIFI的信道(0 表示自动) @out bool wifi.guest 是否属于guest网络 @out bool wifi.up 是否正常启用 @out string ?wifi.passwd WiFi 密码 @out array client 客户端在线总数状态 @out number client.cable_total 有线客户端的在线总数. @out number client.wireless_total 无线客户端的在线总数. @out number ?err_code Error code,-1:获取信息失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","get_status"]} @out-example: {"id":1,"jsonrpc":"2.0","result":{"network":[{"online":false,"up":false,"interface":"wan"},{"online":false,"up":false,"interface":"wwan"},{"online":false,"up":false,"interface":"tethering"},{"online":false,"up":false,"interface":"modem_1_1_2"}],"wifi":[{"guest":false,"ssid":"GL-XE300-49a","up":true,"channel":0,"band":"2G","name":"default_radio0","passwd":"goodlife"},{"guest":true,"ssid":"GL-XE300-49a-Guest","up":false,"channel":0,"band":"2G","name":"guest2g","passwd":"goodlife"}],"service":[{"name":"wgclient","status":0},{"name":"wgserver","status":0},{"name":"ovpnclient","status":0},{"name":"ovpnserver","status":0},{"name":"tor","status":0}],"client":[{"wireless_total":0,"cable_total":1}],"system":{"lan_ip":"192.168.8.1","lan_netmask":"255.255.255.0","guest_ip":"192.168.9.1","guest_netmask":"255.255.255.0","flash_total":106278912,"memory_total":126943232,"memory_free":78471168,"memory_buff_cache":69005248,"ipv6_enabled":false,"ddns_enabled":false,"netnat_enabled":true,"uptime":111,"load_average":[2.01,0.89,0.33],"flash_free":105918464,"flash_app":1025,"mode":0,"mcu":{"charge_cnt":49,"temperature":27.6,"charge_percent":100,"charging_status":1},"cpu":{"temperature":82},"timestamp":1613402877}}} --]] function M.get_status() local c = uci.cursor() local network = {} local service = {} local wifi = {} local client = {} local mode_str = c:get("glconfig", "general", "mode") or "router" local modes = { router = 0, wds = 1, relay = 2, mesh = 3, ap = 4, passthrough = 6 } local mode = modes[mode_str] or 5 local system_status = ubus.call("system", "info") local memory = system_status.memory local _, flash_free, flash_app = fs.statvfs("/") local flash_total = 0 if fs.access("/proc/gl-hw-info/flash_size") then flash_total = string.gsub(lfactory.get_falsh_size(), "[^%d]", "") * 1024 * 1024 end local load1, load5, load15 = utils.readfile("/proc/loadavg"):match("(%d+%.%d+) (%d+%.%d+) (%d+%.%d+)") local system = { mode = mode, timestamp = system_status.localtime, tzoffset = get_tzoffset(), uptime = utils.readfile("/proc/uptime", 'n'), load_average = {tonumber(load1), tonumber(load5), tonumber(load15)}, flash_total = flash_total, flash_app = flash_app, flash_free = flash_free, memory_total = memory.total, memory_free = memory.free, memory_buff_cache = memory.cached and memory.buffered + memory.cached or memory.buffered, guest_ip = c:get("network", "guest", "ipaddr") or "", guest_netmask = c:get("network", "guest", "netmask") or "", ipv6_enabled = c:get("glipv6", "globals", "enabled") == "1", ddns_enabled = c:get("ddns", "glddns", "enabled") == "1", hwnat_enabled = fs.access("/etc/config/mtkhnat") and c:get("mtkhnat", "global", "enable") == "1" or c:get("firewall", "@defaults[0]", "flow_offloading_hw") == "1" } if mode == modes.router then system.lan_ip = c:get("network", "lan", "ipaddr") or "" system.lan_netmask = c:get("network", "lan", "netmask") or "" end -- network c:foreach("mwan3", "interface", function(s) local name = s[".name"] if mode == modes.ap and name == "wan" then name = "lan" elseif mode == modes.wds and name == "wwan" then name = "lan" end local up = (ubus.call("network.interface."..name, "status") or {}).up or false local mwan3track_file = "/var/run/mwan3track/" .. name .. "/STATUS" local online = up local mwan3_enable = c:get("mwan3", name, "enabled") == "1" if mwan3_enable then online = up and fs.access(mwan3track_file) and utils.readfile(mwan3track_file, "*l") == "online" or false end if name == "lan" and up == true then online = true end network[#network + 1] = { interface = s[".name"], online = online, up = up } end) local modem_bus = lfactory.get_build_modem_port() or "" if type(modem_bus) == "string" and modem_bus:gsub("%s+", "") ~= "" then local name = "modem_" .. string.gsub(modem_bus, "%D", "_") local mwan3track_file = "/var/run/mwan3track/" .. name .. "/STATUS" local up = (ubus.call("network.interface."..name, "status") or {}).up or false local online = up and fs.access(mwan3track_file) and utils.readfile(mwan3track_file, "*l") == "online" or false network[#network + 1] = { interface = name, online = online, up = up } end --service local vpn_exist = fs.access("/usr/lib/oui-httpd/rpc/ovpn-client.so") and fs.access("/usr/lib/oui-httpd/rpc/ovpn-server.so") and fs.access("/usr/lib/oui-httpd/rpc/wg-client.so") and fs.access("/usr/lib/oui-httpd/rpc/wg-server.so") if vpn_exist then local wgclient_status = ubus.call("network.interface.wgclient", "status") local wgserver_status = ubus.call("network.interface.wgserver", "status") local ovpnclient_status = ubus.call("network.interface.ovpnclient", "status") local ovpnserver_status = ubus.call("network.interface.ovpnserver", "status") -- wgclient local wgclient = { name = "wgclient", status = 0 } if wgclient_status then wgclient.status = (wgclient_status.pending == false and wgclient_status.up == true) and 1 or 2 local id = c:get("network", "wgclient", "config") local peer_id = id:match(".+_(%d+)") local group_id = c:get("wireguard", id, "group_id") if peer_id and group_id then wgclient.peer_id = tonumber(peer_id) wgclient.group_id = tonumber(group_id) end else wgclient.status = 0 end -- wgserver local wgserver = { name = "wgserver", status = 0 } if wgserver_status then wgserver.status = (wgserver_status.pending == false and wgserver_status.up == true) and 1 or 2 else wgserver.status = 0 end -- ovpnclient local ovpnclient = { name = "ovpnclient", status = 0 } if ovpnclient_status then ovpnclient.status = (ovpnclient_status.pending == false and ovpnclient_status.up == true) and 1 or 2 local ovpnc_id = c:get("network", "ovpnclient", "config") or "" local group_id, client_id = ovpnc_id:match("(%d+)_(%d+)") if group_id and client_id then ovpnclient.group_id = tonumber(group_id) ovpnclient.client_id = tonumber(client_id) end else ovpnclient.status = 0 end -- ovpnserver local ovpnserver = { name = "ovpnserver", status = 0 } if ovpnserver_status then ovpnserver.status = (ovpnserver_status.pending == false and ovpnserver_status.up == true) and 1 or 2 else ovpnserver.status = 0 end table.insert(service, wgclient) table.insert(service, wgserver) table.insert(service, ovpnclient) table.insert(service, ovpnserver) end if fs.access("/etc/config/adguardhome") then --local en = c:get("adguardhome", "config", "enabled") service[#service + 1] = { name = "adguard", status = #utils.pidof("AdGuardHome") > 0 and 1 or 0 } end if fs.access("/etc/config/tor") then local tor_status = 0 if #utils.pidof("tor") > 0 then tor_status = 2 -- connecting local log = utils.readfile("/var/lib/tor/control.log") if log and log:match("Bootstrapped 100%%") then tor_status = 1 -- connected end end service[#service + 1] = { name = "tor", status = tor_status } end --thread if fs.access("/etc/config/otbr") then local thread_status = 0 local thread_status_info = ubus.call("otbr", "state") if thread_status_info.State ~= "disabled" then thread_status = 1 local otbr_commissioner_status = ubus.call("otbr", "commissionerstate") service[#service + 1] = { name = "thread_commission", status = otbr_commissioner_status.State ~= 0 and 1 or 0 } end service[#service + 1] = { name = "thread_network", status = thread_status } end --ble if fs.access("/etc/config/gl_ble_d") then local ble_status = c:get("gl_ble_d", "scan_param", "scan_enable") or 0 service[#service + 1] = { name = "ble", status = tonumber(ble_status) } end --wifi local wifi_devices = {} if lfactory.get_radio() then c:foreach("wireless", "wifi-device", function(s) wifi_devices[s[".name"]] = s end) c:foreach("wireless", "wifi-iface", function(s) if s.mode ~= "sta" then local status = { up = s.disabled ~= '1', ssid = s.ssid, name = s['.name'], guest = s.guest == '1' } if s.encryption and s.encryption ~= "none" then status.passwd = s.key end local dev = wifi_devices[s.device] if dev then status.band = dev.band:upper() status.channel = dev.channel == "auto" and 0 or tonumber(dev.channel) end wifi[#wifi + 1] = status end end) end --client local client_status = ubus.call("gl-clients", "status") or {cable_total = 0, wireless_total = 0} table.insert(client, client_status) --mcu local mcu_support = lfactory.device_has_mcu() if mcu_support then local mcu = ubus.call("mcu", "status") if mcu then system.mcu = { temperature = mcu.temperature, charge_percent = mcu.charge_percent, charging_status = mcu.charging_status, charge_cnt = mcu.charge_cnt } end end --cpu local temperature_support = lfactory.device_has_temperature() if temperature_support then local cpu = { temperature = tonumber(utils.readfile(lfactory.get_temperature_path(), 'n') or 0) } if cpu.temperature > 1000 then cpu.temperature = cpu.temperature/1000 end cpu.temperature = math.floor(cpu.temperature + 0.5) system.cpu = cpu end return {system = system, network = network, service = service, wifi = wifi, client = client} end --[[ @method-type: call @method-name: get_info @method-desc: 获取设备的基本信息,包括型号,固件版本,硬件版本等 @out string ?hardware_version 设备的硬件版本 [暂时没有] @out string firmware_version 设备的固件版本 @out string firmware_type 设备当前版本固件的类型 @out string firmware_date 固件的编译日期 @out string vendor 设备厂商 @out string mac 设备mac地址 @out string ?ddns ddns @out string model 设备型号 @out string ?sn sn @out string ?sn_bak sn 备份 @out string ?country_code 国家代码 @out object ?hardware_feature 硬件属性 @out string ?hardware_feature.wan wan口 @out string ?hardware_feature.lan lan口 @out string ?hardware_feature.build_in_modem 内置modem @out string ?hardware_feature.usb usb口 @out string ?hardware_feature.usb_reset usb gpio路径及电平有效[modem 硬重启使用] @out string ?hardware_feature.radio radio[判断是否支持repeater] @out string ?hardware_feature.reset_button reset button @out string ?hardware_feature.switch_button switch button @out string ?hardware_feature.microsd SD 卡 @out bool ?hardware_feature.mcu 是否支持mcu 【检测电池电量 温度】 @out bool ?hardware_feature.fan 是否支持fan 【检测风扇状态 温度】 @out bool ?hardware_feature.nand 是否支持 nand @out bool ?hardware_feature.hwnat 是否支持 硬件网络加速 @out bool ?hardware_feature.nowds 是否支持 wds, true表示不支持wds @out object ?software_feature 软件属性 @out bool ?software_feature.ipv6 是否支持ipv6服务 @out bool ?software_feature.vpn 是否支持vpn服务 @out bool ?software_feature.adguard 是否支持adguard服务 @out bool ?software_feature.tor 是否支持tor服务 @out bool ?software_feature.ids_ips 是否支持IDS/IPS服务 @out bool ?software_feature.sms_forward 是否支持短信转发服务 @out bool ?software_feature.repeater_eap 是否支持中继 EAP 类型 @out bool ?software_feature.nas 是否支持nas服务 @out bool ?software_feature.passthrough 是否支持passthrough服务 @out bool disable_guest_during_scan_wifi 扫描 WiFi 时,是否会关闭访客 WiFi @out object board_info 板级属性 @out string board_info.architecture CPU架构 @out string board_info.hostname hostname @out string board_info.kernel_version 内核版本 @out string board_info.openwrt_version openwrt版本 @out string board_info.model openwrt板级型号 @out number cpu_num cpu核数 @out number ?err_code Error code,-1:获取信息失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","get_info"]} @out-example: {"id":1,"jsonrpc":"2.0","result":{"mac":"94:83:C4:0C:74:9A","hardware_version":"","software_feature":{"ids_ips":false,"ipv6":true,"adguard":false,"tor":false,"sms_forward":false,"vpn":true},"vendor":"GL.iNet","hardware_feature":{"switch_button":"","radio":"radio0","lan":"eth0","nand":true,"usb":"1-1.3","usb_reset":"/sys/class/gpio0/usb_power/value,1","build_in_modem":"1-1.2","microsd":"1-1.1","wan":"eth1","mcu":true,"fan":true,"nowds":false,"reset_button":"gpio-3"},"country_code":"","sn_bak":"705e8c25242eddd6","board_info":{"architecture":"ARMv7 Processor rev 4 (v7l)","hostname":"GL-AXT1800","kernel_version":"4.4.60","openwrt_version":"OpenWrt 21.02-SNAPSHOT r16273+114-378769b555","model":"GL Technologies, Inc. AXT1800"},"firmware_date":"2022-02-16 5:42:18","model":"xe300","ddns":"evc749a","sn":"4078493df9a88098","firmware_type":"alpha4","firmware_version":"4.0.0","cpu_num":4}} --]] function M.get_info() local model = lfactory.get_model() or "" local hardware_feature = { wan = lfactory.get_wan_port() or "", lan = lfactory.get_lan_port() or "", build_in_modem = lfactory.get_build_modem_port() or "", usb = lfactory.get_usb_port() or "", usb_reset = lfactory.get_usb_reset() or "", radio = lfactory.get_radio() or "", reset_button = lfactory.get_reset_button() or "", switch_button = lfactory.get_switch_button() or "", microsd = lfactory.get_microsd() or "", mcu = lfactory.device_has_mcu(), fan = lfactory.device_has_fan(), nand = lfactory.device_has_nand(), nowds = lfactory.device_has_nowds(), hwnat = fs.access("/sys/kernel/debug/hnat") or fs.access("/sys/kernel/debug/hnat_debug") } local c = uci.cursor() local software_feature = { ipv6 = fs.access("/usr/lib/oui-httpd/rpc/ipv6"), vpn = fs.access("/usr/lib/oui-httpd/rpc/ovpn-client.so") or fs.access("/usr/lib/oui-httpd/rpc/ovpn-server.so") or fs.access("/usr/lib/oui-httpd/rpc/wg-client.so") or fs.access("/usr/lib/oui-httpd/rpc/wg-server.so"), adguard = fs.access("/usr/lib/oui-httpd/rpc/adguardhome"), tor = fs.access("/usr/lib/oui-httpd/rpc/tor"), ids_ips = fs.access("/usr/lib/oui-httpd/rpc/ids_ips"), sms_forward = fs.access("/usr/lib/oui-httpd/rpc/sms-forward"), repeater_eap = c:get("wireless", "@wifi-device[0]", "type") == "mac80211", nas = fs.access("/usr/lib/oui-httpd/rpc/nas-web.so"), passthrough = fs.access("/usr/sbin/passthrough-service") } local board = ubus.call("system", "board") local board_info = { hostname = board.hostname or "", model = board.model or "", architecture = board.system or "", openwrt_version = board.release.description or "", kernel_version = board.kernel or "" } local _,cpu_num = utils.exec("grep","-c","processor","/proc/cpuinfo"):wait() return { model = model, mac = string.upper(lfactory.get_mac() or ""), vendor = utils.readfile("/proc/cpuinfo"):match('machine%s+: (%S+)') or "GL.iNet", hardware_version = "", firmware_version = lfactory.get_firmware_version() or "", firmware_date = lfactory.get_firmware_date() or "", firmware_type = utils.readfile("/etc/version.type", "*l") or "", -- ddns = lfactory.get_ddns() or "", sn = lfactory.get_sn() or "", sn_bak = lfactory.get_sn_bak() or "", country_code = lfactory.get_country_code() or "", hardware_feature = hardware_feature, software_feature = software_feature, board_info = board_info, cpu_num = tonumber(cpu_num), disable_guest_during_scan_wifi = gwifi.disable_guest_during_scan_wifi() } end --[[ @method-type: call @method-name: disk_info @method-desc: 获取磁盘信息(单位字节B) @out object root 根文件系统信息 @out number root.total 总大小 @out number root.free 剩余大小 @out number root.used 可用大小 @out object tmp 内存文件系统信息 @out number tmp.total 总大小 @out number tmp.free 剩余大小 @out number tmp.used 可用大小 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","disk_info"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"root": {"total": 1231, "free": 100, "used": 1131}, "tmp": {"total": 1231, "free": 100, "used": 1131}}} --]] function M.disk_info() local fslist = { root = "/", tmp = "/tmp" } local resp = {} for name, path in pairs(fslist) do local total, free, used = fs.statvfs(path) if total then resp[name] = { total = total, free = free, used = used } end end return resp end --[[ @method-type: call @method-name: get_timezone_config @method-desc: 获取当前路由器的时区信息 @out string zonename 路由器当前时区名[timezone为UTC时zonename会返回空] @out string timezone 路由器当前时区 @out string tzoffset 路由器当前时区偏移量 @out number localtime 路由器当前系统时间戳 @out bool auto_timezone_enabled 标识当前路由器是否启用自动时区. @out number ?err_code Error code,-1:获取信息失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","get_timezone_config"]} @out-example: {"id":1,"jsonrpc":"2.0","result":{"zonename":"Asia\/Shanghai","tzoffset":"+0800","autotimezone_enabled":true,"localtime":1643200134,"timezone":"CST-8"}} --]] function M.get_timezone_config() local c = uci.cursor() local zonename = c:get("system", "@system[0]", "zonename") or "" local timezone = c:get("system", "@system[0]", "timezone") or "" local autotimezone = c:get("glconfig", "general", "autotimezone") if not autotimezone then autotimezone = "1" c:set("glconfig", "general", "service") c:set("glconfig", "general", "autotimezone", "1") end c:commit("glconfig") local tzoffset = get_tzoffset() local status = ubus.call("system", "info") return {localtime = status.localtime, zonename = zonename, timezone = timezone, autotimezone_enabled = autotimezone == "1", tzoffset = tzoffset} end --[[ @method-type: call @method-name: set_timezone_config @method-desc: 设置路由器时区信息[除了时区为UTC外的其他时区,两个参数为必填]。 @in string zonename 指定路由器要设置的时区名. @in string timezone 指定路由器要设置的时区. @in number localtime 指定路由器要设置的系统时间戳 @out number ?err_code Error code,-1:无效参数. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","system","set_timezone_config",{"zonename":"","timezone":"UTC","localtime": 1653622981}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.set_timezone_config(params) local zonename = params.zonename local timezone = params.timezone local localtime = params.localtime if zonename ~= "UTC" and timezone ~= "UTC" then if type(zonename) ~= "string" or type(timezone) ~= "string" or timezone:gsub("%s+", "") == "" or timezone:gsub("%s+", "") == "" then return rpc.ERROR_CODE_INVALID_PARAMS end end local c = uci.cursor() if zonename == "UTC" or timezone == "UTC" then c:set("system", "@system[0]", "timezone", "UTC") else c:set("system", "@system[0]", "timezone", timezone) end c:set("system", "@system[0]", "zonename", zonename) c:commit("system") if localtime then local time = os.date("%Y-%m-%d %H:%M:%S",localtime) local cmd = "date -s ".."'"..time.."'" -- 判断时间戳长度 if type(localtime) ~= "number" then return {err_code = -1, err_msg = "localtime format error"} end ngx.pipe.spawn(cmd) end ngx.pipe.spawn({'/etc/init.d/system', 'reload'}):wait() end --[[ @method-type: call @method-name: reset_firmware @method-desc: 恢复出厂 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","reset_firmware"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.reset_firmware() ngx.timer.at(1, function() os.execute("/sbin/firstboot -r -y") end) end --[[ @method-type: call @method-name: set_password @method-desc: 设置密码 @in string username 用户名 @in string ?old_password 旧密码 @in string new_password 新密码 @out number err_code 错误码(-1: 无权限,-2: 未找到命令, -3: 其它未知错误) @out string err_msg 错误信息 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","set_password",{"username":"admin","old_password":"123","new_password":"456"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.set_password(params) local s = rpc.session() if not s.is_local then if s.aclgroup ~= "root" and params.username ~= s.username then return rpc.ERROR_CODE_ACCESS end end local username = params.username local old_password = params.old_password or "" local new_password = params.new_password if type(username) ~= "string" or type(new_password) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS end local err_code, err_msg = sys.password(username, old_password, new_password) if err_code ~= 0 then return { err_code = err_code, err_msg = err_msg } end end --[[ @method-type: call @method-name: add_user @method-desc: 新增用户 @in string username 用户名 @in number ?uid 用户ID @in number ?gid 用户组ID @in string ?password 密码 @in string ?home 用户主目录 @in string ?interpreter 用户命令解释器 @out number err_code 错误码(-1: uid 和 gid 必须大于0; -2: 用户名已存在; -3: uid 已存在; -4: gid 已存在) @out string err_msg 错误信息 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","add_user",{"username":"test","password":"123"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.add_user(param) local username = param.username local password = param.password local uid = param.uid local gid = param.gid local home = param.home local interpreter = param.interpreter uid = uid and tonumber(uid) gid = gid and tonumber(gid) if uid == 0 or gid == 0 then return { err_code = -1, err_msg = "uid and gid must grater than 0" } end local used_uid = {} local used_gid = {} for line in io.lines("/etc/passwd") do local fields = {} for field in line:gmatch("[^:]+") do fields[#fields + 1] = field end if username == fields[1] then return { err_code = -2, err_msg = "username already exists" } end fields[3] = tonumber(fields[3]) fields[4] = tonumber(fields[4]) if uid and uid == fields[3] then return { err_code = -3, err_msg = "uid already exists" } end if gid and gid == fields[4] then return { err_code = -3, err_msg = "gid already exists" } end used_uid[fields[3]] = true used_gid[fields[4]] = true end if not uid then for i = 1, 10000 do if not used_uid[i] then uid = i break end end end if not gid then for i = 1, 10000 do if not used_gid[i] then gid = i break end end end if not home then home = "/home/" .. username end local f = io.open("/etc/passwd", "a") local data = string.format("%s:x:%d:%d:%s:%s:%s\n", username, uid, gid, username, home, interpreter or "") f:write(data) f:close() f = io.open("/etc/shadow", "a") data = string.format("%s::0:0:99999:7:::\n", username) f:write(data) f:close() if password then sys.password(username, "", password) end end --[[ @method-type: call @method-name: remove_user @method-desc: 删除用户 @in string username 用户名 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","remove_user",{"username":"test"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.remove_user(param) local username = param.username if not username then return end local lines = {} for line in io.lines("/etc/passwd") do if #line > 0 and line:match("[^:]+") ~= username then lines[#lines + 1] = line end end utils.writefile("/etc/passwd", table.concat(lines, "\n") .. "\n") lines = {} for line in io.lines("/etc/shadow") do if #line > 0 and line:match("[^:]+") ~= username then lines[#lines + 1] = line end end utils.writefile("/etc/shadow", table.concat(lines, "\n") .. "\n") end --[[ @method-type: call @method-name: reboot @method-desc: 重启设备 @in number delay=1 延迟重启时间(单位秒) @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","reboot"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.reboot(param) local delay = param and param.delay and tonumber(param.delay) or 1 ngx.timer.at(delay, function() ubus.call("system", "reboot") end) end --[[ @method-type: call @method-name: get_unixtime @method-desc: 获取 Unix 时间戳 @out number time Unix 时间戳 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","get_unixtime"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"time": 1629106981}} --]] function M.get_unixtime() return { time = os.time() } end --[[ @method-type: call @method-name: get_httpd_mem_status @method-desc: 获取http服务器的内存使用情况,在测试内存泄漏时可通过该接口监视API是否存在内存泄漏 @out number memory_used 内存使用情况,单位是kb @out number err_code 错误码(-1: 获取内存使用信息失败或者httpd服务没有运行) @out string err_msg 错误信息 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","get_httpd_mem_status"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"memory_used": 11952}} --]] function M.get_httpd_mem_status() local status = ubus.call("service", "list", {name = "nginx"}) if status["nginx"] and status["nginx"].instances.instance1.running then local pid = status["nginx"].instances.instance1.pid local mem = utils.readfile("/proc/".. pid .. "/status"):match("VmRSS:%s+(%d+)") return { memory_used = tonumber(mem) } else return {err_code = -1, err_msg = "httpd process is not running"} end end --[[ @method-type: call @method-name: get_security_policy @method-desc: 获取路由的安全策略 @out number redirect_https 重定向开关 @out number security_rule 安全法规选项[0:无安全法规需求; 1:通用安全法规需求] @out number ?err_code Error code,-1:获取信息失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","get_security_policy"]} @out-example: {"id":1,"jsonrpc":"2.0","result":{"redirect_https":0,"security_rule":0}} --]] function M.get_security_policy() local c = uci.cursor() local redirect_https = c:get("oui-httpd", "main", "redirect_https") or 0 local security_rule = c:get("oui-httpd", "main", "security_rule") or 0 c:commit("oui-httpd") return { redirect_https = tonumber(redirect_https), security_rule = tonumber(security_rule) } end --[[ @method-type: call @method-name: set_security_policy @method-desc: 设置路由器的安全策略。 @in number redirect_https 是否重定向 @in number security_rule 指定路由器的安全法规选项. @out number ?err_code Error code,-1:无效参数. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","system","set_security_policy",{"redirect_https": 1 ,"security_rule": 1 }],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.set_security_policy(params) local redirect_https = params.redirect_https local security_rule = params.security_rule if not redirect_https or not security_rule then return { err_code = -1, err_msg = "params error, maybe one of your parameters is null" } end local c = uci.cursor() c:set("oui-httpd", "main", "redirect_https", redirect_https) c:set("oui-httpd", "main", "security_rule", security_rule) c:commit("oui-httpd") end --[[ @method-type: call @method-name: get_percent @method-desc: 获取电池电量 @out number percent 电量百分比. @out number ?err_code Error code,-1:获取信息失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","get_percent"]} @out-example: {"id":1,"jsonrpc":"2.0","result":{"percent": 100 }} --]] function M.get_percent() --mcu local mcu_support = lfactory.device_has_mcu() local percent = 0 if mcu_support then local mcu_status = utils.readfile("/tmp/mcu_data", "*l") == nil and "{}" or utils.readfile("/tmp/mcu_data", "*l") local mcu_list = cjson.decode(mcu_status) if mcu_list and mcu_status ~= "{}" then percent = mcu_list.P end return { percent = percent } end return { err_code = -1, err_msg = "Device does not support" } end --[[ @method-type: call @method-name: get_load @method-desc: 获取负载信息 @out array load_average 系统负载情况,固定返回 包含三个小数的数组,用小数表示CPU的使用率,依次表示1/5/15分钟的平均负载值 @out number memory_total 系统的总内存,单位是byte @out number memory_free 系统的空闲内存,单位是byte @out number memory_buff_cache 系统的buffered+cached, 单位为byte @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","system","get_load"]} @out-example: {"id":1,"jsonrpc":"2.0","result":{"load_average": [2.01, 0.89, 0.33], "memory_free": 105918464, "memory_total": 106278912, "memory_buff_cache": 12345678}} --]] function M.get_load() local system_status = ubus.call("system", "info") local memory = system_status.memory local load1, load5, load15 = utils.readfile("/proc/loadavg"):match("(%d+%.%d+) (%d+%.%d+) (%d+%.%d+)") local load_average = {tonumber(load1), tonumber(load5), tonumber(load15)} local memory_total = memory.total local memory_free = memory.free local memory_buff_cache = memory.cached and memory.buffered + memory.cached or memory.buffered return { load_average = load_average, memory_total = memory_total, memory_buff_cache = memory_buff_cache, memory_free = memory_free } end return M