X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fsys%2Fluasrc%2Fsys.lua;h=825092fff8734ea4f01cdebeaaf418112d78cef8;hp=9910ed37ceb0e0b2bf03e6baa9f2e1042a362dad;hb=f66a13dc4d8a40e1b0af0b9fb18a97af70ee9f70;hpb=7590cd25f4bdc1c54088dadab973037c96f3b4d3 diff --git a/libs/sys/luasrc/sys.lua b/libs/sys/luasrc/sys.lua index 9910ed37c..825092fff 100644 --- a/libs/sys/luasrc/sys.lua +++ b/libs/sys/luasrc/sys.lua @@ -25,18 +25,19 @@ limitations under the License. ]]-- -local io = require "io" -local os = require "os" -local posix = require "posix" -local table = require "table" +local io = require "io" +local os = require "os" +local table = require "table" +local nixio = require "nixio" +local fs = require "nixio.fs" +local uci = require "luci.model.uci" local luci = {} luci.util = require "luci.util" -luci.fs = require "luci.fs" luci.ip = require "luci.ip" -local tonumber, ipairs, pairs, pcall, type = - tonumber, ipairs, pairs, pcall, type +local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select = + tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select --- LuCI Linux and POSIX system utilities. @@ -58,20 +59,6 @@ end -- @return String containg the return the output of the command exec = luci.util.exec ---- Invoke the luci-flash executable to write an image to the flash memory. --- @param image Local path or URL to image file --- @param kpattern Pattern of files to keep over flash process --- @return Return value of os.execute() -function flash(image, kpattern) - local cmd = "luci-flash " - if kpattern then - cmd = cmd .. "-k '" .. kpattern:gsub("'", "") .. "' " - end - cmd = cmd .. "'" .. image:gsub("'", "") .. "' >/dev/null 2>&1" - - return os.execute(cmd) -end - --- Retrieve information about currently mounted file systems. -- @return Table containing mount information function mounts() @@ -128,17 +115,17 @@ end -- @param var Name of the environment variable to retrieve (optional) -- @return String containg the value of the specified variable -- @return Table containing all variables if no variable name is given -getenv = posix.getenv +getenv = nixio.getenv --- Get or set the current hostname. -- @param String containing a new hostname to set (optional) -- @return String containing the system hostname function hostname(newname) if type(newname) == "string" and #newname > 0 then - luci.fs.writefile( "/proc/sys/kernel/hostname", newname .. "\n" ) + fs.writefile( "/proc/sys/kernel/hostname", newname ) return newname else - return posix.uname("%n") + return nixio.uname().nodename end end @@ -161,11 +148,9 @@ end -- @return String containing the average load value 1 minute ago -- @return String containing the average load value 5 minutes ago -- @return String containing the average load value 15 minutes ago --- @return String containing the active and total number of processes --- @return String containing the last used pid function loadavg() - local loadavg = io.lines("/proc/loadavg")() - return loadavg:match("^(.-) (.-) (.-) (.-) (.-)$") + local info = nixio.sysinfo() + return info.loads[1], info.loads[2], info.loads[3] end --- Initiate a system reboot. @@ -181,28 +166,31 @@ end -- @return String containing the memory used for caching in kB -- @return String containing the memory used for buffering in kB -- @return String containing the free memory amount in kB +-- @return String containing the cpu bogomips (number) function sysinfo() - local cpuinfo = luci.fs.readfile("/proc/cpuinfo") - local meminfo = luci.fs.readfile("/proc/meminfo") + local cpuinfo = fs.readfile("/proc/cpuinfo") + local meminfo = fs.readfile("/proc/meminfo") - local system = cpuinfo:match("system typ.-:%s*([^\n]+)") - local model = "" local memtotal = tonumber(meminfo:match("MemTotal:%s*(%d+)")) local memcached = tonumber(meminfo:match("\nCached:%s*(%d+)")) local memfree = tonumber(meminfo:match("MemFree:%s*(%d+)")) local membuffers = tonumber(meminfo:match("Buffers:%s*(%d+)")) + local bogomips = tonumber(cpuinfo:match("[Bb]ogo[Mm][Ii][Pp][Ss].-: ([^\n]+)")) or 0 - if not system then - system = posix.uname("%m") - model = cpuinfo:match("model name.-:%s*([^\n]+)") - if not model then - model = cpuinfo:match("Processor.-:%s*([^\n]+)") - end - else - model = cpuinfo:match("cpu model.-:%s*([^\n]+)") - end + local system = + cpuinfo:match("system type\t+: ([^\n]+)") or + cpuinfo:match("Processor\t+: ([^\n]+)") or + cpuinfo:match("model name\t+: ([^\n]+)") + + local model = + luci.util.pcdata(fs.readfile("/tmp/sysinfo/model")) or + cpuinfo:match("machine\t+: ([^\n]+)") or + cpuinfo:match("Hardware\t+: ([^\n]+)") or + luci.util.pcdata(fs.readfile("/proc/diag/model")) or + nixio.uname().machine or + system - return system, model, memtotal, memcached, membuffers, memfree + return system, model, memtotal, memcached, membuffers, memfree, bogomips end --- Retrieves the output of the "logread" command. @@ -221,40 +209,16 @@ end -- @param bytes Number of bytes for the unique id -- @return String containing hex encoded id function uniqueid(bytes) - local fp = io.open("/dev/urandom") - local chunk = { fp:read(bytes):byte(1, bytes) } - fp:close() - - local hex = "" - - local pattern = "%02X" - for i, byte in ipairs(chunk) do - hex = hex .. pattern:format(byte) - end - - return hex + local rand = fs.readfile("/dev/urandom", bytes) + return rand and nixio.bin.hexlify(rand) end --- Returns the current system uptime stats. -- @return String containing total uptime in seconds --- @return String containing idle time in seconds function uptime() - local loadavg = io.lines("/proc/uptime")() - return loadavg:match("^(.-) (.-)$") + return nixio.sysinfo().uptime end ---- LuCI system utilities / POSIX user group related functions. --- @class module --- @name luci.sys.group -group = {} - ---- Returns information about a POSIX user group. --- @class function --- @name getgroup --- @param group Group ID or name of a system user group --- @return Table with information about the requested group -group.getgroup = posix.getgroup - --- LuCI system utilities / network related functions. -- @class module @@ -265,37 +229,223 @@ net = {} -- @return Table of table containing the current arp entries. -- The following fields are defined for arp entry objects: -- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" } -function net.arptable() - return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+") +function net.arptable(callback) + local arp, e, r, v + if fs.access("/proc/net/arp") then + for e in io.lines("/proc/net/arp") do + local r = { }, v + for v in e:gmatch("%S+") do + r[#r+1] = v + end + + if r[1] ~= "IP" then + local x = { + ["IP address"] = r[1], + ["HW type"] = r[2], + ["Flags"] = r[3], + ["HW address"] = r[4], + ["Mask"] = r[5], + ["Device"] = r[6] + } + + if callback then + callback(x) + else + arp = arp or { } + arp[#arp+1] = x + end + end + end + end + return arp +end + +local function _nethints(what, callback) + local _, k, e, mac, ip, name + local cur = uci.cursor() + local ifn = { } + local hosts = { } + + local function _add(i, ...) + local k = select(i, ...) + if k then + if not hosts[k] then hosts[k] = { } end + hosts[k][1] = select(1, ...) or hosts[k][1] + hosts[k][2] = select(2, ...) or hosts[k][2] + hosts[k][3] = select(3, ...) or hosts[k][3] + hosts[k][4] = select(4, ...) or hosts[k][4] + end + end + + if fs.access("/proc/net/arp") then + for e in io.lines("/proc/net/arp") do + ip, mac = e:match("^([%d%.]+)%s+%S+%s+%S+%s+([a-fA-F0-9:]+)%s+") + if ip and mac then + _add(what, mac:upper(), ip, nil, nil) + end + end + end + + if fs.access("/etc/ethers") then + for e in io.lines("/etc/ethers") do + mac, ip = e:match("^([a-f0-9]%S+) (%S+)") + if mac and ip then + _add(what, mac:upper(), ip, nil, nil) + end + end + end + + if fs.access("/var/dhcp.leases") then + for e in io.lines("/var/dhcp.leases") do + mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)") + if mac and ip then + _add(what, mac:upper(), ip, nil, name ~= "*" and name) + end + end + end + + cur:foreach("dhcp", "host", + function(s) + for mac in luci.util.imatch(s.mac) do + _add(what, mac:upper(), s.ip, nil, s.name) + end + end) + + for _, e in ipairs(nixio.getifaddrs()) do + if e.name ~= "lo" then + ifn[e.name] = ifn[e.name] or { } + if e.family == "packet" and e.addr and #e.addr == 17 then + ifn[e.name][1] = e.addr:upper() + elseif e.family == "inet" then + ifn[e.name][2] = e.addr + elseif e.family == "inet6" then + ifn[e.name][3] = e.addr + end + end + end + + for _, e in pairs(ifn) do + if e[what] and (e[2] or e[3]) then + _add(what, e[1], e[2], e[3], e[4]) + end + end + + for _, e in luci.util.kspairs(hosts) do + callback(e[1], e[2], e[3], e[4]) + end +end + +--- Returns a two-dimensional table of mac address hints. +-- @return Table of table containing known hosts from various sources. +-- Each entry contains the values in the following order: +-- [ "mac", "name" ] +function net.mac_hints(callback) + if callback then + _nethints(1, function(mac, v4, v6, name) + name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 + if name and name ~= mac then + callback(mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4) + end + end) + else + local rv = { } + _nethints(1, function(mac, v4, v6, name) + name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 + if name and name ~= mac then + rv[#rv+1] = { mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 } + end + end) + return rv + end +end + +--- Returns a two-dimensional table of IPv4 address hints. +-- @return Table of table containing known hosts from various sources. +-- Each entry contains the values in the following order: +-- [ "ip", "name" ] +function net.ipv4_hints(callback) + if callback then + _nethints(2, function(mac, v4, v6, name) + name = name or nixio.getnameinfo(v4, nil, 100) or mac + if name and name ~= v4 then + callback(v4, name) + end + end) + else + local rv = { } + _nethints(2, function(mac, v4, v6, name) + name = name or nixio.getnameinfo(v4, nil, 100) or mac + if name and name ~= v4 then + rv[#rv+1] = { v4, name } + end + end) + return rv + end +end + +--- Returns a two-dimensional table of IPv6 address hints. +-- @return Table of table containing known hosts from various sources. +-- Each entry contains the values in the following order: +-- [ "ip", "name" ] +function net.ipv6_hints(callback) + if callback then + _nethints(3, function(mac, v4, v6, name) + name = name or nixio.getnameinfo(v6, nil, 100) or mac + if name and name ~= v6 then + callback(v6, name) + end + end) + else + local rv = { } + _nethints(3, function(mac, v4, v6, name) + name = name or nixio.getnameinfo(v6, nil, 100) or mac + if name and name ~= v6 then + rv[#rv+1] = { v6, name } + end + end) + return rv + end end --- Returns conntrack information -- @return Table with the currently tracked IP connections -function net.conntrack() +function net.conntrack(callback) local connt = {} - if luci.fs.access("/proc/net/nf_conntrack", "r") then + if fs.access("/proc/net/nf_conntrack", "r") then for line in io.lines("/proc/net/nf_conntrack") do line = line:match "^(.-( [^ =]+=).-)%2" local entry, flags = _parse_mixed_record(line, " +") - entry.layer3 = flags[1] - entry.layer4 = flags[3] - for i=1, #entry do - entry[i] = nil - end + if flags[6] ~= "TIME_WAIT" then + entry.layer3 = flags[1] + entry.layer4 = flags[3] + for i=1, #entry do + entry[i] = nil + end - connt[#connt+1] = entry + if callback then + callback(entry) + else + connt[#connt+1] = entry + end + end end - elseif luci.fs.access("/proc/net/ip_conntrack", "r") then + elseif fs.access("/proc/net/ip_conntrack", "r") then for line in io.lines("/proc/net/ip_conntrack") do line = line:match "^(.-( [^ =]+=).-)%2" local entry, flags = _parse_mixed_record(line, " +") - entry.layer3 = "ipv4" - entry.layer4 = flags[1] - for i=1, #entry do - entry[i] = nil - end + if flags[4] ~= "TIME_WAIT" then + entry.layer3 = "ipv4" + entry.layer4 = flags[1] + for i=1, #entry do + entry[i] = nil + end - connt[#connt+1] = entry + if callback then + callback(entry) + else + connt[#connt+1] = entry + end + end end else return nil @@ -310,12 +460,14 @@ end -- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", -- "flags", "device" } function net.defaultroute() - local route = nil - for _, r in pairs(net.routes()) do - if r.dest:prefix() == 0 and (not route or route.metric > r.metric) then - route = r + local route + + net.routes(function(rt) + if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then + route = rt end - end + end) + return route end @@ -326,42 +478,70 @@ end -- { "source", "dest", "nexthop", "metric", "refcount", "usecount", -- "flags", "device" } function net.defaultroute6() - local route = nil - local routes6 = net.routes6() - if routes6 then - for _, r in pairs(routes6) do - if r.dest:prefix() == 0 and - (not route or route.metric > r.metric) + local route + + net.routes6(function(rt) + if rt.dest:prefix() == 0 and rt.device ~= "lo" and + (not route or route.metric > rt.metric) + then + route = rt + end + end) + + if not route then + local global_unicast = luci.ip.IPv6("2000::/3") + net.routes6(function(rt) + if rt.dest:equal(global_unicast) and + (not route or route.metric > rt.metric) then - route = r + route = rt end - end + end) end + return route end --- Determine the names of available network interfaces. -- @return Table containing all current interface names function net.devices() - local devices = {} - for line in io.lines("/proc/net/dev") do - table.insert(devices, line:match(" *(.-):")) + local devs = {} + for k, v in ipairs(nixio.getifaddrs()) do + if v.family == "packet" then + devs[#devs+1] = v.name + end end - return devices + return devs end --- Return information about available network interfaces. -- @return Table containing all current interface names and their information function net.deviceinfo() - local devices = {} - for line in io.lines("/proc/net/dev") do - local name, data = line:match("^ *(.-): *(.*)$") - if name and data then - devices[name] = luci.util.split(data, " +", nil, true) + local devs = {} + for k, v in ipairs(nixio.getifaddrs()) do + if v.family == "packet" then + local d = v.data + d[1] = d.rx_bytes + d[2] = d.rx_packets + d[3] = d.rx_errors + d[4] = d.rx_dropped + d[5] = 0 + d[6] = 0 + d[7] = 0 + d[8] = d.multicast + d[9] = d.tx_bytes + d[10] = d.tx_packets + d[11] = d.tx_errors + d[12] = d.tx_dropped + d[13] = 0 + d[14] = d.collisions + d[15] = 0 + d[16] = 0 + devs[v.name] = d end end - return devices + return devs end @@ -370,13 +550,11 @@ end -- @return String containing the MAC address or nil if it cannot be found function net.ip4mac(ip) local mac = nil - - for i, l in ipairs(net.arptable()) do - if l["IP address"] == ip then - mac = l["HW address"] + net.arptable(function(e) + if e["IP address"] == ip then + mac = e["HW address"] end - end - + end) return mac end @@ -385,7 +563,7 @@ end -- The following fields are defined for route entry tables: -- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", -- "flags", "device" } -function net.routes() +function net.routes(callback) local routes = { } for line in io.lines("/proc/net/route") do @@ -403,7 +581,7 @@ function net.routes() dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4 ) - routes[#routes+1] = { + local rt = { dest = dst_ip, gateway = gateway, metric = tonumber(metric), @@ -415,6 +593,12 @@ function net.routes() flags = tonumber(flags, 16), device = dev } + + if callback then + callback(rt) + else + routes[#routes+1] = rt + end end end @@ -426,8 +610,8 @@ end -- The following fields are defined for route entry tables: -- { "source", "dest", "nexthop", "metric", "refcount", "usecount", -- "flags", "device" } -function net.routes6() - if luci.fs.access("/proc/net/ipv6_route", "r") then +function net.routes6(callback) + if fs.access("/proc/net/ipv6_route", "r") then local routes = { } for line in io.lines("/proc/net/ipv6_route") do @@ -441,26 +625,43 @@ function net.routes6() "([a-f0-9]+) +([^%s]+)" ) - src_ip = luci.ip.Hex( - src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false - ) - - dst_ip = luci.ip.Hex( - dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false - ) - - nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false ) - - routes[#routes+1] = { - source = src_ip, - dest = dst_ip, - nexthop = nexthop, - metric = tonumber(metric, 16), - refcount = tonumber(refcnt, 16), - usecount = tonumber(usecnt, 16), - flags = tonumber(flags, 16), - device = dev - } + if dst_ip and dst_prefix and + src_ip and src_prefix and + nexthop and metric and + refcnt and usecnt and + flags and dev + then + src_ip = luci.ip.Hex( + src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false + ) + + dst_ip = luci.ip.Hex( + dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false + ) + + nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false ) + + local rt = { + source = src_ip, + dest = dst_ip, + nexthop = nexthop, + metric = tonumber(metric, 16), + refcount = tonumber(refcnt, 16), + usecount = tonumber(usecnt, 16), + flags = tonumber(flags, 16), + device = dev, + + -- lua number is too small for storing the metric + -- add a metric_raw field with the original content + metric_raw = metric + } + + if callback then + callback(rt) + else + routes[#routes+1] = rt + end + end end return routes @@ -484,7 +685,10 @@ process = {} -- @class function -- @name process.info -- @return Number containing the current pid -process.info = posix.getpid +function process.info(key) + local s = {uid = nixio.getuid(), gid = nixio.getgid()} + return not key and s or s[key] +end --- Retrieve information about currently running processes. -- @return Table containing process information @@ -504,6 +708,9 @@ function process.list() end k = luci.util.split(luci.util.trim(line), "%s+", nil, true) + if k[6] == "%VSZ" then + k[6] = "%MEM" + end if k[1] == "PID" then break end @@ -527,23 +734,21 @@ function process.list() end --- Set the gid of a process identified by given pid. --- @param pid Number containing the process id -- @param gid Number containing the Unix group id -- @return Boolean indicating successful operation -- @return String containing the error message if failed -- @return Number containing the error code if failed -function process.setgroup(pid, gid) - return posix.setpid("g", pid, gid) +function process.setgroup(gid) + return nixio.setgid(gid) end --- Set the uid of a process identified by given pid. --- @param pid Number containing the process id -- @param uid Number containing the Unix user id -- @return Boolean indicating successful operation -- @return String containing the error message if failed -- @return Number containing the error code if failed -function process.setuser(pid, uid) - return posix.setpid("u", pid, uid) +function process.setuser(uid) + return nixio.setuid(uid) end --- Send a signal to a process identified by given pid. @@ -553,7 +758,7 @@ end -- @param sig Signal to send (default: 15 [SIGTERM]) -- @return Boolean indicating successful operation -- @return Number containing the error code if failed -process.signal = posix.kill +process.signal = nixio.kill --- LuCI system utilities / user related functions. @@ -567,43 +772,31 @@ user = {} -- @param uid Number containing the Unix user id -- @return Table containing the following fields: -- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" } -user.getuser = posix.getpasswd +user.getuser = nixio.getpw + +--- Retrieve the current user password hash. +-- @param username String containing the username to retrieve the password for +-- @return String containing the hash or nil if no password is set. +-- @return Password database entry +function user.getpasswd(username) + local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username) + local pwh = pwe and (pwe.pwdp or pwe.passwd) + if not pwh or #pwh < 1 or pwh == "!" or pwh == "x" then + return nil, pwe + else + return pwh, pwe + end +end --- Test whether given string matches the password of a given system user. -- @param username String containing the Unix user name --- @param password String containing the password to compare +-- @param pass String containing the password to compare -- @return Boolean indicating wheather the passwords are equal -function user.checkpasswd(username, password) - local account = user.getuser(username) - - if account then - local pwd = account.passwd - local shadowpw - if #pwd == 1 then - if luci.fs.stat("/etc/shadow") then - if not pcall(function() - for l in io.lines("/etc/shadow") do - shadowpw = l:match("^%s:([^:]+)" % username) - if shadowpw then - pwd = shadowpw - break - end - end - end) then - return nil, "Unable to access shadow-file" - end - end - - if pwd == "!" then - return true - end - end - - if pwd and #pwd > 0 and password and #password > 0 then - return (pwd == posix.crypt(password, pwd)) - end +function user.checkpasswd(username, pass) + local pwh, pwe = user.getpasswd(username) + if pwe then + return (pwh == nil or nixio.crypt(pass, pwh) == pwh) end - return false end @@ -613,16 +806,17 @@ end -- @return Number containing 0 on success and >= 1 on error function user.setpasswd(username, password) if password then - password = password:gsub("'", "") + password = password:gsub("'", [['"'"']]) end if username then - username = username:gsub("'", "") + username = username:gsub("'", [['"'"']]) end - local cmd = "(echo '"..password.."';sleep 1;echo '"..password.."')|" - cmd = cmd .. "passwd '"..username.."' >/dev/null 2>&1" - return os.execute(cmd) + return os.execute( + "(echo '" .. password .. "'; sleep 1; echo '" .. password .. "') | " .. + "passwd '" .. username .. "' >/dev/null 2>&1" + ) end @@ -631,85 +825,51 @@ end -- @name luci.sys.wifi wifi = {} ---- Get iwconfig output for all wireless devices. --- @return Table of tables containing the iwconfing output for each wifi device -function wifi.getiwconfig() - local cnt = luci.util.exec("PATH=/sbin:/usr/sbin iwconfig 2>/dev/null") - local iwc = {} - - for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do - local k = l:match("^(.-) ") - l = l:gsub("^(.-) +", "", 1) - if k then - local entry, flags = _parse_mixed_record(l) - if entry then - entry.flags = flags - end - iwc[k] = entry +--- Get wireless information for given interface. +-- @param ifname String containing the interface name +-- @return A wrapped iwinfo object instance +function wifi.getiwinfo(ifname) + local stat, iwinfo = pcall(require, "iwinfo") + + if ifname then + local c = 0 + local u = uci.cursor_state() + local d, n = ifname:match("^(%w+)%.network(%d+)") + if d and n then + ifname = d + n = tonumber(n) + u:foreach("wireless", "wifi-iface", + function(s) + if s.device == d then + c = c + 1 + if c == n then + ifname = s.ifname or s.device + return false + end + end + end) + elseif u:get("wireless", ifname) == "wifi-device" then + u:foreach("wireless", "wifi-iface", + function(s) + if s.device == ifname and s.ifname then + ifname = s.ifname + return false + end + end) end - end - - return iwc -end ---- Get iwlist scan output from all wireless devices. --- @return Table of tables contaiing all scan results -function wifi.iwscan(iface) - local siface = iface or "" - local cnt = luci.util.exec("iwlist "..siface.." scan 2>/dev/null") - local iws = {} - - for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do - local k = l:match("^(.-) ") - l = l:gsub("^[^\n]+", "", 1) - l = luci.util.trim(l) - if k then - iws[k] = {} - for j, c in pairs(luci.util.split(l, "\n Cell")) do - c = c:gsub("^(.-)- ", "", 1) - c = luci.util.split(c, "\n", 7) - c = table.concat(c, "\n", 1) - local entry, flags = _parse_mixed_record(c) - if entry then - entry.flags = flags + local t = stat and iwinfo.type(ifname) + local x = t and iwinfo[t] or { } + return setmetatable({}, { + __index = function(t, k) + if k == "ifname" then + return ifname + elseif x[k] then + return x[k](ifname) end - table.insert(iws[k], entry) - end - end - end - - return iface and (iws[iface] or {}) or iws -end - ---- Get available channels from given wireless iface. --- @param iface Wireless interface (optional) --- @return Table of available channels -function wifi.channels(iface) - local cmd = "iwlist " .. ( iface or "" ) .. " freq 2>/dev/null" - local cns = { } - - local fd = io.popen(cmd) - if fd then - local ln, c, f - while true do - ln = fd:read("*l") - if not ln then break end - c, f = ln:match("Channel (%d+) : (%d+%.%d+) GHz") - if c and f then - cns[tonumber(c)] = tonumber(f) end - end - fd:close() - end - - if not ((pairs(cns))(cns)) then - cns = { - 2.412, 2.417, 2.422, 2.427, 2.432, 2.437, - 2.442, 2.447, 2.452, 2.457, 2.462 - } + }) end - - return cns end @@ -723,79 +883,65 @@ init.dir = "/etc/init.d/" -- @return Table containing the names of all inistalled init scripts function init.names() local names = { } - for _, name in ipairs(luci.fs.glob(init.dir.."*")) do - names[#names+1] = luci.fs.basename(name) + for name in fs.glob(init.dir.."*") do + names[#names+1] = fs.basename(name) end return names end ---- Test whether the given init script is enabled --- @param name Name of the init script --- @return Boolean indicating whether init is enabled -function init.enabled(name) - if luci.fs.access(init.dir..name) then - return ( call(init.dir..name.." enabled") == 0 ) - end - return false -end - --- Get the index of he given init script -- @param name Name of the init script -- @return Numeric index value function init.index(name) - if luci.fs.access(init.dir..name) then - return call("source "..init.dir..name.."; exit $START") + if fs.access(init.dir..name) then + return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null" + %{ init.dir, name }) end end +local function init_action(action, name) + if fs.access(init.dir..name) then + return call("env -i %s%s %s >/dev/null" %{ init.dir, name, action }) + end +end + +--- Test whether the given init script is enabled +-- @param name Name of the init script +-- @return Boolean indicating whether init is enabled +function init.enabled(name) + return (init_action("enabled", name) == 0) +end + --- Enable the given init script -- @param name Name of the init script -- @return Boolean indicating success function init.enable(name) - if luci.fs.access(init.dir..name) then - return ( call(init.dir..name.." enable") == 1 ) - end + return (init_action("enable", name) == 1) end --- Disable the given init script -- @param name Name of the init script -- @return Boolean indicating success function init.disable(name) - if luci.fs.access(init.dir..name) then - return ( call(init.dir..name.." disable") == 0 ) - end + return (init_action("disable", name) == 0) end +--- Start the given init script +-- @param name Name of the init script +-- @return Boolean indicating success +function init.start(name) + return (init_action("start", name) == 0) +end --- Internal functions - -function _parse_delimited_table(iter, delimiter) - delimiter = delimiter or "%s+" - - local data = {} - local trim = luci.util.trim - local split = luci.util.split - - local keys = split(trim(iter()), delimiter, nil, true) - for i, j in pairs(keys) do - keys[i] = trim(keys[i]) - end +--- Stop the given init script +-- @param name Name of the init script +-- @return Boolean indicating success +function init.stop(name) + return (init_action("stop", name) == 0) +end - for line in iter do - local row = {} - line = trim(line) - if #line > 0 then - for i, j in pairs(split(line, delimiter, nil, true)) do - if keys[i] then - row[keys[i]] = j - end - end - end - table.insert(data, row) - end - return data -end +-- Internal functions function _parse_mixed_record(cnt, delimiter) delimiter = delimiter or " "