]]--
-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.
-- @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()
-- @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
-- @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.
-- @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
+ local swaptotal = tonumber(meminfo:match("SwapTotal:%s*(%d+)"))
+ local swapcached = tonumber(meminfo:match("SwapCached:%s*(%d+)"))
+ local swapfree = tonumber(meminfo:match("SwapFree:%s*(%d+)"))
- 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, swaptotal, swapcached, swapfree
end
--- Retrieves the output of the "logread" command.
-- @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
-- @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
-- { "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
-- { "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
-- @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
-- 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
dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4
)
- routes[#routes+1] = {
+ local rt = {
dest = dst_ip,
gateway = gateway,
metric = tonumber(metric),
flags = tonumber(flags, 16),
device = dev
}
+
+ if callback then
+ callback(rt)
+ else
+ routes[#routes+1] = rt
+ end
end
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
"([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
-- @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
function process.list()
local data = {}
local k
- local ps = luci.util.execi("top -bn1")
+ local ps = luci.util.execi("/bin/busybox top -bn1")
if not ps then
return
end
- while true do
- local line = ps()
- if not line then
- return
- end
-
- k = luci.util.split(luci.util.trim(line), "%s+", nil, true)
- if k[1] == "PID" then
- break
- end
- end
-
for line in ps do
- local row = {}
-
- line = luci.util.trim(line)
- for i, value in ipairs(luci.util.split(line, "%s+", #k-1, true)) do
- row[k[i]] = value
- end
+ local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
+ "^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
+ )
- local pid = tonumber(row[k[1]])
- if pid then
- data[pid] = row
+ local idx = tonumber(pid)
+ if idx then
+ data[idx] = {
+ ['PID'] = pid,
+ ['PPID'] = ppid,
+ ['USER'] = user,
+ ['STAT'] = stat,
+ ['VSZ'] = vsz,
+ ['%MEM'] = mem,
+ ['%CPU'] = cpu,
+ ['COMMAND'] = cmd
+ }
end
end
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.
-- @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.
-- @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
-- @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
-- @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
- repeat
- ln = fd:read("*l") or ""
- c, f = ln:match("Channel (%d+) : (%d+%.%d+) GHz")
- if c and f then
- cns[tonumber(c)] = tonumber(f)
- end
- until not ( #ln > 0 )
- fd:close()
- end
-
- if #cns == 0 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
-- @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 " "