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)
161 -- Returns uptime stats
163 local loadavg = io.lines("/proc/uptime")()
164 return loadavg:match("^(.-) (.-)$")
169 group.getgroup = posix.getgroup
172 -- Returns the ARP-Table
173 function net.arptable()
174 return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+")
177 -- Returns whether an IP-Adress belongs to a certain net
178 function net.belongs(ip, ipnet, prefix)
179 return (net.ip4bin(ip):sub(1, prefix) == net.ip4bin(ipnet):sub(1, prefix))
182 -- Detect the default route
183 function net.defaultroute()
184 local routes = net.routes()
187 for i, r in pairs(luci.sys.net.routes()) do
188 if r.Destination == "00000000" and (not route or route.Metric > r.Metric) then
196 -- Returns all available network interfaces
197 function net.devices()
199 for line in io.lines("/proc/net/dev") do
200 table.insert(devices, line:match(" *(.-):"))
205 -- Returns the MAC-Address belonging to the given IP-Address
206 function net.ip4mac(ip)
209 for i, l in ipairs(net.arptable()) do
210 if l["IP address"] == ip then
211 mac = l["HW address"]
218 -- Returns the prefix to a given netmask
219 function net.mask4prefix(mask)
220 local bin = net.ip4bin(mask)
226 return #luci.util.split(bin, "1")-1
229 -- Returns the kernel routing table
230 function net.routes()
231 return _parse_delimited_table(io.lines("/proc/net/route"))
234 -- Returns the numeric IP to a given hexstring
235 function net.hexip4(hex, be)
240 be = be or bigendian()
242 local hexdec = luci.bits.Hex2Dec
246 ip = ip .. tostring(hexdec(hex:sub(1,2))) .. "."
247 ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "."
248 ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "."
249 ip = ip .. tostring(hexdec(hex:sub(7,8)))
251 ip = ip .. tostring(hexdec(hex:sub(7,8))) .. "."
252 ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "."
253 ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "."
254 ip = ip .. tostring(hexdec(hex:sub(1,2)))
260 -- Returns the binary IP to a given IP
261 function net.ip4bin(ip)
262 local parts = luci.util.split(ip, '.')
267 local decbin = luci.bits.Dec2Bin
270 bin = bin .. decbin(parts[1], 8)
271 bin = bin .. decbin(parts[2], 8)
272 bin = bin .. decbin(parts[3], 8)
273 bin = bin .. decbin(parts[4], 8)
278 -- Tests whether a host is pingable
279 function net.pingtest(host)
280 return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
285 process.info = posix.getpid
287 -- Sets the gid of a process
288 function process.setgroup(pid, gid)
289 return posix.setpid("g", pid, gid)
292 -- Sets the uid of a process
293 function process.setuser(pid, uid)
294 return posix.setpid("u", pid, uid)
298 -- returns user information to a given uid
299 user.getuser = posix.getpasswd
301 -- checks whether a string matches the password of a certain system user
302 function user.checkpasswd(username, password)
303 local account = user.getuser(username)
305 -- FIXME: detect testing environment
306 if luci.fs.stat("/etc/shadow") and not luci.fs.access("/etc/shadow", "r") then
309 if account.passwd == "!" then
312 return (account.passwd == posix.crypt(password, account.passwd))
317 -- Changes the user password of given user
318 function user.setpasswd(user, pwd)
320 pwd = pwd:gsub("'", "")
324 user = user:gsub("'", "")
327 local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|"
328 cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1"
329 return os.execute(cmd)
335 function wifi.getiwconfig()
336 local cnt = exec("/usr/sbin/iwconfig 2>/dev/null")
339 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
340 local k = l:match("^(.-) ")
341 l = l:gsub("^(.-) +", "", 1)
343 iwc[k] = _parse_mixed_record(l)
350 function wifi.iwscan()
351 local cnt = exec("iwlist scan 2>/dev/null")
354 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
355 local k = l:match("^(.-) ")
356 l = l:gsub("^[^\n]+", "", 1)
357 l = luci.util.trim(l)
360 for j, c in pairs(luci.util.split(l, "\n Cell")) do
361 c = c:gsub("^(.-)- ", "", 1)
362 c = luci.util.split(c, "\n", 7)
363 c = table.concat(c, "\n", 1)
364 table.insert(iws[k], _parse_mixed_record(c))
373 -- Internal functions
375 function _parse_delimited_table(iter, delimiter)
376 delimiter = delimiter or "%s+"
379 local trim = luci.util.trim
380 local split = luci.util.split
382 local keys = split(trim(iter()), delimiter, nil, true)
383 for i, j in pairs(keys) do
384 keys[i] = trim(keys[i])
391 for i, j in pairs(split(line, delimiter, nil, true)) do
397 table.insert(data, row)
403 function _parse_mixed_record(cnt)
406 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
407 for j, f in pairs(luci.util.split(luci.util.trim(l), " ")) do
408 local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*')
412 table.insert(data, k)