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