80d702b942af7f659a0f568ed97ab279cefea2b2
[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 -- checks whether a string matches the password of a certain system user
268 function user.checkpasswd(user, password)
269 local account = user.getuser(user)
270 if posix.crypt and account then
271 return (account.passwd == posix.crypt(account.passwd, password))
272 end
273 end
274
275 -- Changes the user password of given user
276 function user.setpasswd(user, pwd)
277 if pwd then
278 pwd = pwd:gsub("'", "")
279 end
280
281 if user then
282 user = user:gsub("'", "")
283 end
284
285 local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|"
286 cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1"
287 return os.execute(cmd)
288 end
289
290
291 wifi = {}
292
293 function wifi.getiwconfig()
294 local cnt = exec("/usr/sbin/iwconfig 2>/dev/null")
295 local iwc = {}
296
297 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
298 local k = l:match("^(.-) ")
299 l = l:gsub("^(.-) +", "", 1)
300 if k then
301 iwc[k] = _parse_mixed_record(l)
302 end
303 end
304
305 return iwc
306 end
307
308 function wifi.iwscan()
309 local cnt = exec("iwlist scan 2>/dev/null")
310 local iws = {}
311
312 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
313 local k = l:match("^(.-) ")
314 l = l:gsub("^[^\n]+", "", 1)
315 l = luci.util.trim(l)
316 if k then
317 iws[k] = {}
318 for j, c in pairs(luci.util.split(l, "\n Cell")) do
319 c = c:gsub("^(.-)- ", "", 1)
320 c = luci.util.split(c, "\n", 7)
321 c = table.concat(c, "\n", 1)
322 table.insert(iws[k], _parse_mixed_record(c))
323 end
324 end
325 end
326
327 return iws
328 end
329
330
331 -- Internal functions
332
333 function _parse_delimited_table(iter, delimiter)
334 delimiter = delimiter or "%s+"
335
336 local data = {}
337 local trim = luci.util.trim
338 local split = luci.util.split
339
340 local keys = split(trim(iter()), delimiter, nil, true)
341 for i, j in pairs(keys) do
342 keys[i] = trim(keys[i])
343 end
344
345 for line in iter do
346 local row = {}
347 line = trim(line)
348 if #line > 0 then
349 for i, j in pairs(split(line, delimiter, nil, true)) do
350 if keys[i] then
351 row[keys[i]] = j
352 end
353 end
354 end
355 table.insert(data, row)
356 end
357
358 return data
359 end
360
361 function _parse_mixed_record(cnt)
362 local data = {}
363
364 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
365 for j, f in pairs(luci.util.split(luci.util.trim(l), " ")) do
366 local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*')
367
368 if k then
369 if x == "" then
370 table.insert(data, k)
371 else
372 data[k] = v
373 end
374 end
375 end
376 end
377
378 return data
379 end