luci-mod-base: drop luci.sys.sysinfo() and luci.sys.loadavg()
[project/luci.git] / modules / luci-base / 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
28 local io = require "io"
29 local os = require "os"
30 local table = require "table"
31 local nixio = require "nixio"
32 local fs = require "nixio.fs"
33 local uci = require "luci.model.uci"
34
35 local luci = {}
36 luci.util = require "luci.util"
37 luci.ip = require "luci.ip"
38
39 local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select =
40 tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
41
42
43 --- LuCI Linux and POSIX system utilities.
44 module "luci.sys"
45
46 --- Execute a given shell command and return the error code
47 -- @class function
48 -- @name call
49 -- @param ... Command to call
50 -- @return Error code of the command
51 function call(...)
52 return os.execute(...) / 256
53 end
54
55 --- Execute a given shell command and capture its standard output
56 -- @class function
57 -- @name exec
58 -- @param command Command to call
59 -- @return String containg the return the output of the command
60 exec = luci.util.exec
61
62 --- Retrieve information about currently mounted file systems.
63 -- @return Table containing mount information
64 function mounts()
65 local data = {}
66 local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"}
67 local ps = luci.util.execi("df")
68
69 if not ps then
70 return
71 else
72 ps()
73 end
74
75 for line in ps do
76 local row = {}
77
78 local j = 1
79 for value in line:gmatch("[^%s]+") do
80 row[k[j]] = value
81 j = j + 1
82 end
83
84 if row[k[1]] then
85
86 -- this is a rather ugly workaround to cope with wrapped lines in
87 -- the df output:
88 --
89 -- /dev/scsi/host0/bus0/target0/lun0/part3
90 -- 114382024 93566472 15005244 86% /mnt/usb
91 --
92
93 if not row[k[2]] then
94 j = 2
95 line = ps()
96 for value in line:gmatch("[^%s]+") do
97 row[k[j]] = value
98 j = j + 1
99 end
100 end
101
102 table.insert(data, row)
103 end
104 end
105
106 return data
107 end
108
109 --- Retrieve environment variables. If no variable is given then a table
110 -- containing the whole environment is returned otherwise this function returns
111 -- the corresponding string value for the given name or nil if no such variable
112 -- exists.
113 -- @class function
114 -- @name getenv
115 -- @param var Name of the environment variable to retrieve (optional)
116 -- @return String containg the value of the specified variable
117 -- @return Table containing all variables if no variable name is given
118 getenv = nixio.getenv
119
120 --- Get or set the current hostname.
121 -- @param String containing a new hostname to set (optional)
122 -- @return String containing the system hostname
123 function hostname(newname)
124 if type(newname) == "string" and #newname > 0 then
125 fs.writefile( "/proc/sys/kernel/hostname", newname )
126 return newname
127 else
128 return nixio.uname().nodename
129 end
130 end
131
132 --- Returns the contents of a documented referred by an URL.
133 -- @param url The URL to retrieve
134 -- @param stream Return a stream instead of a buffer
135 -- @param target Directly write to target file name
136 -- @return String containing the contents of given the URL
137 function httpget(url, stream, target)
138 if not target then
139 local source = stream and io.popen or luci.util.exec
140 return source("wget -qO- '"..url:gsub("'", "").."'")
141 else
142 return os.execute("wget -qO '%s' '%s'" %
143 {target:gsub("'", ""), url:gsub("'", "")})
144 end
145 end
146
147 --- Initiate a system reboot.
148 -- @return Return value of os.execute()
149 function reboot()
150 return os.execute("reboot >/dev/null 2>&1")
151 end
152
153 --- Retrieves the output of the "logread" command.
154 -- @return String containing the current log buffer
155 function syslog()
156 return luci.util.exec("logread")
157 end
158
159 --- Retrieves the output of the "dmesg" command.
160 -- @return String containing the current log buffer
161 function dmesg()
162 return luci.util.exec("dmesg")
163 end
164
165 --- Generates a random id with specified length.
166 -- @param bytes Number of bytes for the unique id
167 -- @return String containing hex encoded id
168 function uniqueid(bytes)
169 local rand = fs.readfile("/dev/urandom", bytes)
170 return rand and nixio.bin.hexlify(rand)
171 end
172
173 --- Returns the current system uptime stats.
174 -- @return String containing total uptime in seconds
175 function uptime()
176 return nixio.sysinfo().uptime
177 end
178
179
180 --- LuCI system utilities / network related functions.
181 -- @class module
182 -- @name luci.sys.net
183 net = {}
184
185 --- Returns the current arp-table entries as two-dimensional table.
186 -- @return Table of table containing the current arp entries.
187 -- The following fields are defined for arp entry objects:
188 -- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
189 function net.arptable(callback)
190 local arp = (not callback) and {} or nil
191 local e, r, v
192 if fs.access("/proc/net/arp") then
193 for e in io.lines("/proc/net/arp") do
194 local r = { }, v
195 for v in e:gmatch("%S+") do
196 r[#r+1] = v
197 end
198
199 if r[1] ~= "IP" then
200 local x = {
201 ["IP address"] = r[1],
202 ["HW type"] = r[2],
203 ["Flags"] = r[3],
204 ["HW address"] = r[4],
205 ["Mask"] = r[5],
206 ["Device"] = r[6]
207 }
208
209 if callback then
210 callback(x)
211 else
212 arp = arp or { }
213 arp[#arp+1] = x
214 end
215 end
216 end
217 end
218 return arp
219 end
220
221 local function _nethints(what, callback)
222 local _, k, e, mac, ip, name
223 local cur = uci.cursor()
224 local ifn = { }
225 local hosts = { }
226
227 local function _add(i, ...)
228 local k = select(i, ...)
229 if k then
230 if not hosts[k] then hosts[k] = { } end
231 hosts[k][1] = select(1, ...) or hosts[k][1]
232 hosts[k][2] = select(2, ...) or hosts[k][2]
233 hosts[k][3] = select(3, ...) or hosts[k][3]
234 hosts[k][4] = select(4, ...) or hosts[k][4]
235 end
236 end
237
238 if fs.access("/proc/net/arp") then
239 for e in io.lines("/proc/net/arp") do
240 ip, mac = e:match("^([%d%.]+)%s+%S+%s+%S+%s+([a-fA-F0-9:]+)%s+")
241 if ip and mac then
242 _add(what, mac:upper(), ip, nil, nil)
243 end
244 end
245 end
246
247 if fs.access("/etc/ethers") then
248 for e in io.lines("/etc/ethers") do
249 mac, ip = e:match("^([a-f0-9]%S+) (%S+)")
250 if mac and ip then
251 _add(what, mac:upper(), ip, nil, nil)
252 end
253 end
254 end
255
256 if fs.access("/var/dhcp.leases") then
257 for e in io.lines("/var/dhcp.leases") do
258 mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)")
259 if mac and ip then
260 _add(what, mac:upper(), ip, nil, name ~= "*" and name)
261 end
262 end
263 end
264
265 cur:foreach("dhcp", "host",
266 function(s)
267 for mac in luci.util.imatch(s.mac) do
268 _add(what, mac:upper(), s.ip, nil, s.name)
269 end
270 end)
271
272 for _, e in ipairs(nixio.getifaddrs()) do
273 if e.name ~= "lo" then
274 ifn[e.name] = ifn[e.name] or { }
275 if e.family == "packet" and e.addr and #e.addr == 17 then
276 ifn[e.name][1] = e.addr:upper()
277 elseif e.family == "inet" then
278 ifn[e.name][2] = e.addr
279 elseif e.family == "inet6" then
280 ifn[e.name][3] = e.addr
281 end
282 end
283 end
284
285 for _, e in pairs(ifn) do
286 if e[what] and (e[2] or e[3]) then
287 _add(what, e[1], e[2], e[3], e[4])
288 end
289 end
290
291 for _, e in luci.util.kspairs(hosts) do
292 callback(e[1], e[2], e[3], e[4])
293 end
294 end
295
296 --- Returns a two-dimensional table of mac address hints.
297 -- @return Table of table containing known hosts from various sources.
298 -- Each entry contains the values in the following order:
299 -- [ "mac", "name" ]
300 function net.mac_hints(callback)
301 if callback then
302 _nethints(1, function(mac, v4, v6, name)
303 name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
304 if name and name ~= mac then
305 callback(mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4)
306 end
307 end)
308 else
309 local rv = { }
310 _nethints(1, function(mac, v4, v6, name)
311 name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
312 if name and name ~= mac then
313 rv[#rv+1] = { mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 }
314 end
315 end)
316 return rv
317 end
318 end
319
320 --- Returns a two-dimensional table of IPv4 address hints.
321 -- @return Table of table containing known hosts from various sources.
322 -- Each entry contains the values in the following order:
323 -- [ "ip", "name" ]
324 function net.ipv4_hints(callback)
325 if callback then
326 _nethints(2, function(mac, v4, v6, name)
327 name = name or nixio.getnameinfo(v4, nil, 100) or mac
328 if name and name ~= v4 then
329 callback(v4, name)
330 end
331 end)
332 else
333 local rv = { }
334 _nethints(2, function(mac, v4, v6, name)
335 name = name or nixio.getnameinfo(v4, nil, 100) or mac
336 if name and name ~= v4 then
337 rv[#rv+1] = { v4, name }
338 end
339 end)
340 return rv
341 end
342 end
343
344 --- Returns a two-dimensional table of IPv6 address hints.
345 -- @return Table of table containing known hosts from various sources.
346 -- Each entry contains the values in the following order:
347 -- [ "ip", "name" ]
348 function net.ipv6_hints(callback)
349 if callback then
350 _nethints(3, function(mac, v4, v6, name)
351 name = name or nixio.getnameinfo(v6, nil, 100) or mac
352 if name and name ~= v6 then
353 callback(v6, name)
354 end
355 end)
356 else
357 local rv = { }
358 _nethints(3, function(mac, v4, v6, name)
359 name = name or nixio.getnameinfo(v6, nil, 100) or mac
360 if name and name ~= v6 then
361 rv[#rv+1] = { v6, name }
362 end
363 end)
364 return rv
365 end
366 end
367
368 --- Returns conntrack information
369 -- @return Table with the currently tracked IP connections
370 function net.conntrack(callback)
371 local connt = {}
372 if fs.access("/proc/net/nf_conntrack", "r") then
373 for line in io.lines("/proc/net/nf_conntrack") do
374 line = line:match "^(.-( [^ =]+=).-)%2"
375 local entry, flags = _parse_mixed_record(line, " +")
376 if flags[6] ~= "TIME_WAIT" then
377 entry.layer3 = flags[1]
378 entry.layer4 = flags[3]
379 for i=1, #entry do
380 entry[i] = nil
381 end
382
383 if callback then
384 callback(entry)
385 else
386 connt[#connt+1] = entry
387 end
388 end
389 end
390 elseif fs.access("/proc/net/ip_conntrack", "r") then
391 for line in io.lines("/proc/net/ip_conntrack") do
392 line = line:match "^(.-( [^ =]+=).-)%2"
393 local entry, flags = _parse_mixed_record(line, " +")
394 if flags[4] ~= "TIME_WAIT" then
395 entry.layer3 = "ipv4"
396 entry.layer4 = flags[1]
397 for i=1, #entry do
398 entry[i] = nil
399 end
400
401 if callback then
402 callback(entry)
403 else
404 connt[#connt+1] = entry
405 end
406 end
407 end
408 else
409 return nil
410 end
411 return connt
412 end
413
414 --- Determine the current IPv4 default route. If multiple default routes exist,
415 -- return the one with the lowest metric.
416 -- @return Table with the properties of the current default route.
417 -- The following fields are defined:
418 -- { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
419 -- "flags", "device" }
420 function net.defaultroute()
421 local route
422
423 net.routes(function(rt)
424 if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then
425 route = rt
426 end
427 end)
428
429 return route
430 end
431
432 --- Determine the current IPv6 default route. If multiple default routes exist,
433 -- return the one with the lowest metric.
434 -- @return Table with the properties of the current default route.
435 -- The following fields are defined:
436 -- { "source", "dest", "nexthop", "metric", "refcount", "usecount",
437 -- "flags", "device" }
438 function net.defaultroute6()
439 local route
440
441 net.routes6(function(rt)
442 if rt.dest:prefix() == 0 and rt.device ~= "lo" and
443 (not route or route.metric > rt.metric)
444 then
445 route = rt
446 end
447 end)
448
449 if not route then
450 local global_unicast = luci.ip.IPv6("2000::/3")
451 net.routes6(function(rt)
452 if rt.dest:equal(global_unicast) and
453 (not route or route.metric > rt.metric)
454 then
455 route = rt
456 end
457 end)
458 end
459
460 return route
461 end
462
463 --- Determine the names of available network interfaces.
464 -- @return Table containing all current interface names
465 function net.devices()
466 local devs = {}
467 for k, v in ipairs(nixio.getifaddrs()) do
468 if v.family == "packet" then
469 devs[#devs+1] = v.name
470 end
471 end
472 return devs
473 end
474
475
476 --- Return information about available network interfaces.
477 -- @return Table containing all current interface names and their information
478 function net.deviceinfo()
479 local devs = {}
480 for k, v in ipairs(nixio.getifaddrs()) do
481 if v.family == "packet" then
482 local d = v.data
483 d[1] = d.rx_bytes
484 d[2] = d.rx_packets
485 d[3] = d.rx_errors
486 d[4] = d.rx_dropped
487 d[5] = 0
488 d[6] = 0
489 d[7] = 0
490 d[8] = d.multicast
491 d[9] = d.tx_bytes
492 d[10] = d.tx_packets
493 d[11] = d.tx_errors
494 d[12] = d.tx_dropped
495 d[13] = 0
496 d[14] = d.collisions
497 d[15] = 0
498 d[16] = 0
499 devs[v.name] = d
500 end
501 end
502 return devs
503 end
504
505
506 -- Determine the MAC address belonging to the given IP address.
507 -- @param ip IPv4 address
508 -- @return String containing the MAC address or nil if it cannot be found
509 function net.ip4mac(ip)
510 local mac = nil
511 net.arptable(function(e)
512 if e["IP address"] == ip then
513 mac = e["HW address"]
514 end
515 end)
516 return mac
517 end
518
519 --- Returns the current kernel routing table entries.
520 -- @return Table of tables with properties of the corresponding routes.
521 -- The following fields are defined for route entry tables:
522 -- { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
523 -- "flags", "device" }
524 function net.routes(callback)
525 local routes = { }
526
527 for line in io.lines("/proc/net/route") do
528
529 local dev, dst_ip, gateway, flags, refcnt, usecnt, metric,
530 dst_mask, mtu, win, irtt = line:match(
531 "([^%s]+)\t([A-F0-9]+)\t([A-F0-9]+)\t([A-F0-9]+)\t" ..
532 "(%d+)\t(%d+)\t(%d+)\t([A-F0-9]+)\t(%d+)\t(%d+)\t(%d+)"
533 )
534
535 if dev then
536 gateway = luci.ip.Hex( gateway, 32, luci.ip.FAMILY_INET4 )
537 dst_mask = luci.ip.Hex( dst_mask, 32, luci.ip.FAMILY_INET4 )
538 dst_ip = luci.ip.Hex(
539 dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4
540 )
541
542 local rt = {
543 dest = dst_ip,
544 gateway = gateway,
545 metric = tonumber(metric),
546 refcount = tonumber(refcnt),
547 usecount = tonumber(usecnt),
548 mtu = tonumber(mtu),
549 window = tonumber(window),
550 irtt = tonumber(irtt),
551 flags = tonumber(flags, 16),
552 device = dev
553 }
554
555 if callback then
556 callback(rt)
557 else
558 routes[#routes+1] = rt
559 end
560 end
561 end
562
563 return routes
564 end
565
566 --- Returns the current ipv6 kernel routing table entries.
567 -- @return Table of tables with properties of the corresponding routes.
568 -- The following fields are defined for route entry tables:
569 -- { "source", "dest", "nexthop", "metric", "refcount", "usecount",
570 -- "flags", "device" }
571 function net.routes6(callback)
572 if fs.access("/proc/net/ipv6_route", "r") then
573 local routes = { }
574
575 for line in io.lines("/proc/net/ipv6_route") do
576
577 local dst_ip, dst_prefix, src_ip, src_prefix, nexthop,
578 metric, refcnt, usecnt, flags, dev = line:match(
579 "([a-f0-9]+) ([a-f0-9]+) " ..
580 "([a-f0-9]+) ([a-f0-9]+) " ..
581 "([a-f0-9]+) ([a-f0-9]+) " ..
582 "([a-f0-9]+) ([a-f0-9]+) " ..
583 "([a-f0-9]+) +([^%s]+)"
584 )
585
586 if dst_ip and dst_prefix and
587 src_ip and src_prefix and
588 nexthop and metric and
589 refcnt and usecnt and
590 flags and dev
591 then
592 src_ip = luci.ip.Hex(
593 src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false
594 )
595
596 dst_ip = luci.ip.Hex(
597 dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false
598 )
599
600 nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false )
601
602 local rt = {
603 source = src_ip,
604 dest = dst_ip,
605 nexthop = nexthop,
606 metric = tonumber(metric, 16),
607 refcount = tonumber(refcnt, 16),
608 usecount = tonumber(usecnt, 16),
609 flags = tonumber(flags, 16),
610 device = dev,
611
612 -- lua number is too small for storing the metric
613 -- add a metric_raw field with the original content
614 metric_raw = metric
615 }
616
617 if callback then
618 callback(rt)
619 else
620 routes[#routes+1] = rt
621 end
622 end
623 end
624
625 return routes
626 end
627 end
628
629 --- Tests whether the given host responds to ping probes.
630 -- @param host String containing a hostname or IPv4 address
631 -- @return Number containing 0 on success and >= 1 on error
632 function net.pingtest(host)
633 return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
634 end
635
636
637 --- LuCI system utilities / process related functions.
638 -- @class module
639 -- @name luci.sys.process
640 process = {}
641
642 --- Get the current process id.
643 -- @class function
644 -- @name process.info
645 -- @return Number containing the current pid
646 function process.info(key)
647 local s = {uid = nixio.getuid(), gid = nixio.getgid()}
648 return not key and s or s[key]
649 end
650
651 --- Retrieve information about currently running processes.
652 -- @return Table containing process information
653 function process.list()
654 local data = {}
655 local k
656 local ps = luci.util.execi("/bin/busybox top -bn1")
657
658 if not ps then
659 return
660 end
661
662 for line in ps do
663 local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
664 "^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
665 )
666
667 local idx = tonumber(pid)
668 if idx then
669 data[idx] = {
670 ['PID'] = pid,
671 ['PPID'] = ppid,
672 ['USER'] = user,
673 ['STAT'] = stat,
674 ['VSZ'] = vsz,
675 ['%MEM'] = mem,
676 ['%CPU'] = cpu,
677 ['COMMAND'] = cmd
678 }
679 end
680 end
681
682 return data
683 end
684
685 --- Set the gid of a process identified by given pid.
686 -- @param gid Number containing the Unix group id
687 -- @return Boolean indicating successful operation
688 -- @return String containing the error message if failed
689 -- @return Number containing the error code if failed
690 function process.setgroup(gid)
691 return nixio.setgid(gid)
692 end
693
694 --- Set the uid of a process identified by given pid.
695 -- @param uid Number containing the Unix user id
696 -- @return Boolean indicating successful operation
697 -- @return String containing the error message if failed
698 -- @return Number containing the error code if failed
699 function process.setuser(uid)
700 return nixio.setuid(uid)
701 end
702
703 --- Send a signal to a process identified by given pid.
704 -- @class function
705 -- @name process.signal
706 -- @param pid Number containing the process id
707 -- @param sig Signal to send (default: 15 [SIGTERM])
708 -- @return Boolean indicating successful operation
709 -- @return Number containing the error code if failed
710 process.signal = nixio.kill
711
712
713 --- LuCI system utilities / user related functions.
714 -- @class module
715 -- @name luci.sys.user
716 user = {}
717
718 --- Retrieve user informations for given uid.
719 -- @class function
720 -- @name getuser
721 -- @param uid Number containing the Unix user id
722 -- @return Table containing the following fields:
723 -- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
724 user.getuser = nixio.getpw
725
726 --- Retrieve the current user password hash.
727 -- @param username String containing the username to retrieve the password for
728 -- @return String containing the hash or nil if no password is set.
729 -- @return Password database entry
730 function user.getpasswd(username)
731 local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username)
732 local pwh = pwe and (pwe.pwdp or pwe.passwd)
733 if not pwh or #pwh < 1 or pwh == "!" or pwh == "x" then
734 return nil, pwe
735 else
736 return pwh, pwe
737 end
738 end
739
740 --- Test whether given string matches the password of a given system user.
741 -- @param username String containing the Unix user name
742 -- @param pass String containing the password to compare
743 -- @return Boolean indicating wheather the passwords are equal
744 function user.checkpasswd(username, pass)
745 local pwh, pwe = user.getpasswd(username)
746 if pwe then
747 return (pwh == nil or nixio.crypt(pass, pwh) == pwh)
748 end
749 return false
750 end
751
752 --- Change the password of given user.
753 -- @param username String containing the Unix user name
754 -- @param password String containing the password to compare
755 -- @return Number containing 0 on success and >= 1 on error
756 function user.setpasswd(username, password)
757 if password then
758 password = password:gsub("'", [['"'"']])
759 end
760
761 if username then
762 username = username:gsub("'", [['"'"']])
763 end
764
765 return os.execute(
766 "(echo '" .. password .. "'; sleep 1; echo '" .. password .. "') | " ..
767 "passwd '" .. username .. "' >/dev/null 2>&1"
768 )
769 end
770
771
772 --- LuCI system utilities / wifi related functions.
773 -- @class module
774 -- @name luci.sys.wifi
775 wifi = {}
776
777 --- Get wireless information for given interface.
778 -- @param ifname String containing the interface name
779 -- @return A wrapped iwinfo object instance
780 function wifi.getiwinfo(ifname)
781 local stat, iwinfo = pcall(require, "iwinfo")
782
783 if ifname then
784 local c = 0
785 local u = uci.cursor_state()
786 local d, n = ifname:match("^(%w+)%.network(%d+)")
787 if d and n then
788 ifname = d
789 n = tonumber(n)
790 u:foreach("wireless", "wifi-iface",
791 function(s)
792 if s.device == d then
793 c = c + 1
794 if c == n then
795 ifname = s.ifname or s.device
796 return false
797 end
798 end
799 end)
800 elseif u:get("wireless", ifname) == "wifi-device" then
801 u:foreach("wireless", "wifi-iface",
802 function(s)
803 if s.device == ifname and s.ifname then
804 ifname = s.ifname
805 return false
806 end
807 end)
808 end
809
810 local t = stat and iwinfo.type(ifname)
811 local x = t and iwinfo[t] or { }
812 return setmetatable({}, {
813 __index = function(t, k)
814 if k == "ifname" then
815 return ifname
816 elseif x[k] then
817 return x[k](ifname)
818 end
819 end
820 })
821 end
822 end
823
824
825 --- LuCI system utilities / init related functions.
826 -- @class module
827 -- @name luci.sys.init
828 init = {}
829 init.dir = "/etc/init.d/"
830
831 --- Get the names of all installed init scripts
832 -- @return Table containing the names of all inistalled init scripts
833 function init.names()
834 local names = { }
835 for name in fs.glob(init.dir.."*") do
836 names[#names+1] = fs.basename(name)
837 end
838 return names
839 end
840
841 --- Get the index of he given init script
842 -- @param name Name of the init script
843 -- @return Numeric index value
844 function init.index(name)
845 if fs.access(init.dir..name) then
846 return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null"
847 %{ init.dir, name })
848 end
849 end
850
851 local function init_action(action, name)
852 if fs.access(init.dir..name) then
853 return call("env -i %s%s %s >/dev/null" %{ init.dir, name, action })
854 end
855 end
856
857 --- Test whether the given init script is enabled
858 -- @param name Name of the init script
859 -- @return Boolean indicating whether init is enabled
860 function init.enabled(name)
861 return (init_action("enabled", name) == 0)
862 end
863
864 --- Enable the given init script
865 -- @param name Name of the init script
866 -- @return Boolean indicating success
867 function init.enable(name)
868 return (init_action("enable", name) == 1)
869 end
870
871 --- Disable the given init script
872 -- @param name Name of the init script
873 -- @return Boolean indicating success
874 function init.disable(name)
875 return (init_action("disable", name) == 0)
876 end
877
878 --- Start the given init script
879 -- @param name Name of the init script
880 -- @return Boolean indicating success
881 function init.start(name)
882 return (init_action("start", name) == 0)
883 end
884
885 --- Stop the given init script
886 -- @param name Name of the init script
887 -- @return Boolean indicating success
888 function init.stop(name)
889 return (init_action("stop", name) == 0)
890 end
891
892
893 -- Internal functions
894
895 function _parse_mixed_record(cnt, delimiter)
896 delimiter = delimiter or " "
897 local data = {}
898 local flags = {}
899
900 for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
901 for j, f in pairs(luci.util.split(luci.util.trim(l), delimiter, nil, true)) do
902 local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*')
903
904 if k then
905 if x == "" then
906 table.insert(flags, k)
907 else
908 data[k] = v
909 end
910 end
911 end
912 end
913
914 return data, flags
915 end