* libs/web: Reworked authentication
[project/luci.git] / libs / sys / 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 --- LuCI Linux and POSIX system utilities.
28 module("luci.sys", package.seeall)
29 require("posix")
30 require("luci.bits")
31 require("luci.util")
32 require("luci.fs")
33 require("luci.ip")
34
35 --- Invoke the luci-flash executable to write an image to the flash memory.
36 -- @param kpattern Pattern of files to keep over flash process
37 -- @return Return value of os.execute()
38 function flash(image, kpattern)
39 local cmd = "luci-flash "
40 if kpattern then
41 cmd = cmd .. "-k '" .. kpattern:gsub("'", "") .. "' "
42 end
43 cmd = cmd .. "'" .. image:gsub("'", "") .. "' >/dev/null 2>&1"
44
45 return os.execute(cmd)
46 end
47
48 --- Retrieve environment variables. If no variable is given then a table
49 -- containing the whole environment is returned otherwise this function returns
50 -- the corresponding string value for the given name or nil if no such variable
51 -- exists.
52 -- @class function
53 -- @name getenv
54 -- @param var Name of the environment variable to retrieve (optional)
55 -- @return String containg the value of the specified variable
56 -- @return Table containing all variables if no variable name is given
57 getenv = posix.getenv
58
59 --- Determine the current hostname.
60 -- @return String containing the system hostname
61 function hostname()
62 return io.lines("/proc/sys/kernel/hostname")()
63 end
64
65 --- Returns the contents of a documented referred by an URL.
66 -- @param url The URL to retrieve
67 -- @param stream Return a stream instead of a buffer
68 -- @return String containing the contents of given the URL
69 function httpget(url, stream)
70 local source = stream and io.open or luci.util.exec
71 return source("wget -qO- '"..url:gsub("'", "").."'")
72 end
73
74 --- Returns the system load average values.
75 -- @return String containing the average load value 1 minute ago
76 -- @return String containing the average load value 5 minutes ago
77 -- @return String containing the average load value 15 minutes ago
78 -- @return String containing the active and total number of processes
79 -- @return String containing the last used pid
80 function loadavg()
81 local loadavg = io.lines("/proc/loadavg")()
82 return loadavg:match("^(.-) (.-) (.-) (.-) (.-)$")
83 end
84
85 --- Initiate a system reboot.
86 -- @return Return value of os.execute()
87 function reboot()
88 return os.execute("reboot >/dev/null 2>&1")
89 end
90
91 --- Returns the system type, cpu name and installed physical memory.
92 -- @return String containing the system or platform identifier
93 -- @return String containing hardware model information
94 -- @return String containing the total memory amount in kB
95 -- @return String containing the memory used for caching in kB
96 -- @return String containing the memory used for buffering in kB
97 -- @return String containing the free memory amount in kB
98 -- @return Number containing free memory in percent
99 -- @return Number containing buffer memory in percent
100 -- @return Number containing cache memory in percent
101 function sysinfo()
102 local c1 = "cat /proc/cpuinfo|grep system\\ typ|cut -d: -f2 2>/dev/null"
103 local c2 = "uname -m 2>/dev/null"
104 local c3 = "cat /proc/cpuinfo|grep model\\ name|cut -d: -f2 2>/dev/null"
105 local c4 = "cat /proc/cpuinfo|grep cpu\\ model|cut -d: -f2 2>/dev/null"
106 local c5 = "cat /proc/meminfo|grep MemTotal|awk {' print $2 '} 2>/dev/null"
107 local c6 = "cat /proc/meminfo|grep ^Cached|awk {' print $2 '} 2>/dev/null"
108 local c7 = "cat /proc/meminfo|grep MemFree|awk {' print $2 '} 2>/dev/null"
109 local c8 = "cat /proc/meminfo|grep Buffers|awk {' print $2 '} 2>/dev/null"
110
111 local system = luci.util.trim(luci.util.exec(c1))
112 local model = ""
113 local memtotal = luci.util.trim(luci.util.exec(c5))
114 local memcached = luci.util.trim(luci.util.exec(c6))
115 local memfree = luci.util.trim(luci.util.exec(c7))
116 local membuffers = luci.util.trim(luci.util.exec(c8))
117 local perc_memfree = math.floor((memfree/memtotal)*100)
118 local perc_membuffers = math.floor((membuffers/memtotal)*100)
119 local perc_memcached = math.floor((memcached/memtotal)*100)
120
121 if system == "" then
122 system = luci.util.trim(luci.util.exec(c2))
123 model = luci.util.trim(luci.util.exec(c3))
124 else
125 model = luci.util.trim(luci.util.exec(c4))
126 end
127
128 return system, model, memtotal, memcached, membuffers, memfree, perc_memfree, perc_membuffers, perc_memcached
129 end
130
131 --- Retrieves the output of the "logread" command.
132 -- @return String containing the current log buffer
133 function syslog()
134 return luci.util.exec("logread")
135 end
136
137 --- Generates a random id with specified length.
138 -- @param bytes Number of bytes for the unique id
139 -- @return String containing hex encoded id
140 function uniqueid(bytes)
141 local fp = io.open("/dev/urandom")
142 local chunk = { fp:read(bytes):byte(1, bytes) }
143 fp:close()
144
145 local hex = ""
146
147 local pattern = "%02X"
148 for i, byte in ipairs(chunk) do
149 hex = hex .. pattern:format(byte)
150 end
151
152 return hex
153 end
154
155 --- Returns the current system uptime stats.
156 -- @return String containing total uptime in seconds
157 -- @return String containing idle time in seconds
158 function uptime()
159 local loadavg = io.lines("/proc/uptime")()
160 return loadavg:match("^(.-) (.-)$")
161 end
162
163 --- LuCI system utilities / POSIX user group related functions.
164 -- @class module
165 -- @name luci.sys.group
166 group = {}
167
168 --- Returns information about a POSIX user group.
169 -- @param group Group ID or name of a system user group
170 -- @return Table with information about the requested group
171 group.getgroup = posix.getgroup
172
173
174 --- LuCI system utilities / network related functions.
175 -- @class module
176 -- @name luci.sys.net
177 net = {}
178
179 --- Returns the current arp-table entries as two-dimensional table.
180 -- @return Table of table containing the current arp entries.
181 -- The following fields are defined for arp entry objects:
182 -- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
183 function net.arptable()
184 return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+")
185 end
186
187 --- Determine the current default route.
188 -- @return Table with the properties of the current default route.
189 -- The following fields are defined:
190 -- { "Mask", "RefCnt", "Iface", "Flags", "Window", "IRTT",
191 -- "MTU", "Gateway", "Destination", "Metric", "Use" }
192 function net.defaultroute()
193 local routes = net.routes()
194 local route = nil
195
196 for i, r in pairs(luci.sys.net.routes()) do
197 if r.Destination == "00000000" and (not route or route.Metric > r.Metric) then
198 route = r
199 end
200 end
201
202 return route
203 end
204
205 --- Determine the names of available network interfaces.
206 -- @return Table containing all current interface names
207 function net.devices()
208 local devices = {}
209 for line in io.lines("/proc/net/dev") do
210 table.insert(devices, line:match(" *(.-):"))
211 end
212 return devices
213 end
214
215 -- Determine the MAC address belonging to the given IP address.
216 -- @param ip IPv4 address
217 -- @return String containing the MAC address or nil if it cannot be found
218 function net.ip4mac(ip)
219 local mac = nil
220
221 for i, l in ipairs(net.arptable()) do
222 if l["IP address"] == ip then
223 mac = l["HW address"]
224 end
225 end
226
227 return mac
228 end
229
230 --- Returns the current kernel routing table entries.
231 -- @return Table of tables with properties of the corresponding routes.
232 -- The following fields are defined for route entry tables:
233 -- { "Mask", "RefCnt", "Iface", "Flags", "Window", "IRTT",
234 -- "MTU", "Gateway", "Destination", "Metric", "Use" }
235 function net.routes()
236 return _parse_delimited_table(io.lines("/proc/net/route"))
237 end
238
239
240 --- Tests whether the given host responds to ping probes.
241 -- @param host String containing a hostname or IPv4 address
242 -- @return Number containing 0 on success and >= 1 on error
243 function net.pingtest(host)
244 return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
245 end
246
247
248 --- LuCI system utilities / process related functions.
249 -- @class module
250 -- @name luci.sys.process
251 process = {}
252
253 --- Get the current process id.
254 -- @return Number containing the current pid
255 process.info = posix.getpid
256
257 --- Set the gid of a process identified by given pid.
258 -- @param pid Number containing the process id
259 -- @param gid Number containing the Unix group id
260 -- @return Boolean indicating successful operation
261 -- @return String containing the error message if failed
262 -- @return Number containing the error code if failed
263 function process.setgroup(pid, gid)
264 return posix.setpid("g", pid, gid)
265 end
266
267 --- Set the uid of a process identified by given pid.
268 -- @param pid Number containing the process id
269 -- @param uid Number containing the Unix user id
270 -- @return Boolean indicating successful operation
271 -- @return String containing the error message if failed
272 -- @return Number containing the error code if failed
273 function process.setuser(pid, uid)
274 return posix.setpid("u", pid, uid)
275 end
276
277
278 --- LuCI system utilities / user related functions.
279 -- @class module
280 -- @name luci.sys.user
281 user = {}
282
283 --- Retrieve user informations for given uid.
284 -- @class function
285 -- @name getuser
286 -- @param uid Number containing the Unix user id
287 -- @return Table containing the following fields:
288 -- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
289 user.getuser = posix.getpasswd
290
291 --- Test whether given string matches the password of a given system user.
292 -- @param username String containing the Unix user name
293 -- @param password String containing the password to compare
294 -- @return Boolean indicating wheather the passwords are equal
295 function user.checkpasswd(username, password)
296 local account = user.getuser(username)
297
298 if account then
299 if account.passwd == "!" then
300 return true
301 else
302 return (account.passwd == posix.crypt(password, account.passwd))
303 end
304 end
305 end
306
307 --- Change the password of given user.
308 -- @param username String containing the Unix user name
309 -- @param password String containing the password to compare
310 -- @return Number containing 0 on success and >= 1 on error
311 function user.setpasswd(username, password)
312 if password then
313 password = password:gsub("'", "")
314 end
315
316 if username then
317 username = username:gsub("'", "")
318 end
319
320 local cmd = "(echo '"..password.."';sleep 1;echo '"..password.."')|"
321 cmd = cmd .. "passwd '"..username.."' >/dev/null 2>&1"
322 return os.execute(cmd)
323 end
324
325
326 --- LuCI system utilities / wifi related functions.
327 -- @class module
328 -- @name luci.sys.wifi
329 wifi = {}
330
331 --- Get iwconfig output for all wireless devices.
332 -- @return Table of tables containing the iwconfing output for each wifi device
333 function wifi.getiwconfig()
334 local cnt = luci.util.exec("/usr/sbin/iwconfig 2>/dev/null")
335 local iwc = {}
336
337 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
338 local k = l:match("^(.-) ")
339 l = l:gsub("^(.-) +", "", 1)
340 if k then
341 iwc[k] = _parse_mixed_record(l)
342 end
343 end
344
345 return iwc
346 end
347
348 --- Get iwlist scan output from all wireless devices.
349 -- @return Table of tables contaiing all scan results
350 function wifi.iwscan()
351 local cnt = luci.util.exec("iwlist scan 2>/dev/null")
352 local iws = {}
353
354 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
355 local k = l:match("^(.-) ")
356 l = l:gsub("^[^\n]+", "", 1)
357 l = luci.util.trim(l)
358 if k then
359 iws[k] = {}
360 for j, c in pairs(luci.util.split(l, "\n Cell")) do
361 c = c:gsub("^(.-)- ", "", 1)
362 c = luci.util.split(c, "\n", 7)
363 c = table.concat(c, "\n", 1)
364 table.insert(iws[k], _parse_mixed_record(c))
365 end
366 end
367 end
368
369 return iws
370 end
371
372
373 -- Internal functions
374
375 function _parse_delimited_table(iter, delimiter)
376 delimiter = delimiter or "%s+"
377
378 local data = {}
379 local trim = luci.util.trim
380 local split = luci.util.split
381
382 local keys = split(trim(iter()), delimiter, nil, true)
383 for i, j in pairs(keys) do
384 keys[i] = trim(keys[i])
385 end
386
387 for line in iter do
388 local row = {}
389 line = trim(line)
390 if #line > 0 then
391 for i, j in pairs(split(line, delimiter, nil, true)) do
392 if keys[i] then
393 row[keys[i]] = j
394 end
395 end
396 end
397 table.insert(data, row)
398 end
399
400 return data
401 end
402
403 function _parse_mixed_record(cnt)
404 local data = {}
405
406 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
407 for j, f in pairs(luci.util.split(luci.util.trim(l), " ")) do
408 local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*')
409
410 if k then
411 if x == "" then
412 table.insert(data, k)
413 else
414 data[k] = v
415 end
416 end
417 end
418 end
419
420 return data
421 end