X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fsys%2Fluasrc%2Fsys.lua;h=f9b253e764c7063b52a72e91a1fa23c87087f33e;hp=3fa34fd5bc046dfd259b2171969a74e73618c465;hb=d60d892385735ef274278cf63d363917eb3e6361;hpb=67049e97b4ff02874d62b41f72dbbfdfd97e2e88 diff --git a/libs/sys/luasrc/sys.lua b/libs/sys/luasrc/sys.lua index 3fa34fd5b..f9b253e76 100644 --- a/libs/sys/luasrc/sys.lua +++ b/libs/sys/luasrc/sys.lua @@ -25,17 +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 = tonumber, ipairs, pairs +local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require = + tonumber, ipairs, pairs, pcall, type, next, setmetatable, require --- LuCI Linux and POSIX system utilities. @@ -57,67 +59,50 @@ 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 boolean indicating status --- @return error message if any -function flash(image, kpattern) - local cmd = "luci-flash " - if kpattern then - cmd = cmd .. "-k '" .. kpattern:gsub("'", "") .. "' " - end - cmd = cmd .. "'" .. image:gsub("'", "") .. "' 2>/dev/null" - - local fp = io.popen(cmd) - local line = fp:read() - - if line == "Invalid image type" then - fp:close() - return false, line - else - line = fp:read() - if line == "Performing system upgrade..." then - return true - end - - line = fp:read() - if line == "Performing system upgrade..." then - return true - end - - fp:close() - return false, line - end -end - --- Retrieve information about currently mounted file systems. -- @return Table containing mount information function mounts() local data = {} local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"} local ps = luci.util.execi("df") - + if not ps then return else ps() end - + for line in ps do local row = {} - + local j = 1 for value in line:gmatch("[^%s]+") do row[k[j]] = value j = j + 1 end - + if row[k[1]] then + + -- this is a rather ugly workaround to cope with wrapped lines in + -- the df output: + -- + -- /dev/scsi/host0/bus0/target0/lun0/part3 + -- 114382024 93566472 15005244 86% /mnt/usb + -- + + if not row[k[2]] then + j = 2 + line = ps() + for value in line:gmatch("[^%s]+") do + row[k[j]] = value + j = j + 1 + end + end + table.insert(data, row) end end - + return data end @@ -130,12 +115,18 @@ 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 ---- Determine the current hostname. +--- Get or set the current hostname. +-- @param String containing a new hostname to set (optional) -- @return String containing the system hostname -function hostname() - return io.lines("/proc/sys/kernel/hostname")() +function hostname(newname) + if type(newname) == "string" and #newname > 0 then + fs.writefile( "/proc/sys/kernel/hostname", newname ) + return newname + else + return nixio.uname().nodename + end end --- Returns the contents of a documented referred by an URL. @@ -145,7 +136,7 @@ end -- @return String containing the contents of given the URL function httpget(url, stream, target) if not target then - local source = stream and io.open or luci.util.exec + local source = stream and io.popen or luci.util.exec return source("wget -qO- '"..url:gsub("'", "").."'") else return os.execute("wget -qO '%s' '%s'" % @@ -157,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. @@ -177,31 +166,30 @@ 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 c1 = "cat /proc/cpuinfo|grep system\\ typ|cut -d: -f2 2>/dev/null" - local c2 = "uname -m 2>/dev/null" - local c3 = "cat /proc/cpuinfo|grep model\\ name|cut -d: -f2 2>/dev/null" - local c4 = "cat /proc/cpuinfo|grep cpu\\ model|cut -d: -f2 2>/dev/null" - local c5 = "cat /proc/meminfo|grep MemTotal|awk {' print $2 '} 2>/dev/null" - local c6 = "cat /proc/meminfo|grep ^Cached|awk {' print $2 '} 2>/dev/null" - local c7 = "cat /proc/meminfo|grep MemFree|awk {' print $2 '} 2>/dev/null" - local c8 = "cat /proc/meminfo|grep Buffers|awk {' print $2 '} 2>/dev/null" - - local system = luci.util.trim(luci.util.exec(c1)) - local model = "" - local memtotal = tonumber(luci.util.trim(luci.util.exec(c5))) - local memcached = tonumber(luci.util.trim(luci.util.exec(c6))) - local memfree = tonumber(luci.util.trim(luci.util.exec(c7))) - local membuffers = tonumber(luci.util.trim(luci.util.exec(c8))) - - if system == "" then - system = luci.util.trim(luci.util.exec(c2)) - model = luci.util.trim(luci.util.exec(c3)) - else - model = luci.util.trim(luci.util.exec(c4)) - end - - return system, model, memtotal, memcached, membuffers, memfree + local cpuinfo = fs.readfile("/proc/cpuinfo") + local meminfo = fs.readfile("/proc/meminfo") + + 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 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, bogomips end --- Retrieves the output of the "logread" command. @@ -210,44 +198,26 @@ function syslog() return luci.util.exec("logread") end +--- Retrieves the output of the "dmesg" command. +-- @return String containing the current log buffer +function dmesg() + return luci.util.exec("dmesg") +end + --- Generates a random id with specified length. -- @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 @@ -258,23 +228,100 @@ 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) + return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+", callback) end ---- Determine the current default route. +--- Returns conntrack information +-- @return Table with the currently tracked IP connections +function net.conntrack(callback) + local connt = {} + 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, " +") + if flags[6] ~= "TIME_WAIT" then + entry.layer3 = flags[1] + entry.layer4 = flags[3] + for i=1, #entry do + entry[i] = nil + end + + if callback then + callback(entry) + else + connt[#connt+1] = entry + end + end + end + 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, " +") + if flags[4] ~= "TIME_WAIT" then + entry.layer3 = "ipv4" + entry.layer4 = flags[1] + for i=1, #entry do + entry[i] = nil + end + + if callback then + callback(entry) + else + connt[#connt+1] = entry + end + end + end + else + return nil + end + return connt +end + +--- Determine the current IPv4 default route. If multiple default routes exist, +-- return the one with the lowest metric. -- @return Table with the properties of the current default route. -- The following fields are defined: --- { "Mask", "RefCnt", "Iface", "Flags", "Window", "IRTT", --- "MTU", "Gateway", "Destination", "Metric", "Use" } +-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", +-- "flags", "device" } function net.defaultroute() - local routes = net.routes() - local route = nil + local route - for i, r in pairs(luci.sys.net.routes()) do - if r.Destination == "00000000" and (not route or route.Metric > r.Metric) then - route = r + net.routes(function(rt) + if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then + route = rt end + end) + + return route +end + +--- Determine the current IPv6 default route. If multiple default routes exist, +-- return the one with the lowest metric. +-- @return Table with the properties of the current default route. +-- The following fields are defined: +-- { "source", "dest", "nexthop", "metric", "refcount", "usecount", +-- "flags", "device" } +function net.defaultroute6() + 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 = rt + end + end) end return route @@ -283,25 +330,43 @@ 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 @@ -310,25 +375,116 @@ 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 --- Returns the current kernel routing table entries. -- @return Table of tables with properties of the corresponding routes. -- The following fields are defined for route entry tables: --- { "Mask", "RefCnt", "Iface", "Flags", "Window", "IRTT", --- "MTU", "Gateway", "Destination", "Metric", "Use" } -function net.routes() - return _parse_delimited_table(io.lines("/proc/net/route")) +-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", +-- "flags", "device" } +function net.routes(callback) + local routes = { } + + for line in io.lines("/proc/net/route") do + + local dev, dst_ip, gateway, flags, refcnt, usecnt, metric, + dst_mask, mtu, win, irtt = line:match( + "([^%s]+)\t([A-F0-9]+)\t([A-F0-9]+)\t([A-F0-9]+)\t" .. + "(%d+)\t(%d+)\t(%d+)\t([A-F0-9]+)\t(%d+)\t(%d+)\t(%d+)" + ) + + if dev then + gateway = luci.ip.Hex( gateway, 32, luci.ip.FAMILY_INET4 ) + dst_mask = luci.ip.Hex( dst_mask, 32, luci.ip.FAMILY_INET4 ) + dst_ip = luci.ip.Hex( + dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4 + ) + + local rt = { + dest = dst_ip, + gateway = gateway, + metric = tonumber(metric), + refcount = tonumber(refcnt), + usecount = tonumber(usecnt), + mtu = tonumber(mtu), + window = tonumber(window), + irtt = tonumber(irtt), + flags = tonumber(flags, 16), + device = dev + } + + if callback then + callback(rt) + else + routes[#routes+1] = rt + end + end + end + + return routes end +--- Returns the current ipv6 kernel routing table entries. +-- @return Table of tables with properties of the corresponding routes. +-- The following fields are defined for route entry tables: +-- { "source", "dest", "nexthop", "metric", "refcount", "usecount", +-- "flags", "device" } +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 + + local dst_ip, dst_prefix, src_ip, src_prefix, nexthop, + metric, refcnt, usecnt, flags, dev = line:match( + "([a-f0-9]+) ([a-f0-9]+) " .. + "([a-f0-9]+) ([a-f0-9]+) " .. + "([a-f0-9]+) ([a-f0-9]+) " .. + "([a-f0-9]+) ([a-f0-9]+) " .. + "([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 ) + + 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 + + return routes + end +end --- Tests whether the given host responds to ping probes. -- @param host String containing a hostname or IPv4 address @@ -347,7 +503,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 @@ -355,58 +514,59 @@ function process.list() local data = {} local k local ps = luci.util.execi("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[6] == "%VSZ" then + k[6] = "%MEM" + end 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 = tonumber(row[k[1]]) if pid then data[pid] = row end end - + return data 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. @@ -416,7 +576,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. @@ -430,21 +590,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. +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 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 - if account.passwd == "!" then - return true - else - return (account.passwd == posix.crypt(password, account.passwd)) - end +function user.checkpasswd(username, pass) + local pwh = user.getpasswd(username) + if pwh and nixio.crypt(pass, pwh) ~= pwh then + return false + else + return true end end @@ -454,16 +624,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 @@ -472,17 +643,67 @@ 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() - local cnt = luci.util.exec("/usr/sbin/iwconfig 2>/dev/null") + 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 - iwc[k] = _parse_mixed_record(l) + local entry, flags = _parse_mixed_record(l) + if entry then + entry.flags = flags + end + iwc[k] = entry end end @@ -506,7 +727,11 @@ function wifi.iwscan(iface) c = c:gsub("^(.-)- ", "", 1) c = luci.util.split(c, "\n", 7) c = table.concat(c, "\n", 1) - table.insert(iws[k], _parse_mixed_record(c)) + local entry, flags = _parse_mixed_record(c) + if entry then + entry.flags = flags + end + table.insert(iws[k], entry) end end end @@ -514,10 +739,97 @@ function wifi.iwscan(iface) 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 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 + end + + if not cns or #cns == 0 then + cns = { + {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 cns +end + + +--- LuCI system utilities / init related functions. +-- @class module +-- @name luci.sys.init +init = {} +init.dir = "/etc/init.d/" + +--- Get the names of all installed init scripts +-- @return Table containing the names of all inistalled init scripts +function init.names() + local names = { } + 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 fs.access(init.dir..name) then + return ( call(init.dir..name.." enabled >/dev/null") == 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 fs.access(init.dir..name) then + return call("source "..init.dir..name.." enabled >/dev/null; exit $START") + end +end + +--- Enable the given init script +-- @param name Name of the init script +-- @return Boolean indicating success +function init.enable(name) + if fs.access(init.dir..name) then + return ( call(init.dir..name.." enable >/dev/null") == 1 ) + end +end + +--- Disable the given init script +-- @param name Name of the init script +-- @return Boolean indicating success +function init.disable(name) + 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 = {} @@ -539,28 +851,35 @@ function _parse_delimited_table(iter, delimiter) end end end - table.insert(data, row) + + if callback then + callback(row) + else + data[#data+1] = row + end end return data end -function _parse_mixed_record(cnt) +function _parse_mixed_record(cnt, delimiter) + delimiter = delimiter or " " local data = {} + local flags = {} for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do - for j, f in pairs(luci.util.split(luci.util.trim(l), " ")) do - local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*') + for j, f in pairs(luci.util.split(luci.util.trim(l), delimiter, nil, true)) do + local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*') - if k then + if k then if x == "" then - table.insert(data, k) + table.insert(flags, k) else - data[k] = v + data[k] = v end - end - end + end + end end - return data + return data, flags end