* Mördercommit ;-)
[project/luci.git] / core / src / sys.lua
1 --[[
2 FFLuCI - 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("ffluci.sys", package.seeall)
28 require("posix")
29 require("ffluci.bits")
30 require("ffluci.util")
31
32 -- Returns whether a system is bigendian
33 function bigendian()
34         local fp = io.open("/bin/sh")
35         fp:seek("set", 5)
36         return (fp:read(1):byte() ~= 1)
37 end
38
39 -- Runs "command" and returns its output
40 function exec(command)
41         local pp   = io.popen(command)
42         local data = pp:read("*a")
43         pp:close()
44         
45         return data
46 end
47
48 -- Runs "command" and returns its output as a array of lines
49 function execl(command)
50         local pp   = io.popen(command)  
51         local line = ""
52         local data = {}
53         
54         while true do
55                 line = pp:read()
56                 if (line == nil) then break end
57                 table.insert(data, line)
58         end 
59         pp:close()      
60         
61         return data
62 end
63
64 -- Uses "ffluci-flash" to flash a new image file to the system
65 function flash(image, kpattern)
66         local cmd = "ffluci-flash "
67         if kpattern then
68                 cmd = cmd .. "-k '" .. kpattern:gsub("'", "") .. "' "
69         end
70         cmd = cmd .. "'" .. image:gsub("'", "") .. "' >/dev/null 2>&1"
71         
72         return os.execute(cmd)
73 end
74
75 -- Returns the hostname
76 function hostname()
77         return io.lines("/proc/sys/kernel/hostname")()
78 end
79
80 -- Returns the contents of a documented referred by an URL
81 function httpget(url)
82         return exec("wget -qO- '"..url:gsub("'", "").."'")
83 end
84
85 -- Returns the FFLuci-Basedir
86 function libpath()
87         return ffluci.fs.dirname(require("ffluci.debug").__file__)
88 end
89
90 -- Returns the load average
91 function loadavg()
92         local loadavg = io.lines("/proc/loadavg")()
93         return loadavg:match("^(.-) (.-) (.-) (.-) (.-)$")
94 end
95
96 -- Reboots the system
97 function reboot()
98         return os.execute("reboot >/dev/null 2>&1")
99 end
100
101 -- Returns the system type, cpu name, and installed physical memory
102 function sysinfo()
103         local c1 = "cat /proc/cpuinfo|grep system\\ typ|cut -d: -f2 2>/dev/null"
104         local c2 = "uname -m 2>/dev/null"
105         local c3 = "cat /proc/cpuinfo|grep model\\ name|cut -d: -f2 2>/dev/null"
106         local c4 = "cat /proc/cpuinfo|grep cpu\\ model|cut -d: -f2 2>/dev/null"
107         local c5 = "cat /proc/meminfo|grep MemTotal|cut -d: -f2 2>/dev/null"
108         
109         local s = ffluci.util.trim(exec(c1))
110         local m = ""
111         local r = ""
112         
113         if s == "" then
114                 s = ffluci.util.trim(exec(c2))
115                 m = ffluci.util.trim(exec(c3))
116         else
117                 m = ffluci.util.trim(exec(c4))
118         end
119         
120         r = ffluci.util.trim(exec(c5))
121         
122         return s, m, r
123 end
124
125
126 group = {}
127 group.getgroup = posix.getgroup
128
129 net = {}
130 -- Returns the ARP-Table
131 function net.arptable()
132         return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+")
133 end
134
135 -- Returns whether an IP-Adress belongs to a certain net
136 function net.belongs(ip, ipnet, prefix)
137         return (net.ip4bin(ip):sub(1, prefix) == net.ip4bin(ipnet):sub(1, prefix))
138 end
139
140 -- Detect the default route
141 function net.defaultroute()
142         local routes = net.routes()
143         local route = nil
144         
145         for i, r in pairs(ffluci.sys.net.routes()) do
146                 if r.Destination == "00000000" and (not route or route.Metric > r.Metric) then
147                         route = r
148                 end
149         end
150         
151         return route
152 end
153
154 -- Returns all available network interfaces
155 function net.devices()
156         local devices = {}
157         for line in io.lines("/proc/net/dev") do
158                 table.insert(devices, line:match(" *(.-):"))
159         end
160         return devices
161 end
162
163 -- Returns the MAC-Address belonging to the given IP-Address
164 function net.ip4mac(ip)
165         local mac = nil
166         
167         for i, l in ipairs(net.arptable()) do
168                 if l["IP address"] == ip then
169                         mac = l["HW address"]
170                 end
171         end
172         
173         return mac
174 end
175
176 -- Returns the prefix to a given netmask
177 function net.mask4prefix(mask)
178         local bin = net.ip4bin(mask)
179         
180         if not bin then
181                 return nil
182         end
183         
184         return #ffluci.util.split(bin, "1")-1
185 end
186
187 -- Returns the kernel routing table
188 function net.routes()
189         return _parse_delimited_table(io.lines("/proc/net/route"))
190 end
191
192 -- Returns the numeric IP to a given hexstring
193 function net.hexip4(hex, be)
194         if #hex ~= 8 then
195                 return nil
196         end
197         
198         be = be or bigendian()
199         
200         local hexdec = ffluci.bits.Hex2Dec
201         
202         local ip = ""
203         if be then
204                 ip = ip .. tostring(hexdec(hex:sub(1,2))) .. "."
205                 ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "."
206                 ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "."
207                 ip = ip .. tostring(hexdec(hex:sub(7,8)))
208         else
209                 ip = ip .. tostring(hexdec(hex:sub(7,8))) .. "."
210                 ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "."
211                 ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "."
212                 ip = ip .. tostring(hexdec(hex:sub(1,2)))
213         end
214         
215         return ip
216 end
217
218 -- Returns the binary IP to a given IP
219 function net.ip4bin(ip)
220         local parts = ffluci.util.split(ip, '.')
221         if #parts ~= 4 then
222                 return nil
223         end
224         
225         local decbin = ffluci.bits.Dec2Bin
226         
227         local bin = ""
228         bin = bin .. decbin(parts[1], 8)
229         bin = bin .. decbin(parts[2], 8)
230         bin = bin .. decbin(parts[3], 8)
231         bin = bin .. decbin(parts[4], 8)
232         
233         return bin
234 end
235
236 -- Tests whether a host is pingable
237 function net.pingtest(host)
238         return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
239 end
240
241
242 process = {}
243 process.info = posix.getpid 
244
245 -- Sets the gid of a process
246 function process.setgroup(pid, gid)
247         return posix.setpid("g", pid, gid)
248 end
249
250 -- Sets the uid of a process
251 function process.setuser(pid, uid)
252         return posix.setpid("u", pid, uid)
253 end
254
255 user = {}
256 -- returns user information to a given uid
257 user.getuser = posix.getpasswd
258         
259 -- Changes the user password of given user
260 function user.setpasswd(user, pwd)
261         if pwd then
262                 pwd = pwd:gsub("'", "")
263         end
264         
265         if user then
266                 user = user:gsub("'", "")
267         end
268         
269         local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|"
270         cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1"
271         return os.execute(cmd)
272 end
273
274
275 wifi = {}
276
277 function wifi.getiwconfig()
278         local cnt = exec("/usr/sbin/iwconfig 2>/dev/null")
279         local iwc = {}
280         
281         for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n\n")) do
282                 local k = l:match("^(.-) ")
283                 l = l:gsub("^(.-) +", "", 1)
284                 if k then
285                         iwc[k] = _parse_mixed_record(l)
286                 end
287         end
288         
289         return iwc      
290 end
291
292 function wifi.iwscan()
293         local cnt = exec("iwlist scan 2>/dev/null")
294         local iws = {}
295         
296         for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n\n")) do
297                 local k = l:match("^(.-) ")
298                 l = l:gsub("^[^\n]+", "", 1)
299                 l = ffluci.util.trim(l)
300                 if k then
301                         iws[k] = {}
302                         for j, c in pairs(ffluci.util.split(l, "\n          Cell")) do
303                                 c = c:gsub("^(.-)- ", "", 1)
304                                 c = ffluci.util.split(c, "\n", 7)
305                                 c = table.concat(c, "\n", 1)
306                                 table.insert(iws[k], _parse_mixed_record(c))
307                         end
308                 end
309         end
310         
311         return iws      
312 end
313
314
315 -- Internal functions
316
317 function _parse_delimited_table(iter, delimiter)
318         delimiter = delimiter or "%s+"
319         
320         local data  = {}
321         local trim  = ffluci.util.trim
322         local split = ffluci.util.split
323         
324         local keys = split(trim(iter()), delimiter, nil, true)
325         for i, j in pairs(keys) do
326                 keys[i] = trim(keys[i])
327         end
328         
329         for line in iter do
330                 local row = {}
331                 line = trim(line)
332                 if #line > 0 then
333                         for i, j in pairs(split(line, delimiter, nil, true)) do
334                                 if keys[i] then
335                                         row[keys[i]] = j
336                                 end
337                         end
338                 end
339                 table.insert(data, row)
340         end
341         
342         return data
343 end
344
345 function _parse_mixed_record(cnt)
346         local data = {}
347         
348         for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n")) do
349         for j, f in pairs(ffluci.util.split(ffluci.util.trim(l), "  ")) do
350                 local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*')
351
352             if k then
353                                 if x == "" then
354                                         table.insert(data, k)                           
355                                 else
356                         data[k] = v
357                                 end
358             end
359         end
360         end
361                 
362     return data
363 end