]]--
-local io = require "io"
-local os = require "os"
-local nixio = require "nixio"
-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 =
+ tonumber, ipairs, pairs, pcall, type, next, setmetatable, require
--- 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()
-- @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 nixio.uname().nodename
-- @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 = nixio.uname().machine
- 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 =
+ 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.
-- @param bytes Number of bytes for the unique id
-- @return String containing hex encoded id
function uniqueid(bytes)
- local rand = luci.fs.readfile("/dev/urandom", bytes)
+ local rand = fs.readfile("/dev/urandom", bytes)
return rand and nixio.bin.hexlify(rand)
end
-- @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)
+ return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+", callback)
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
-- { "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
-- @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:
-- { "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
nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false )
- routes[#routes+1] = {
+ local rt = {
source = src_ip,
dest = dst_ip,
nexthop = nexthop,
refcount = tonumber(refcnt, 16),
usecount = tonumber(usecnt, 16),
flags = tonumber(flags, 16),
- device = dev
+ 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
return routes
-- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
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.
+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
+ else
+ return pwh
+ end
+end
+
--- Test whether given string matches the password of a given system user.
-- @param username String containing the Unix user name
-- @param pass String containing the password to compare
-- @return Boolean indicating wheather the passwords are equal
function user.checkpasswd(username, pass)
- 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 ~= "!" and nixio.crypt(pass, pwh) ~= pwh then
+ local pwh = user.getpasswd(username)
+ if pwh and nixio.crypt(pass, pwh) ~= pwh then
return false
else
return true
-- @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 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
+ 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
+
+ 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
+ end
+ })
+ end
+end
+
--- Get iwconfig output for all wireless devices.
-- @return Table of tables containing the iwconfing output for each wifi device
function wifi.getiwconfig()
-- @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
+ local stat, iwinfo = pcall(require, "iwinfo")
+ local cns
+
+ if stat then
+ local t = iwinfo.type(iface or "")
+ if iface and t and iwinfo[t] then
+ cns = iwinfo[t].freqlist(iface)
end
- fd:close()
end
- if not ((pairs(cns))(cns)) then
+ if not cns or #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
+ {channel = 1, mhz = 2412},
+ {channel = 2, mhz = 2417},
+ {channel = 3, mhz = 2422},
+ {channel = 4, mhz = 2427},
+ {channel = 5, mhz = 2432},
+ {channel = 6, mhz = 2437},
+ {channel = 7, mhz = 2442},
+ {channel = 8, mhz = 2447},
+ {channel = 9, mhz = 2452},
+ {channel = 10, mhz = 2457},
+ {channel = 11, mhz = 2462}
}
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
-- @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 )
+ if fs.access(init.dir..name) then
+ return ( call(init.dir..name.." enabled >/dev/null") == 0 )
end
return false
end
-- @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("source "..init.dir..name.." enabled >/dev/null; exit $START")
end
end
-- @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 )
+ if fs.access(init.dir..name) then
+ return ( call(init.dir..name.." enable >/dev/null") == 1 )
end
end
-- @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 )
+ if fs.access(init.dir..name) then
+ return ( call(init.dir..name.." disable >/dev/null") == 0 )
end
end
-- Internal functions
-function _parse_delimited_table(iter, delimiter)
+function _parse_delimited_table(iter, delimiter, callback)
delimiter = delimiter or "%s+"
local data = {}
end
end
end
- table.insert(data, row)
+
+ if callback then
+ callback(row)
+ else
+ data[#data+1] = row
+ end
end
return data