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