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