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