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