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|awk {' print $2 '} 2>/dev/null"
114 local c6 = "cat /proc/meminfo|grep ^Cached|awk {' print $2 '} 2>/dev/null"
115 local c7 = "cat /proc/meminfo|grep MemFree|awk {' print $2 '} 2>/dev/null"
116 local c8 = "cat /proc/meminfo|grep Buffers|awk {' print $2 '} 2>/dev/null"
118 local system = luci.util.trim(exec(c1))
120 local memtotal = luci.util.trim(exec(c5))
121 local memcached = luci.util.trim(exec(c6))
122 local memfree = luci.util.trim(exec(c7))
123 local membuffers = luci.util.trim(exec(c8))
124 local perc_memfree = math.floor((memfree/memtotal)*100)
125 local perc_membuffers = math.floor((membuffers/memtotal)*100)
126 local perc_memcached = math.floor((memcached/memtotal)*100)
129 system = luci.util.trim(exec(c2))
130 model = luci.util.trim(exec(c3))
132 model = luci.util.trim(exec(c4))
135 return system, model, memtotal, memcached, membuffers, memfree, perc_memfree, perc_membuffers, perc_memcached
140 return exec("logread")
144 -- Generates a random key of length BYTES
145 function uniqueid(bytes)
146 local fp = io.open("/dev/urandom")
147 local chunk = { fp:read(bytes):byte(1, bytes) }
152 local pattern = "%02X"
153 for i, byte in ipairs(chunk) do
154 hex = hex .. pattern:format(byte)
162 group.getgroup = posix.getgroup
165 -- Returns the ARP-Table
166 function net.arptable()
167 return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+")
170 -- Returns whether an IP-Adress belongs to a certain net
171 function net.belongs(ip, ipnet, prefix)
172 return (net.ip4bin(ip):sub(1, prefix) == net.ip4bin(ipnet):sub(1, prefix))
175 -- Detect the default route
176 function net.defaultroute()
177 local routes = net.routes()
180 for i, r in pairs(luci.sys.net.routes()) do
181 if r.Destination == "00000000" and (not route or route.Metric > r.Metric) then
189 -- Returns all available network interfaces
190 function net.devices()
192 for line in io.lines("/proc/net/dev") do
193 table.insert(devices, line:match(" *(.-):"))
198 -- Returns the MAC-Address belonging to the given IP-Address
199 function net.ip4mac(ip)
202 for i, l in ipairs(net.arptable()) do
203 if l["IP address"] == ip then
204 mac = l["HW address"]
211 -- Returns the prefix to a given netmask
212 function net.mask4prefix(mask)
213 local bin = net.ip4bin(mask)
219 return #luci.util.split(bin, "1")-1
222 -- Returns the kernel routing table
223 function net.routes()
224 return _parse_delimited_table(io.lines("/proc/net/route"))
227 -- Returns the numeric IP to a given hexstring
228 function net.hexip4(hex, be)
233 be = be or bigendian()
235 local hexdec = luci.bits.Hex2Dec
239 ip = ip .. tostring(hexdec(hex:sub(1,2))) .. "."
240 ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "."
241 ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "."
242 ip = ip .. tostring(hexdec(hex:sub(7,8)))
244 ip = ip .. tostring(hexdec(hex:sub(7,8))) .. "."
245 ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "."
246 ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "."
247 ip = ip .. tostring(hexdec(hex:sub(1,2)))
253 -- Returns the binary IP to a given IP
254 function net.ip4bin(ip)
255 local parts = luci.util.split(ip, '.')
260 local decbin = luci.bits.Dec2Bin
263 bin = bin .. decbin(parts[1], 8)
264 bin = bin .. decbin(parts[2], 8)
265 bin = bin .. decbin(parts[3], 8)
266 bin = bin .. decbin(parts[4], 8)
271 -- Tests whether a host is pingable
272 function net.pingtest(host)
273 return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
278 process.info = posix.getpid
280 -- Sets the gid of a process
281 function process.setgroup(pid, gid)
282 return posix.setpid("g", pid, gid)
285 -- Sets the uid of a process
286 function process.setuser(pid, uid)
287 return posix.setpid("u", pid, uid)
291 -- returns user information to a given uid
292 user.getuser = posix.getpasswd
294 -- checks whether a string matches the password of a certain system user
295 function user.checkpasswd(username, password)
296 local account = user.getuser(username)
298 -- FIXME: detect testing environment
299 if luci.fs.stat("/etc/shadow") and not luci.fs.access("/etc/shadow", "r") then
302 if account.passwd == "!" then
305 return (account.passwd == posix.crypt(password, account.passwd))
310 -- Changes the user password of given user
311 function user.setpasswd(user, pwd)
313 pwd = pwd:gsub("'", "")
317 user = user:gsub("'", "")
320 local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|"
321 cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1"
322 return os.execute(cmd)
328 function wifi.getiwconfig()
329 local cnt = exec("/usr/sbin/iwconfig 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("^(.-) +", "", 1)
336 iwc[k] = _parse_mixed_record(l)
343 function wifi.iwscan()
344 local cnt = exec("iwlist scan 2>/dev/null")
347 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
348 local k = l:match("^(.-) ")
349 l = l:gsub("^[^\n]+", "", 1)
350 l = luci.util.trim(l)
353 for j, c in pairs(luci.util.split(l, "\n Cell")) do
354 c = c:gsub("^(.-)- ", "", 1)
355 c = luci.util.split(c, "\n", 7)
356 c = table.concat(c, "\n", 1)
357 table.insert(iws[k], _parse_mixed_record(c))
366 -- Internal functions
368 function _parse_delimited_table(iter, delimiter)
369 delimiter = delimiter or "%s+"
372 local trim = luci.util.trim
373 local split = luci.util.split
375 local keys = split(trim(iter()), delimiter, nil, true)
376 for i, j in pairs(keys) do
377 keys[i] = trim(keys[i])
384 for i, j in pairs(split(line, delimiter, nil, true)) do
390 table.insert(data, row)
396 function _parse_mixed_record(cnt)
399 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
400 for j, f in pairs(luci.util.split(luci.util.trim(l), " ")) do
401 local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*')
405 table.insert(data, k)