5 Utilities for interaction with the Linux system
11 Copyright 2008 Steven Barth <steven@midlink.org>
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
17 http://www.apache.org/licenses/LICENSE-2.0
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
27 module("luci.sys", package.seeall)
33 -- Returns whether a system is bigendian
35 local fp = io.open("/bin/sh")
37 local be = (fp:read(1):byte() ~= 1)
42 -- Runs "command" and returns its output
43 function exec(command)
44 local pp = io.popen(command)
45 local data = pp:read("*a")
51 -- Runs "command" and returns its output as a array of lines
52 function execl(command)
53 local pp = io.popen(command)
59 if (line == nil) then break end
60 table.insert(data, line)
67 -- Uses "luci-flash" to flash a new image file to the system
68 function flash(image, kpattern)
69 local cmd = "luci-flash "
71 cmd = cmd .. "-k '" .. kpattern:gsub("'", "") .. "' "
73 cmd = cmd .. "'" .. image:gsub("'", "") .. "' >/dev/null 2>&1"
75 return os.execute(cmd)
78 -- Returns the enivornment
81 -- Returns the hostname
83 return io.lines("/proc/sys/kernel/hostname")()
86 -- Returns the contents of a documented referred by an URL
88 return exec("wget -qO- '"..url:gsub("'", "").."'")
91 -- Returns the FFLuci-Basedir
93 return luci.fs.dirname(require("luci.debug").__file__)
96 -- Returns the load average
98 local loadavg = io.lines("/proc/loadavg")()
99 return loadavg:match("^(.-) (.-) (.-) (.-) (.-)$")
102 -- Reboots the system
104 return os.execute("reboot >/dev/null 2>&1")
107 -- Returns the system type, cpu name, and installed physical memory
109 local c1 = "cat /proc/cpuinfo|grep system\\ typ|cut -d: -f2 2>/dev/null"
110 local c2 = "uname -m 2>/dev/null"
111 local c3 = "cat /proc/cpuinfo|grep model\\ name|cut -d: -f2 2>/dev/null"
112 local c4 = "cat /proc/cpuinfo|grep cpu\\ model|cut -d: -f2 2>/dev/null"
113 local c5 = "cat /proc/meminfo|grep MemTotal|cut -d: -f2 2>/dev/null"
115 local s = luci.util.trim(exec(c1))
120 s = luci.util.trim(exec(c2))
121 m = luci.util.trim(exec(c3))
123 m = luci.util.trim(exec(c4))
126 r = luci.util.trim(exec(c5))
133 return exec("logread")
137 -- Generates a random key of length BYTES
138 function uniqueid(bytes)
139 local fp = io.open("/dev/urandom")
140 local chunk = { fp:read(bytes):byte(1, bytes) }
145 local pattern = "%02X"
146 for i, byte in ipairs(chunk) do
147 hex = hex .. pattern:format(byte)
155 group.getgroup = posix.getgroup
158 -- Returns the ARP-Table
159 function net.arptable()
160 return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+")
163 -- Returns whether an IP-Adress belongs to a certain net
164 function net.belongs(ip, ipnet, prefix)
165 return (net.ip4bin(ip):sub(1, prefix) == net.ip4bin(ipnet):sub(1, prefix))
168 -- Detect the default route
169 function net.defaultroute()
170 local routes = net.routes()
173 for i, r in pairs(luci.sys.net.routes()) do
174 if r.Destination == "00000000" and (not route or route.Metric > r.Metric) then
182 -- Returns all available network interfaces
183 function net.devices()
185 for line in io.lines("/proc/net/dev") do
186 table.insert(devices, line:match(" *(.-):"))
191 -- Returns the MAC-Address belonging to the given IP-Address
192 function net.ip4mac(ip)
195 for i, l in ipairs(net.arptable()) do
196 if l["IP address"] == ip then
197 mac = l["HW address"]
204 -- Returns the prefix to a given netmask
205 function net.mask4prefix(mask)
206 local bin = net.ip4bin(mask)
212 return #luci.util.split(bin, "1")-1
215 -- Returns the kernel routing table
216 function net.routes()
217 return _parse_delimited_table(io.lines("/proc/net/route"))
220 -- Returns the numeric IP to a given hexstring
221 function net.hexip4(hex, be)
226 be = be or bigendian()
228 local hexdec = luci.bits.Hex2Dec
232 ip = ip .. tostring(hexdec(hex:sub(1,2))) .. "."
233 ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "."
234 ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "."
235 ip = ip .. tostring(hexdec(hex:sub(7,8)))
237 ip = ip .. tostring(hexdec(hex:sub(7,8))) .. "."
238 ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "."
239 ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "."
240 ip = ip .. tostring(hexdec(hex:sub(1,2)))
246 -- Returns the binary IP to a given IP
247 function net.ip4bin(ip)
248 local parts = luci.util.split(ip, '.')
253 local decbin = luci.bits.Dec2Bin
256 bin = bin .. decbin(parts[1], 8)
257 bin = bin .. decbin(parts[2], 8)
258 bin = bin .. decbin(parts[3], 8)
259 bin = bin .. decbin(parts[4], 8)
264 -- Tests whether a host is pingable
265 function net.pingtest(host)
266 return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
271 process.info = posix.getpid
273 -- Sets the gid of a process
274 function process.setgroup(pid, gid)
275 return posix.setpid("g", pid, gid)
278 -- Sets the uid of a process
279 function process.setuser(pid, uid)
280 return posix.setpid("u", pid, uid)
284 -- returns user information to a given uid
285 user.getuser = posix.getpasswd
287 -- checks whether a string matches the password of a certain system user
288 function user.checkpasswd(username, password)
289 local account = user.getuser(username)
291 -- FIXME: detect testing environment
292 if luci.fs.stat("/etc/shadow") and not luci.fs.access("/etc/shadow", "r") then
295 if account.passwd == "!" then
298 return (account.passwd == posix.crypt(password, account.passwd))
303 -- Changes the user password of given user
304 function user.setpasswd(user, pwd)
306 pwd = pwd:gsub("'", "")
310 user = user:gsub("'", "")
313 local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|"
314 cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1"
315 return os.execute(cmd)
321 function wifi.getiwconfig()
322 local cnt = exec("/usr/sbin/iwconfig 2>/dev/null")
325 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
326 local k = l:match("^(.-) ")
327 l = l:gsub("^(.-) +", "", 1)
329 iwc[k] = _parse_mixed_record(l)
336 function wifi.iwscan()
337 local cnt = exec("iwlist scan 2>/dev/null")
340 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
341 local k = l:match("^(.-) ")
342 l = l:gsub("^[^\n]+", "", 1)
343 l = luci.util.trim(l)
346 for j, c in pairs(luci.util.split(l, "\n Cell")) do
347 c = c:gsub("^(.-)- ", "", 1)
348 c = luci.util.split(c, "\n", 7)
349 c = table.concat(c, "\n", 1)
350 table.insert(iws[k], _parse_mixed_record(c))
359 -- Internal functions
361 function _parse_delimited_table(iter, delimiter)
362 delimiter = delimiter or "%s+"
365 local trim = luci.util.trim
366 local split = luci.util.split
368 local keys = split(trim(iter()), delimiter, nil, true)
369 for i, j in pairs(keys) do
370 keys[i] = trim(keys[i])
377 for i, j in pairs(split(line, delimiter, nil, true)) do
383 table.insert(data, row)
389 function _parse_mixed_record(cnt)
392 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
393 for j, f in pairs(luci.util.split(luci.util.trim(l), " ")) do
394 local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*')
398 table.insert(data, k)