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