* libs/httpd: Introduced keep-alive and pipelining 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(user, password)
289         local account = user.getuser(user)
290         if posix.crypt and account then
291                 return (account.passwd == posix.crypt(account.passwd, password))
292         end
293 end
294         
295 -- Changes the user password of given user
296 function user.setpasswd(user, pwd)
297         if pwd then
298                 pwd = pwd:gsub("'", "")
299         end
300         
301         if user then
302                 user = user:gsub("'", "")
303         end
304         
305         local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|"
306         cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1"
307         return os.execute(cmd)
308 end
309
310
311 wifi = {}
312
313 function wifi.getiwconfig()
314         local cnt = exec("/usr/sbin/iwconfig 2>/dev/null")
315         local iwc = {}
316         
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)
320                 if k then
321                         iwc[k] = _parse_mixed_record(l)
322                 end
323         end
324         
325         return iwc      
326 end
327
328 function wifi.iwscan()
329         local cnt = exec("iwlist scan 2>/dev/null")
330         local iws = {}
331         
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)
336                 if k then
337                         iws[k] = {}
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))
343                         end
344                 end
345         end
346         
347         return iws      
348 end
349
350
351 -- Internal functions
352
353 function _parse_delimited_table(iter, delimiter)
354         delimiter = delimiter or "%s+"
355         
356         local data  = {}
357         local trim  = luci.util.trim
358         local split = luci.util.split
359         
360         local keys = split(trim(iter()), delimiter, nil, true)
361         for i, j in pairs(keys) do
362                 keys[i] = trim(keys[i])
363         end
364         
365         for line in iter do
366                 local row = {}
367                 line = trim(line)
368                 if #line > 0 then
369                         for i, j in pairs(split(line, delimiter, nil, true)) do
370                                 if keys[i] then
371                                         row[keys[i]] = j
372                                 end
373                         end
374                 end
375                 table.insert(data, row)
376         end
377         
378         return data
379 end
380
381 function _parse_mixed_record(cnt)
382         local data = {}
383         
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"]*)"*')
387
388             if k then
389                                 if x == "" then
390                                         table.insert(data, k)                           
391                                 else
392                         data[k] = v
393                                 end
394             end
395         end
396         end
397                 
398     return data
399 end