X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=modules%2Fluci-base%2Fluasrc%2Fsys.lua;h=a97271732ad0f2d0d72db2b4695963dc391e9e3f;hp=1e594e1c88508816135874ff6c06d41838796d3c;hb=86a708b06b1284045c0216e35a2bf3f40411177e;hpb=7a3493b1f7d75a3945279115324cf2ff4da26b7b diff --git a/modules/luci-base/luasrc/sys.lua b/modules/luci-base/luasrc/sys.lua index 1e594e1c8..a97271732 100644 --- a/modules/luci-base/luasrc/sys.lua +++ b/modules/luci-base/luasrc/sys.lua @@ -16,27 +16,14 @@ 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. module "luci.sys" ---- Execute a given shell command and return the error code --- @class function --- @name call --- @param ... Command to call --- @return Error code of the command function call(...) return os.execute(...) / 256 end ---- Execute a given shell command and capture its standard output --- @class function --- @name exec --- @param command Command to call --- @return String containg the return the output of the command exec = luci.util.exec ---- Retrieve information about currently mounted file systems. --- @return Table containing mount information function mounts() local data = {} local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"} @@ -82,20 +69,11 @@ function mounts() return data end ---- Retrieve environment variables. If no variable is given then a table -- containing the whole environment is returned otherwise this function returns -- the corresponding string value for the given name or nil if no such variable -- exists. --- @class function --- @name getenv --- @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 = 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 fs.writefile( "/proc/sys/kernel/hostname", newname ) @@ -105,11 +83,6 @@ function hostname(newname) end end ---- Returns the contents of a documented referred by an URL. --- @param url The URL to retrieve --- @param stream Return a stream instead of a buffer --- @param target Directly write to target file name --- @return String containing the contents of given the URL function httpget(url, stream, target) if not target then local source = stream and io.popen or luci.util.exec @@ -120,46 +93,30 @@ function httpget(url, stream, target) end end ---- Initiate a system reboot. --- @return Return value of os.execute() function reboot() return os.execute("reboot >/dev/null 2>&1") end ---- Retrieves the output of the "logread" command. --- @return String containing the current log buffer 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 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 function uptime() return nixio.sysinfo().uptime end ---- LuCI system utilities / network related functions. --- @class module --- @name luci.sys.net net = {} ---- Returns the current arp-table entries as two-dimensional table. --- @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(callback) @@ -211,14 +168,13 @@ local function _nethints(what, callback) 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 + luci.ip.neighbors(nil, function(neigh) + if neigh.mac and neigh.family == 4 then + _add(what, neigh.mac:upper(), neigh.dest:string(), nil, nil) + elseif neigh.mac and neigh.family == 6 then + _add(what, neigh.mac:upper(), nil, neigh.dest:string(), nil) end - end + end) if fs.access("/etc/ethers") then for e in io.lines("/etc/ethers") do @@ -229,14 +185,18 @@ local function _nethints(what, callback) 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) + cur:foreach("dhcp", "dnsmasq", + function(s) + if s.leasefile and fs.access(s.leasefile) then + for e in io.lines(s.leasefile) 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 end - end + ) cur:foreach("dhcp", "host", function(s) @@ -269,8 +229,6 @@ local function _nethints(what, callback) 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) @@ -293,8 +251,6 @@ function net.mac_hints(callback) 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) @@ -317,8 +273,6 @@ function net.ipv4_hints(callback) 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) @@ -341,103 +295,78 @@ function net.ipv6_hints(callback) end end ---- 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 +function net.host_hints(callback) + if callback then + _nethints(1, function(mac, v4, v6, name) + if mac and mac ~= "00:00:00:00:00:00" and (v4 or v6 or name) then + callback(mac, v4, v6, name) end - end + end) else - return nil + local rv = { } + _nethints(1, function(mac, v4, v6, name) + if mac and mac ~= "00:00:00:00:00:00" and (v4 or v6 or name) then + local e = { } + if v4 then e.ipv4 = v4 end + if v6 then e.ipv6 = v6 end + if name then e.name = name end + rv[mac] = e + end + end) + return rv 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: --- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", --- "flags", "device" } -function net.defaultroute() - local route +function net.conntrack(callback) + local ok, nfct = pcall(io.lines, "/proc/net/nf_conntrack") + if not ok or not nfct then + return nil + end - net.routes(function(rt) - if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then - route = rt - end - end) + local line, connt = nil, (not callback) and { } + for line in nfct do + local fam, l3, l4, timeout, tuples = + line:match("^(ipv[46]) +(%d+) +%S+ +(%d+) +(%d+) +(.+)$") - return route -end + if fam and l3 and l4 and timeout and not tuples:match("^TIME_WAIT ") then + l4 = nixio.getprotobynumber(l4) ---- 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 + local entry = { + bytes = 0, + packets = 0, + layer3 = fam, + layer4 = l4 and l4.name or "unknown", + timeout = tonumber(timeout, 10) + } - 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) + local key, val + for key, val in tuples:gmatch("(%w+)=(%S+)") do + if key == "bytes" or key == "packets" then + entry[key] = entry[key] + tonumber(val, 10) + elseif key == "src" or key == "dst" then + if entry[key] == nil then + entry[key] = luci.ip.new(val):string() + end + elseif key == "sport" or key == "dport" then + if entry[key] == nil then + entry[key] = val + end + elseif val then + entry[key] = val + 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 + if callback then + callback(entry) + else + connt[#connt+1] = entry end - end) + end end - return route + return callback and true or connt end ---- Determine the names of available network interfaces. --- @return Table containing all current interface names function net.devices() local devs = {} for k, v in ipairs(nixio.getifaddrs()) do @@ -449,8 +378,6 @@ function net.devices() end ---- Return information about available network interfaces. --- @return Table containing all current interface names and their information function net.deviceinfo() local devs = {} for k, v in ipairs(nixio.getifaddrs()) do @@ -479,21 +406,6 @@ function net.deviceinfo() end --- Determine the MAC address belonging to the given IP address. --- @param ip IPv4 address --- @return String containing the MAC address or nil if it cannot be found -function net.ip4mac(ip) - local mac = nil - net.arptable(function(e) - if e["IP address"] == ip then - mac = e["HW address"] - 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: -- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", -- "flags", "device" } @@ -539,8 +451,6 @@ function net.routes(callback) 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" } @@ -602,30 +512,18 @@ function net.routes6(callback) end end ---- Tests whether the given host responds to ping probes. --- @param host String containing a hostname or IPv4 address --- @return Number containing 0 on success and >= 1 on error function net.pingtest(host) return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1") end ---- LuCI system utilities / process related functions. --- @class module --- @name luci.sys.process process = {} ---- Get the current process id. --- @class function --- @name process.info --- @return Number containing the current pid 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 @@ -658,51 +556,22 @@ function process.list() return data end ---- Set the gid of a process identified by given pid. --- @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(gid) return nixio.setgid(gid) end ---- Set the uid of a process identified by given pid. --- @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(uid) return nixio.setuid(uid) end ---- Send a signal to a process identified by given pid. --- @class function --- @name process.signal --- @param pid Number containing the process id --- @param sig Signal to send (default: 15 [SIGTERM]) --- @return Boolean indicating successful operation --- @return Number containing the error code if failed process.signal = nixio.kill ---- LuCI system utilities / user related functions. --- @class module --- @name luci.sys.user user = {} ---- Retrieve user informations for given uid. --- @class function --- @name getuser --- @param uid Number containing the Unix user id --- @return Table containing the following fields: -- { "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. --- @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) @@ -713,10 +582,6 @@ function user.getpasswd(username) 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 pwh, pwe = user.getpasswd(username) if pwe then @@ -725,10 +590,6 @@ function user.checkpasswd(username, pass) return false end ---- Change the password of given user. --- @param username String containing the Unix user name --- @param password String containing the password to compare --- @return Number containing 0 on success and >= 1 on error function user.setpasswd(username, password) if password then password = password:gsub("'", [['"'"']]) @@ -745,42 +606,26 @@ function user.setpasswd(username, password) end ---- LuCI system utilities / wifi related functions. --- @class module --- @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 + local wstate = luci.util.ubus("network.wireless", "status") or { } + + d = d or ifname + n = n and tonumber(n) or 1 + + if type(wstate[d]) == "table" and + type(wstate[d].interfaces) == "table" and + type(wstate[d].interfaces[n]) == "table" and + type(wstate[d].interfaces[n].ifname) == "string" + then + ifname = wstate[d].interfaces[n].ifname + else 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 local t = stat and iwinfo.type(ifname) @@ -798,14 +643,9 @@ function wifi.getiwinfo(ifname) 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 @@ -814,9 +654,6 @@ function init.names() return names 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("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null" @@ -830,62 +667,22 @@ local function init_action(action, name) 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) 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) 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 ---- 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 - - --- Internal functions - -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), delimiter, nil, true)) do - local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*') - - if k then - if x == "" then - table.insert(flags, k) - else - data[k] = v - end - end - end - end - - return data, flags -end