* libs/web: Added Logout support
[project/luci.git] / libs / core / luasrc / sys.lua
1 --[[
2 LuCI - System library
3
4 Description:
5 Utilities for interaction with the Linux system
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
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
16
17 http://www.apache.org/licenses/LICENSE-2.0
18
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.
24
25 ]]--
26
27 module("luci.sys", package.seeall)
28 require("posix")
29 require("luci.bits")
30 require("luci.util")
31 require("luci.fs")
32
33 -- Returns whether a system is bigendian
34 function bigendian()
35 local fp = io.open("/bin/sh")
36 fp:seek("set", 5)
37 local be = (fp:read(1):byte() ~= 1)
38 fp:close()
39 return be
40 end
41
42 -- Runs "command" and returns its output
43 function exec(command)
44 local pp = io.popen(command)
45 local data = pp:read("*a")
46 pp:close()
47
48 return data
49 end
50
51 -- Runs "command" and returns its output as a array of lines
52 function execl(command)
53 local pp = io.popen(command)
54 local line = ""
55 local data = {}
56
57 while true do
58 line = pp:read()
59 if (line == nil) then break end
60 table.insert(data, line)
61 end
62 pp:close()
63
64 return data
65 end
66
67 -- Uses "luci-flash" to flash a new image file to the system
68 function flash(image, kpattern)
69 local cmd = "luci-flash "
70 if kpattern then
71 cmd = cmd .. "-k '" .. kpattern:gsub("'", "") .. "' "
72 end
73 cmd = cmd .. "'" .. image:gsub("'", "") .. "' >/dev/null 2>&1"
74
75 return os.execute(cmd)
76 end
77
78 -- Returns the enivornment
79 getenv = posix.getenv
80
81 -- Returns the hostname
82 function hostname()
83 return io.lines("/proc/sys/kernel/hostname")()
84 end
85
86 -- Returns the contents of a documented referred by an URL
87 function httpget(url)
88 return exec("wget -qO- '"..url:gsub("'", "").."'")
89 end
90
91 -- Returns the FFLuci-Basedir
92 function libpath()
93 return luci.fs.dirname(require("luci.debug").__file__)
94 end
95
96 -- Returns the load average
97 function loadavg()
98 local loadavg = io.lines("/proc/loadavg")()
99 return loadavg:match("^(.-) (.-) (.-) (.-) (.-)$")
100 end
101
102 -- Reboots the system
103 function reboot()
104 return os.execute("reboot >/dev/null 2>&1")
105 end
106
107 -- Returns the system type, cpu name, and installed physical memory
108 function sysinfo()
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"
114
115 local s = luci.util.trim(exec(c1))
116 local m = ""
117 local r = ""
118
119 if s == "" then
120 s = luci.util.trim(exec(c2))
121 m = luci.util.trim(exec(c3))
122 else
123 m = luci.util.trim(exec(c4))
124 end
125
126 r = luci.util.trim(exec(c5))
127
128 return s, m, r
129 end
130
131 -- Reads the syslog
132 function syslog()
133 return exec("logread")
134 end
135
136
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) }
141 fp:close()
142
143 local hex = ""
144
145 local pattern = "%02X"
146 for i, byte in ipairs(chunk) do
147 hex = hex .. pattern:format(byte)
148 end
149
150 return hex
151 end
152
153
154 group = {}
155 group.getgroup = posix.getgroup
156
157 net = {}
158 -- Returns the ARP-Table
159 function net.arptable()
160 return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+")
161 end
162
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))
166 end
167
168 -- Detect the default route
169 function net.defaultroute()
170 local routes = net.routes()
171 local route = nil
172
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
175 route = r
176 end
177 end
178
179 return route
180 end
181
182 -- Returns all available network interfaces
183 function net.devices()
184 local devices = {}
185 for line in io.lines("/proc/net/dev") do
186 table.insert(devices, line:match(" *(.-):"))
187 end
188 return devices
189 end
190
191 -- Returns the MAC-Address belonging to the given IP-Address
192 function net.ip4mac(ip)
193 local mac = nil
194
195 for i, l in ipairs(net.arptable()) do
196 if l["IP address"] == ip then
197 mac = l["HW address"]
198 end
199 end
200
201 return mac
202 end
203
204 -- Returns the prefix to a given netmask
205 function net.mask4prefix(mask)
206 local bin = net.ip4bin(mask)
207
208 if not bin then
209 return nil
210 end
211
212 return #luci.util.split(bin, "1")-1
213 end
214
215 -- Returns the kernel routing table
216 function net.routes()
217 return _parse_delimited_table(io.lines("/proc/net/route"))
218 end
219
220 -- Returns the numeric IP to a given hexstring
221 function net.hexip4(hex, be)
222 if #hex ~= 8 then
223 return nil
224 end
225
226 be = be or bigendian()
227
228 local hexdec = luci.bits.Hex2Dec
229
230 local ip = ""
231 if be then
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)))
236 else
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)))
241 end
242
243 return ip
244 end
245
246 -- Returns the binary IP to a given IP
247 function net.ip4bin(ip)
248 local parts = luci.util.split(ip, '.')
249 if #parts ~= 4 then
250 return nil
251 end
252
253 local decbin = luci.bits.Dec2Bin
254
255 local bin = ""
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)
260
261 return bin
262 end
263
264 -- Tests whether a host is pingable
265 function net.pingtest(host)
266 return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
267 end
268
269
270 process = {}
271 process.info = posix.getpid
272
273 -- Sets the gid of a process
274 function process.setgroup(pid, gid)
275 return posix.setpid("g", pid, gid)
276 end
277
278 -- Sets the uid of a process
279 function process.setuser(pid, uid)
280 return posix.setpid("u", pid, uid)
281 end
282
283 user = {}
284 -- returns user information to a given uid
285 user.getuser = posix.getpasswd
286
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)
290
291 -- FIXME: detect testing environment
292 if luci.fs.stat("/etc/shadow") and not luci.fs.access("/etc/shadow", "r") then
293 return true
294 elseif account then
295 if account.passwd == "!" then
296 return true
297 else
298 return (account.passwd == posix.crypt(password, account.passwd))
299 end
300 end
301 end
302
303 -- Changes the user password of given user
304 function user.setpasswd(user, pwd)
305 if pwd then
306 pwd = pwd:gsub("'", "")
307 end
308
309 if user then
310 user = user:gsub("'", "")
311 end
312
313 local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|"
314 cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1"
315 return os.execute(cmd)
316 end
317
318
319 wifi = {}
320
321 function wifi.getiwconfig()
322 local cnt = exec("/usr/sbin/iwconfig 2>/dev/null")
323 local iwc = {}
324
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)
328 if k then
329 iwc[k] = _parse_mixed_record(l)
330 end
331 end
332
333 return iwc
334 end
335
336 function wifi.iwscan()
337 local cnt = exec("iwlist scan 2>/dev/null")
338 local iws = {}
339
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)
344 if k then
345 iws[k] = {}
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))
351 end
352 end
353 end
354
355 return iws
356 end
357
358
359 -- Internal functions
360
361 function _parse_delimited_table(iter, delimiter)
362 delimiter = delimiter or "%s+"
363
364 local data = {}
365 local trim = luci.util.trim
366 local split = luci.util.split
367
368 local keys = split(trim(iter()), delimiter, nil, true)
369 for i, j in pairs(keys) do
370 keys[i] = trim(keys[i])
371 end
372
373 for line in iter do
374 local row = {}
375 line = trim(line)
376 if #line > 0 then
377 for i, j in pairs(split(line, delimiter, nil, true)) do
378 if keys[i] then
379 row[keys[i]] = j
380 end
381 end
382 end
383 table.insert(data, row)
384 end
385
386 return data
387 end
388
389 function _parse_mixed_record(cnt)
390 local data = {}
391
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"]*)"*')
395
396 if k then
397 if x == "" then
398 table.insert(data, k)
399 else
400 data[k] = v
401 end
402 end
403 end
404 end
405
406 return data
407 end