* Separated CBI from LuCI Web
[project/luci.git] / core / src / 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