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