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(user, password)
289 local account = user.getuser(user)
290 if posix.crypt and account then
291 return (account.passwd == posix.crypt(account.passwd, password))
295 -- Changes the user password of given user
296 function user.setpasswd(user, pwd)
298 pwd = pwd:gsub("'", "")
302 user = user:gsub("'", "")
305 local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|"
306 cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1"
307 return os.execute(cmd)
313 function wifi.getiwconfig()
314 local cnt = exec("/usr/sbin/iwconfig 2>/dev/null")
317 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
318 local k = l:match("^(.-) ")
319 l = l:gsub("^(.-) +", "", 1)
321 iwc[k] = _parse_mixed_record(l)
328 function wifi.iwscan()
329 local cnt = exec("iwlist scan 2>/dev/null")
332 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
333 local k = l:match("^(.-) ")
334 l = l:gsub("^[^\n]+", "", 1)
335 l = luci.util.trim(l)
338 for j, c in pairs(luci.util.split(l, "\n Cell")) do
339 c = c:gsub("^(.-)- ", "", 1)
340 c = luci.util.split(c, "\n", 7)
341 c = table.concat(c, "\n", 1)
342 table.insert(iws[k], _parse_mixed_record(c))
351 -- Internal functions
353 function _parse_delimited_table(iter, delimiter)
354 delimiter = delimiter or "%s+"
357 local trim = luci.util.trim
358 local split = luci.util.split
360 local keys = split(trim(iter()), delimiter, nil, true)
361 for i, j in pairs(keys) do
362 keys[i] = trim(keys[i])
369 for i, j in pairs(split(line, delimiter, nil, true)) do
375 table.insert(data, row)
381 function _parse_mixed_record(cnt)
384 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
385 for j, f in pairs(luci.util.split(luci.util.trim(l), " ")) do
386 local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*')
390 table.insert(data, k)