Merge pull request #1221 from Cye3s/master
authorHannu Nyman <hannu.nyman@iki.fi>
Sun, 23 Jul 2017 16:33:32 +0000 (19:33 +0300)
committerGitHub <noreply@github.com>
Sun, 23 Jul 2017 16:33:32 +0000 (19:33 +0300)
luci_base: Update zh_CN base.po, translate fix

24 files changed:
applications/luci-app-adblock/luasrc/model/cbi/adblock/overview_tab.lua
applications/luci-app-advanced-reboot/Makefile [new file with mode: 0644]
applications/luci-app-advanced-reboot/README.md [new file with mode: 0644]
applications/luci-app-advanced-reboot/luasrc/controller/advanced_reboot.lua [new file with mode: 0644]
applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/advanced_reboot.htm [new file with mode: 0644]
applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/alternative_reboot.htm [new file with mode: 0644]
applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/power_off.htm [new file with mode: 0644]
applications/luci-app-advanced-reboot/po/templates/luci-app-advanced-reboot.pot [new file with mode: 0644]
applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua
applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua
applications/luci-app-statistics/luasrc/model/cbi/luci_statistics/apcups.lua [new file with mode: 0644]
applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua [new file with mode: 0644]
applications/luci-app-statistics/root/etc/config/luci_statistics
applications/luci-app-statistics/root/usr/bin/stat-genconfig
collections/luci/Makefile
libs/rpcd-mod-rrdns/Makefile [new file with mode: 0644]
libs/rpcd-mod-rrdns/src/CMakeLists.txt [new file with mode: 0644]
libs/rpcd-mod-rrdns/src/rrdns.c [new file with mode: 0644]
libs/rpcd-mod-rrdns/src/rrdns.h [new file with mode: 0644]
modules/luci-base/luasrc/dispatcher.lua
modules/luci-base/luasrc/sys.lua
modules/luci-mod-admin-full/luasrc/controller/admin/status.lua
modules/luci-mod-admin-mini/luasrc/model/cbi/mini/network.lua
modules/luci-mod-freifunk/luasrc/controller/freifunk/freifunk.lua

index 38086c9..68f9c88 100644 (file)
@@ -6,9 +6,25 @@ local uci = require("uci")
 local sys = require("luci.sys")
 local json = require("luci.jsonc")
 local adbinput = uci.get("adblock", "global", "adb_rtfile") or "/tmp/adb_runtime.json"
+local dnspath = uci.get("adblock", "global", "adb_dnsdir") or ""
 local parse = json.parse(fs.readfile(adbinput) or "")
-local dnsFile1 = sys.exec("find '/tmp/dnsmasq.d/.adb_hidden' -maxdepth 1 -type f -name 'adb_list*' -print 2>/dev/null")
-local dnsFile2 = sys.exec("find '/var/lib/unbound/.adb_hidden' -maxdepth 1 -type f -name 'adb_list*' -print 2>/dev/null")
+if parse ~= nil then
+       version = parse.data.adblock_version
+       domains = parse.data.blocked_domains
+       fetch = parse.data.fetch_info
+       backend = parse.data.dns_backend
+       rundate = parse.data.last_rundate
+       if dnspath == "" then
+               if backend == "dnsmasq" then
+                       dnspath = "/tmp/dnsmasq.d"
+               elseif backend == "unbound" then
+                       dnspath = "/var/lib/unbound"
+               elseif backend == "named" then
+                       dnspath = "/var/lib/bind"
+               end
+       end
+end
+local dnsfile = dnspath .. "/.adb_hidden/adb_list.overall"
 
 m = Map("adblock", translate("Adblock"),
        translate("Configuration of the adblock package to block ad/abuse domains by using DNS. ")
@@ -36,7 +52,7 @@ o1.default = o1.enabled
 o1.rmempty = false
 
 btn = s:option(Button, "", translate("Suspend / Resume adblock"))
-if dnsFile1 ~= "" or dnsFile2 ~= "" then
+if parse ~= nil and nixio.fs.access(dnsfile) then
        btn.inputtitle = translate("Resume adblock")
        btn.inputstyle = "apply"
        btn.disabled = false
@@ -78,52 +94,53 @@ dv1 = s:option(DummyValue, "status", translate("Status"))
 dv1.template = "adblock/runtime"
 if parse == nil then
        dv1.value = translate("n/a")
-elseif parse.data.blocked_domains == "0" then
+elseif domains == "0" then
        dv1.value = translate("no domains blocked")
-elseif dnsFile1 ~= "" or dnsFile2 ~= "" then
+elseif nixio.fs.access(dnsfile) then
        dv1.value = translate("suspended")
 else
        dv1.value = translate("active")
 end
+
 dv2 = s:option(DummyValue, "adblock_version", translate("Adblock version"))
 dv2.template = "adblock/runtime"
-if parse ~= nil then
-       dv2.value = parse.data.adblock_version or translate("n/a")
-else
+if parse == nil then
        dv2.value = translate("n/a")
+else
+       dv2.value = version
 end
 
 dv3 = s:option(DummyValue, "fetch_info", translate("Download Utility (SSL Library)"),
        translate("For SSL protected blocklist sources you need a suitable SSL library, e.g. 'libustream-ssl' or the wget 'built-in'."))
 dv3.template = "adblock/runtime"
-if parse ~= nil then
-       dv3.value = parse.data.fetch_info or translate("n/a")
-else
+if parse == nil then
        dv3.value = translate("n/a")
+else
+       dv3.value = fetch
 end
 
 dv4 = s:option(DummyValue, "dns_backend", translate("DNS backend"))
 dv4.template = "adblock/runtime"
-if parse ~= nil then
-       dv4.value = parse.data.dns_backend or translate("n/a")
-else
+if parse == nil then
        dv4.value = translate("n/a")
+else
+       dv4.value = backend
 end
 
 dv5 = s:option(DummyValue, "blocked_domains", translate("Blocked domains (overall)"))
 dv5.template = "adblock/runtime"
-if parse ~= nil then
-       dv5.value = parse.data.blocked_domains or translate("n/a")
-else
+if parse == nil then
        dv5.value = translate("n/a")
+else
+       dv5.value = domains
 end
 
 dv6 = s:option(DummyValue, "last_rundate", translate("Last rundate"))
 dv6.template = "adblock/runtime"
-if parse ~= nil then
-       dv6.value = parse.data.last_rundate or translate("n/a")
-else
+if parse == nil then
        dv6.value = translate("n/a")
+else
+       dv6.value = rundate
 end
 
 -- Blocklist table
diff --git a/applications/luci-app-advanced-reboot/Makefile b/applications/luci-app-advanced-reboot/Makefile
new file mode 100644 (file)
index 0000000..3a886eb
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright (c) 2017 Stan Grishin (stangri@melmac.net)
+# This is free software, licensed under the GNU General Public License v3.
+
+include $(TOPDIR)/rules.mk
+
+PKG_LICENSE:=GPL-3.0+
+PKG_MAINTAINER:=Stan Grishin <stangri@melmac.net>
+
+LUCI_TITLE:=Advanced Linksys Reboot Web UI
+LUCI_DESCRIPTION:=Provides Web UI (found under System/Advanced Reboot) to reboot supported Linksys routers to\
+       an altnerative partition. Also provides Web UI to shut down (power off) your device.    Supported dual-partition\
+       routers are listed at https://github.com/stangri/openwrt-luci/blob/luci-app-advanced-reboot/applications/luci-app-advanced-reboot/README.md
+
+LUCI_DEPENDS:=+luci
+LUCI_PKGARCH:=all
+PKG_RELEASE:=23
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/applications/luci-app-advanced-reboot/README.md b/applications/luci-app-advanced-reboot/README.md
new file mode 100644 (file)
index 0000000..ee87a00
--- /dev/null
@@ -0,0 +1,35 @@
+# Advanced Reboot Web UI (luci-app-advanced-reboot)
+
+## Description
+This package allows you to reboot to an alternative partition on supported (dual-partition) routers and to power off (power down) your OpenWrt/LEDE Project device.
+
+## Supported Devices
+Currently supported dual-partition devices include:
+- Linksys WRT1200AC
+- Linksys WRT1900AC
+- Linksys WRT1900ACv2
+- Linksys WRT1900ACS
+- Linksys WRT3200ACM
+- Linksys E4200v2
+- Linksys EA4500
+- Linksys EA8500
+
+If you're interested in having your device supported, please post in [LEDE Project Forum Support Thread](https://forum.lede-project.org/t/web-ui-to-reboot-to-another-partition-dual-partition-routers/3423).
+
+## Screenshot (luci-app-advanced-reboot)
+![screenshot](https://raw.githubusercontent.com/stangri/screenshots/master/luci-app-advanced-reboot/screenshot01.png "screenshot")
+
+## How to install
+Install ```luci-app-advanced-reboot``` from Web UI or connect to your router via ssh and run the following commands:
+```sh
+opkg update
+opkg install luci-app-advanced-reboot
+```
+
+## Notes/Known Issues
+- When you reboot to a different partition, your current settings (WiFi SSID/password, etc.) will not apply to a different partition. Different partitions might have completely different settings and even firmware.
+- If you reboot to a partition which doesn't allow you to switch boot partitions (like stock Linksys firmware), you might not be able to boot back to OpenWrt/LEDE Project unless you reflash it, loosing all the settings.
+- Some devices allow you to trigger reboot to alternative partition by interrupting boot 3 times in a row (by resetting/switching off the device or pulling power). As these methods might be different for different devices, do your own homework.
+
+## Thanks
+I'd like to thank everyone who helped create, test and troubleshoot this package. Without contributions from [@hnyman](https://github.com/hnyman) and [@jpstyves](https://github.com/jpstyves) it wouldn't have been possible.
diff --git a/applications/luci-app-advanced-reboot/luasrc/controller/advanced_reboot.lua b/applications/luci-app-advanced-reboot/luasrc/controller/advanced_reboot.lua
new file mode 100644 (file)
index 0000000..2b55217
--- /dev/null
@@ -0,0 +1,120 @@
+-- Copyright 2017 Stan Grishin <stangri@melmac.net>
+-- Licensed to the public under the Apache License 2.0.
+
+module("luci.controller.advanced_reboot", package.seeall)
+
+-- device, board_name, part1, part2, offset, env_var_1, value_1_1, value_1_2, env_var_2, value_2_1, value_2_2
+devices = {
+  {"Linksys WRT1200AC", "armada-385-linksys-caiman", "mtd4", "mtd6", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
+  {"Linksys WRT1900AC", "armada-xp-linksys-mamba", "mtd4", "mtd6", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
+  {"Linksys WRT1900ACv2", "armada-385-linksys-cobra", "mtd4", "mtd6", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
+  {"Linksys WRT1900ACS", "armada-385-linksys-shelby", "mtd4", "mtd6", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
+  {"Linksys WRT3200ACM", "armada-385-linksys-rango", "mtd5", "mtd7", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
+  {"Linksys E4200v2/EA4500", "linksys-viper", "mtd3", "mtd5", 32, "boot_part", 1, 2, "bootcmd", "run nandboot", "run altnandboot"},
+  {"Linksys EA8500", "ea8500", "mtd13", "mtd15", 32, "boot_part", 1, 2}
+}
+
+board_name = luci.util.trim(luci.sys.exec("cat /tmp/sysinfo/board_name"))
+for i=1, #devices do
+  if board_name and devices[i][2] == board_name then
+    device_name = devices[i][1]
+    partition_one_mtd = devices[i][3] or nil
+    partition_two_mtd = devices[i][4] or nil
+    partition_skip = devices[i][5] or nil
+    boot_envvar1 = devices[i][6] or nil
+    boot_envvar1_partition_one = tonumber(devices[i][7]) or nil
+    boot_envvar1_partition_two = tonumber(devices[i][8]) or nil
+    boot_envvar2 = devices[i][9] or nil
+    boot_envvar2_partition_one = devices[i][10] or nil
+    boot_envvar2_partition_two = devices[i][11] or nil
+    if partition_one_mtd and partition_skip then
+      partition_one_label = luci.util.trim(luci.sys.exec("dd if=/dev/" .. partition_one_mtd .. " bs=1 skip=" .. partition_skip .. " count=25" .. "  2>/dev/null"))
+      n, partition_one_version = string.match(partition_one_label, '(Linux)-([%d|.]+)')
+    end
+    if partition_two_mtd and partition_skip then
+      partition_two_label = luci.util.trim(luci.sys.exec("dd if=/dev/" .. partition_two_mtd .. " bs=1 skip=" .. partition_skip .. " count=25" .. "  2>/dev/null"))
+      n, partition_two_version = string.match(partition_two_label, '(Linux)-([%d|.]+)')
+    end
+    if string.find(partition_one_label, "LEDE") then partition_one_os = "LEDE" end
+    if string.find(partition_one_label, "OpenWrt") then partition_one_os = "OpenWrt" end
+    if string.find(partition_one_label, "Linksys") then partition_one_os = "Linksys" end
+    if string.find(partition_two_label, "LEDE") then partition_two_os = "LEDE" end
+    if string.find(partition_two_label, "OpenWrt") then partition_two_os = "OpenWrt" end
+    if string.find(partition_two_label, "Linksys") then partition_two_os = "Linksys" end
+    if not partition_one_os then partition_one_os = "Unknown" end
+    if not partition_two_os then partition_two_os = "Unknown" end
+    if partition_one_os and partition_one_version then partition_one_os = partition_one_os .. " (Linux " .. partition_one_version .. ")" end
+    if partition_two_os and partition_two_version then partition_two_os = partition_two_os .. " (Linux " .. partition_two_version .. ")" end
+    if nixio.fs.access("/usr/sbin/fw_printenv") and nixio.fs.access("/usr/sbin/fw_setenv") then
+      current_partition = tonumber(luci.util.trim(luci.sys.exec("/usr/sbin/fw_printenv -n " .. boot_envvar1)))
+      other_partition = current_partition == boot_envvar1_partition_one and boot_envvar1_partition_two or boot_envvar1_partition_one
+    end
+  end
+end
+
+function index()
+  entry({"admin", "system", "advanced_reboot"}, template("advanced_reboot/advanced_reboot"), _("Advanced Reboot"), 90)
+  entry({"admin", "system", "advanced_reboot", "reboot"}, post("action_reboot"))
+--  if device_name then entry({"admin", "system", "advanced_reboot", "altreboot"}, post("action_altreboot")) end
+  entry({"admin", "system", "advanced_reboot", "alternative_reboot"}, post("action_altreboot"))
+  entry({"admin", "system", "advanced_reboot", "power_off"}, post("action_poweroff"))
+end
+
+function action_reboot()
+  luci.template.render("admin_system/applyreboot", {
+        title = luci.i18n.translate("Rebooting..."),
+        msg   = luci.i18n.translate("The system is rebooting now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a few minutes before you try to reconnect. It might be necessary to renew the address of your computer to reach the device again, depending on your settings."),
+        addr  = luci.ip.new(uci.cursor():get("network", "lan", "ipaddr")) or "192.168.1.1"
+      })
+  luci.sys.reboot()
+end
+
+function action_altreboot()
+  if luci.http.formvalue("cancel") then
+    luci.http.redirect(luci.dispatcher.build_url('admin/system/advanced_reboot'))
+    return
+  end
+  local step = tonumber(luci.http.formvalue("step") or 1)
+  if step == 1 then
+    if device_name and nixio.fs.access("/usr/sbin/fw_printenv") and nixio.fs.access("/usr/sbin/fw_setenv") then
+      luci.template.render("advanced_reboot/alternative_reboot",{})
+    else
+      luci.template.render("advanced_reboot/advanced_reboot",{})
+    end
+  elseif step == 2 then
+    luci.template.render("admin_system/applyreboot", {
+          title = luci.i18n.translate("Rebooting..."),
+          msg   = luci.i18n.translate("The system is rebooting to an alternative partition now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a few minutes before you try to reconnect. It might be necessary to renew the address of your computer to reach the device again, depending on your settings."),
+          addr  = luci.ip.new(uci.cursor():get("network", "lan", "ipaddr")) or "192.168.1.1"
+        })
+    if boot_envvar1 then env1 = tonumber(luci.util.trim(luci.sys.exec("/usr/sbin/fw_printenv -n " .. boot_envvar1))) end
+    if boot_envvar2 then env2 = luci.util.trim(luci.sys.exec("/usr/sbin/fw_printenv -n " .. boot_envvar2)) end
+    if env1 and env1 == boot_envvar1_partition_one then luci.sys.call("/usr/sbin/fw_setenv " .. boot_envvar1 .. " " .. boot_envvar1_partition_two) end
+    if env1 and env1 == boot_envvar1_partition_two then luci.sys.call("/usr/sbin/fw_setenv " .. boot_envvar1 .. " " .. boot_envvar1_partition_one) end
+    if env2 and env2 == boot_envvar2_partition_one then luci.sys.call("/usr/sbin/fw_setenv " .. boot_envvar2 .. " '" .. boot_envvar2_partition_two .. "'") end
+    if env2 and env2 == boot_envvar2_partition_two then luci.sys.call("/usr/sbin/fw_setenv " .. boot_envvar2 .. " '" .. boot_envvar2_partition_one .. "'") end
+    luci.sys.reboot()
+  end
+end
+
+function action_poweroff()
+  if luci.http.formvalue("cancel") then
+    luci.http.redirect(luci.dispatcher.build_url('admin/system/advanced_reboot'))
+    return
+  end
+  local step = tonumber(luci.http.formvalue("step") or 1)
+  if step == 1 then
+    if nixio.fs.access("/sbin/poweroff") then
+      luci.template.render("advanced_reboot/power_off",{})
+    else
+      luci.template.render("advanced_reboot/advanced_reboot",{})
+    end
+  elseif step == 2 then
+    luci.template.render("admin_system/applyreboot", {
+          title = luci.i18n.translate("Shutting down..."),
+          msg   = luci.i18n.translate("The system is shutting down now.<br /> DO NOT POWER OFF THE DEVICE!<br /> It might be necessary to renew the address of your computer to reach the device again, depending on your settings."),
+          addr  = luci.ip.new(uci.cursor():get("network", "lan", "ipaddr")) or "192.168.1.1"
+        })
+    luci.sys.call("/sbin/poweroff")
+  end
+end
diff --git a/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/advanced_reboot.htm b/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/advanced_reboot.htm
new file mode 100644 (file)
index 0000000..206d250
--- /dev/null
@@ -0,0 +1,92 @@
+<%#
+ Copyright 2008 Steven Barth <steven@midlink.org>
+ Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
+ Copyright 2017 Stan Grishin <stangri@melmac.net>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+
+<h2 name="content"><%:Advanced Reboot%></h2>
+<br />
+
+<%- local c = require("luci.model.uci").cursor():changes(); if c and next(c) then -%>
+       <p class="alert-message warning"><%:Warning: There are unsaved changes that will get lost on reboot!%></p>
+<%- end -%>
+
+<%- if device_name then -%>
+<fieldset class="cbi-section">
+  <legend><%=device_name%><%: Partitions%></legend>
+  <table class="cbi-section-table" id="partitions">
+    <tr class="cbi-section-table-titles">
+      <th class="cbi-section-table-cell"><%:Partition%></th>
+      <th class="cbi-section-table-cell"><%:Status%></th>
+      <th class="cbi-section-table-cell"><%:Firmware/OS (Kernel)%></th>
+      <th class="cbi-section-table-cell"><%:Action%></th>
+    </tr>
+    <tr class="cbi-section-table-row">
+      <td>
+        <%=boot_envvar1_partition_one%>
+      </td>
+      <td>
+        <%- if boot_envvar1_partition_one == current_partition then -%><%:Current%><%- else -%><%:Alternative%><%- end -%>
+      </td>
+      <td>
+        <%=partition_one_os%>
+      </td>
+      <td>
+        <%- if boot_envvar1_partition_one == current_partition then -%>
+        <form method="post" action="<%=url('admin/system/advanced_reboot/reboot')%>">
+          <input type="hidden" name="token" value="<%=token%>" />
+          <input id="reboot-button" type="submit" class="cbi-button cbi-button-apply" value="<%:Reboot to current partition%>" />
+        </form>
+      <%- else -%>
+      <form method="post" action="<%=url('admin/system/advanced_reboot/alternative_reboot')%>">
+        <input type="hidden" name="token" value="<%=token%>" />
+        <input id="altreboot-button" type="submit" class="cbi-button cbi-button-apply" value="<%:Reboot to alternative partition...%>" />
+      </form>
+        <%- end -%>
+      </td>
+    </tr>
+    <tr class="cbi-section-table-row">
+      <td>
+        <%=boot_envvar1_partition_two%>
+      </td>
+      <td>
+        <%- if boot_envvar1_partition_two == current_partition then -%><%:Current%><%- else -%><%:Alternative%><%- end -%>
+      </td>
+      <td>
+        <%=partition_two_os%>
+      </td>
+      <td>
+        <%- if boot_envvar1_partition_two == current_partition then -%>
+          <form method="post" action="<%=url('admin/system/advanced_reboot/reboot')%>">
+               <input type="hidden" name="token" value="<%=token%>" />
+            <input id="reboot-button" type="submit" class="cbi-button cbi-button-apply" value="<%:Reboot to current partition%>" />
+          </form>
+        <%- else -%>
+        <form method="post" action="<%=url('admin/system/advanced_reboot/alternative_reboot')%>">
+          <input type="hidden" name="token" value="<%=token%>" />
+          <input id="altreboot-button" type="submit" class="cbi-button cbi-button-apply" value="<%:Reboot to alternative partition...%>" />
+        </form>
+        <%- end -%>
+      </td>
+    </tr>
+  </table>
+</fieldset>
+<%- else -%>
+  <p class="alert-message warning"><%:Warning: This system does not have two partitions!%></p>
+<%- end -%>
+
+<hr />
+
+<%- if nixio.fs.access("/sbin/poweroff") then -%>
+<form method="post" action="<%=url('admin/system/advanced_reboot/power_off')%>">
+       <input type="hidden" name="token" value="<%=token%>" />
+  <input id="poweroff-button" type="submit" class="cbi-button cbi-button-apply" value="<%:Perform power off...%>" />
+</form>
+<%- else -%>
+  <p class="alert-message warning"><%:Warning: This system does not support powering off!%></p>
+<%- end -%>
+
+<%+footer%>
diff --git a/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/alternative_reboot.htm b/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/alternative_reboot.htm
new file mode 100644 (file)
index 0000000..6325934
--- /dev/null
@@ -0,0 +1,29 @@
+<%#
+ Copyright 2008 Steven Barth <steven@midlink.org>
+ Copyright 2008-2009 Jo-Philipp Wich <jow@openwrt.org>
+ Copyright 2017 Stan Grishin <stangri@melmac.net>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+
+<h2 name="content"><%:Reboot Device to an Alternative Partition%> - <%:Confirm%></h2>
+<p>
+       <%_ WARNING: An alternative partition might have its own settings and completely different firmware.<br /><br />
+    As your network configuration and WiFi SSID/password on alternative partition might be different,
+      you might have to adjust your computer settings to be able to access your device once it reboots.<br /><br />
+    Please also be aware that alternative partition firmware might not provide an easy way to switch active partition
+      and boot back to the currently active partition.<br /><br />
+               Click "Proceed" below to reboot device to an alternative partition. %>
+</p>
+
+<div class="cbi-page-actions right">
+       <form class="inline" action="<%=REQUEST_URI%>" method="post">
+               <input type="hidden" name="token" value="<%=token%>" />
+               <input type="hidden" name="step" value="2" />
+               <input class="cbi-button cbi-button-reset" name="cancel" type="submit" value="<%:Cancel%>" />
+               <input class="cbi-button cbi-button-apply" type="submit" value="<%:Proceed%>" />
+       </form>
+</div>
+
+<%+footer%>
diff --git a/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/power_off.htm b/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/power_off.htm
new file mode 100644 (file)
index 0000000..0ddea11
--- /dev/null
@@ -0,0 +1,25 @@
+<%#
+ Copyright 2008 Steven Barth <steven@midlink.org>
+ Copyright 2008-2009 Jo-Philipp Wich <jow@openwrt.org>
+ Copyright 2017 Stan Grishin <stangri@melmac.net>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+
+<h2 name="content"><%:Power Off Device%> - <%:Confirm%></h2>
+<p>
+       <%_ WARNING: Power off might result in a reboot on a device which doesn't support power off.<br /><br />
+               Click "Proceed" below to power off your device. %>
+</p>
+
+<div class="cbi-page-actions right">
+       <form class="inline" action="<%=REQUEST_URI%>" method="post">
+               <input type="hidden" name="token" value="<%=token%>" />
+               <input type="hidden" name="step" value="2" />
+               <input class="cbi-button cbi-button-reset" name="cancel" type="submit" value="<%:Cancel%>" />
+               <input class="cbi-button cbi-button-apply" type="submit" value="<%:Proceed%>" />
+       </form>
+</div>
+
+<%+footer%>
diff --git a/applications/luci-app-advanced-reboot/po/templates/luci-app-advanced-reboot.pot b/applications/luci-app-advanced-reboot/po/templates/luci-app-advanced-reboot.pot
new file mode 100644 (file)
index 0000000..9c81089
--- /dev/null
@@ -0,0 +1,102 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "Action"
+msgstr ""
+
+msgid "Advanced Reboot"
+msgstr ""
+
+msgid "Alternative"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Confirm"
+msgstr ""
+
+msgid "Current"
+msgstr ""
+
+msgid "Firmware/OS (Kernel)"
+msgstr ""
+
+msgid "Partition"
+msgstr ""
+
+msgid "Partitions"
+msgstr ""
+
+msgid "Perform power off..."
+msgstr ""
+
+msgid "Power Off Device"
+msgstr ""
+
+msgid "Proceed"
+msgstr ""
+
+msgid "Reboot Device to an Alternative Partition"
+msgstr ""
+
+msgid "Reboot to alternative partition..."
+msgstr ""
+
+msgid "Reboot to current partition"
+msgstr ""
+
+msgid "Rebooting..."
+msgstr ""
+
+msgid "Shutting down..."
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid ""
+"The system is rebooting now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a "
+"few minutes before you try to reconnect. It might be necessary to renew the "
+"address of your computer to reach the device again, depending on your "
+"settings."
+msgstr ""
+
+msgid ""
+"The system is rebooting to an alternative partition now.<br /> DO NOT POWER "
+"OFF THE DEVICE!<br /> Wait a few minutes before you try to reconnect. It "
+"might be necessary to renew the address of your computer to reach the device "
+"again, depending on your settings."
+msgstr ""
+
+msgid ""
+"The system is shutting down now.<br /> DO NOT POWER OFF THE DEVICE!<br /> It "
+"might be necessary to renew the address of your computer to reach the device "
+"again, depending on your settings."
+msgstr ""
+
+msgid ""
+"WARNING: An alternative partition might have its own settings and completely "
+"different firmware.<br /><br /> As your network configuration and WiFi SSID/"
+"password on alternative partition might be different, you might have to "
+"adjust your computer settings to be able to access your device once it "
+"reboots.<br /><br /> Please also be aware that alternative partition "
+"firmware might not provide an easy way to switch active partition and boot "
+"back to the currently active partition.<br /><br /> Click \"Proceed\" below "
+"to reboot device to an alternative partition."
+msgstr ""
+
+msgid ""
+"WARNING: Power off might result in a reboot on a device which doesn't "
+"support power off.<br /><br /> Click \"Proceed\" below to power off your "
+"device."
+msgstr ""
+
+msgid "Warning: There are unsaved changes that will get lost on reboot!"
+msgstr ""
+
+msgid "Warning: This system does not have two partitions!"
+msgstr ""
+
+msgid "Warning: This system does not support powering off!"
+msgstr ""
index 76435e2..97ce83f 100644 (file)
@@ -137,13 +137,14 @@ o:value("2", translate("Allow all except listed"))
 o.default = 0
 o.rmempty = false
 
-a = luci.sys.net.arptable() or {}
-
 o = s:taboption("lan_ac", DynamicList, "lan_ac_ip", translate("LAN IP List"))
 o.datatype = "ipaddr"
-for i,v in ipairs(a) do
-       o:value(v["IP address"])
-end
+
+luci.ip.neighbors({ family = 4 }, function(entry)
+       if entry.reachable then
+               o:value(entry.dest:string())
+       end
+end)
 
 s:tab("wan_ac", translate("WAN"))
 
index 443f20b..ec26f02 100644 (file)
@@ -23,6 +23,7 @@ function index()
                s_general       = _("General plugins"),
                s_network       = _("Network plugins"),
 
+               apcups          = _("APC UPS"),
                conntrack       = _("Conntrack"),
                contextswitch   = _("Context Switches"),
                cpu                     = _("Processor"),
@@ -59,8 +60,8 @@ function index()
        -- our collectd menu
        local collectd_menu = {
                output  = { "csv", "network", "rrdtool", "unixsock" },
-               general = { "contextswitch", "cpu", "cpufreq", "df", "disk", "email",
-                       "entropy", "exec", "irq", "load", "memory",
+               general = { "apcups", "contextswitch", "cpu", "cpufreq", "df",
+                       "disk", "email", "entropy", "exec", "irq", "load", "memory",
                        "nut", "processes", "sensors", "thermal", "uptime" },
                network = { "conntrack", "dns", "interface", "iptables",
                        "netlink", "olsrd", "openvpn", "ping",
diff --git a/applications/luci-app-statistics/luasrc/model/cbi/luci_statistics/apcups.lua b/applications/luci-app-statistics/luasrc/model/cbi/luci_statistics/apcups.lua
new file mode 100644 (file)
index 0000000..49e28c7
--- /dev/null
@@ -0,0 +1,28 @@
+-- Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
+-- Licensed to the public under the Apache License 2.0.
+
+m = Map("luci_statistics",
+       translate("APCUPS Plugin Configuration"),
+       translate(
+               "The APCUPS plugin collects statistics about the APC UPS."
+       ))
+
+-- collectd_apcups config section
+s = m:section( NamedSection, "collectd_apcups", "luci_statistics" )
+
+-- collectd_apcups.enable
+enable = s:option( Flag, "enable", translate("Enable this plugin") )
+enable.default = 0
+
+-- collectd_apcups.host (Host)
+host = s:option( Value, "Host", translate("Monitor host"), translate ("Add multiple hosts separated by space."))
+host.default = "localhost"
+host:depends( "enable", 1 )
+
+-- collectd_apcups.port (Port)
+port = s:option( Value, "Port", translate("Port for apcupsd communication") )
+port.isinteger = true
+port.default   = 3551
+port:depends( "enable", 1 )
+
+return m
diff --git a/applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua b/applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua
new file mode 100644 (file)
index 0000000..04eee93
--- /dev/null
@@ -0,0 +1,101 @@
+-- Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
+-- Licensed to the public under the Apache License 2.0.
+
+module("luci.statistics.rrdtool.definitions.apcups",package.seeall)
+
+function rrdargs( graph, plugin, plugin_instance, dtype )
+
+       local voltages = {
+               title = "%H: Voltages on APC UPS ",
+               vlabel = "V",
+               number_format = "%5.1lfV",
+               data = {
+                       instances = {
+                               voltage = { "battery", "input", "output" }
+                       },
+
+                       options = {
+                               voltage_output  = { color = "00e000", title = "Output voltage", noarea=true, overlay=true },
+                               voltage_battery = { color = "0000ff", title = "Battery voltage", noarea=true, overlay=true },
+                               voltage_input   = { color = "ffb000", title = "Input voltage", noarea=true, overlay=true }
+                       }
+               }
+       }
+
+       local percentload = {
+               title = "%H: Load on APC UPS ",
+               vlabel = "Percent",
+               y_min = "0",
+               y_max = "100",
+               number_format = "%5.1lf%%",
+               data = {
+                       sources = {
+                               percent_load = { "value" }
+                       },
+                       instances = {
+                               percent = "load"
+                       },
+                       options = {
+                               percent_load = { color = "00ff00", title = "Load level"  }
+                       }
+               }
+       }
+
+       local charge_percent = {
+               title = "%H: Battery charge on APC UPS ",
+               vlabel = "Percent",
+               y_min = "0",
+               y_max = "100",
+               number_format = "%5.1lf%%",
+               data = {
+                       types = { "charge" },
+                       options = {
+                               charge = { color = "00ff0b", title = "Charge level"  }
+                       }
+               }
+       }
+
+       local temperature = {
+               title = "%H: Battery temperature on APC UPS ",
+               vlabel = "\176C",
+               number_format = "%5.1lf\176C",
+               data = {
+                       types = { "temperature" },
+                       options = {
+                               temperature = { color = "ffb000", title = "Battery temperature" } }
+               }
+       }
+
+       local timeleft = {
+               title = "%H: Time left on APC UPS ",
+               vlabel = "Minutes",
+               number_format = "%.1lfm",
+               data = {
+                       sources = {
+                               timeleft = { "value" }
+                       },
+                       options = {
+                               timeleft = { color = "0000ff", title = "Time left" }
+                       }
+               }
+       }
+
+       local frequency = {
+               title = "%H: Incoming line frequency on APC UPS ",
+               vlabel = "Hz",
+               number_format = "%5.0lfhz",
+               data = {
+                       sources = {
+                               frequency_input = { "value" }
+                       },
+                       instances = {
+                               frequency = "frequency"
+                       },
+                       options = {
+                               frequency_frequency = { color = "000fff", title = "Line frequency" }
+                       }
+               }
+       }
+
+       return { voltages, percentload, charge_percent, temperature, timeleft, frequency }
+end
index c081a8e..8cc918e 100644 (file)
@@ -49,6 +49,11 @@ config statistics 'collectd_unixsock'
 
 # input plugins
 
+config statistics 'collectd_apcups'
+       option enable '0'
+       option Host 'localhost'
+       option Port '3551'
+
 config statistics 'collectd_conntrack'
        option enable '0'
 
index 090344c..2bf63c1 100755 (executable)
@@ -255,6 +255,12 @@ end
 
 
 plugins = {
+       apcups = {
+               { "Host", "Port" },
+               { },
+               { }
+       },
+
        collectd = {
                { "BaseDir", "Include", "PIDFile", "PluginDir", "TypesDB", "Interval", "ReadThreads", "Hostname" },
                { },
@@ -461,8 +467,10 @@ preprocess = {
 
 section("collectd")
 
+section("logfile")
+
 for plugin in pairs(plugins) do
-       if plugin ~= "collectd" then
+       if (plugin ~= "collectd") and (plugin ~= "logfile") then
                section( plugin )
        end
 end
index 6f6c3a9..9b495c3 100644 (file)
@@ -12,7 +12,8 @@ LUCI_BASENAME:=luci
 LUCI_TITLE:=Standard OpenWrt set including full admin with ppp support and the default Bootstrap theme
 LUCI_DEPENDS:= \
        +uhttpd +uhttpd-mod-ubus +luci-mod-admin-full +luci-theme-bootstrap \
-       +luci-app-firewall +luci-proto-ppp +libiwinfo-lua +IPV6:luci-proto-ipv6
+       +luci-app-firewall +luci-proto-ppp +libiwinfo-lua +IPV6:luci-proto-ipv6 \
+       +rpcd-mod-rrdns
 
 PKG_LICENSE:=Apache-2.0
 
diff --git a/libs/rpcd-mod-rrdns/Makefile b/libs/rpcd-mod-rrdns/Makefile
new file mode 100644 (file)
index 0000000..f0bf140
--- /dev/null
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2016-2017 Jo-Philipp Wich <jo@mein.io>
+#
+# Licensed under the Apache License, Version 2.0.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=rpcd-mod-rrdns
+PKG_VERSION:=20170710
+PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
+
+PKG_LICENSE:=Apache-2.0
+
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+define Build/Prepare
+       $(INSTALL_DIR) $(PKG_BUILD_DIR)
+       $(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Package/rpcd-mod-rrdns
+  SECTION:=libs
+  CATEGORY:=Libraries
+  TITLE:=Rapid reverse DNS rpcd module
+  DEPENDS:=+rpcd +libubox +libubus
+endef
+
+define Package/rpcd-mod-rrdns/description
+ Provides rapid mass reverse DNS lookup functionality.
+endef
+
+define Package/rpcd-mod-rrdns/install
+       $(INSTALL_DIR) $(1)/usr/lib/rpcd
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/rrdns.so $(1)/usr/lib/rpcd/
+endef
+
+define Package/rpcd-mod-rrdns/postinst
+#!/bin/sh
+killall -HUP rpcd 2>/dev/null
+exit 0
+endef
+
+$(eval $(call BuildPackage,rpcd-mod-rrdns))
diff --git a/libs/rpcd-mod-rrdns/src/CMakeLists.txt b/libs/rpcd-mod-rrdns/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ace6ac8
--- /dev/null
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 2.6)
+
+PROJECT(rpcd-mod-rrdns C)
+
+ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations)
+
+SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+
+IF(APPLE)
+  INCLUDE_DIRECTORIES(/opt/local/include)
+  LINK_DIRECTORIES(/opt/local/lib)
+ENDIF()
+
+FIND_LIBRARY(resolv NAMES resolv)
+IF(resolv STREQUAL "LIBS-NOTFOUND")
+  SET(resolv "")
+ENDIF()
+
+ADD_LIBRARY(rpcd-mod-rrdns MODULE rrdns.c)
+TARGET_LINK_LIBRARIES(rpcd-mod-rrdns ubox ubus ${resolv})
+SET_TARGET_PROPERTIES(rpcd-mod-rrdns PROPERTIES OUTPUT_NAME rrdns PREFIX "")
+
+INSTALL(TARGETS rpcd-mod-rrdns LIBRARY DESTINATION lib)
diff --git a/libs/rpcd-mod-rrdns/src/rrdns.c b/libs/rpcd-mod-rrdns/src/rrdns.c
new file mode 100644 (file)
index 0000000..691db9c
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * rrdns - Rapid Reverse DNS lookup plugin for the UBUS RPC server
+ *
+ *   Copyright (C) 2016 Jo-Philipp Wich <jow@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <resolv.h>
+
+#include <libubox/avl.h>
+#include <libubox/usock.h>
+#include <libubox/uloop.h>
+
+#include <rpcd/plugin.h>
+
+#include "rrdns.h"
+
+
+enum {
+       RPC_L_ADDRS,
+       RPC_L_TIMEOUT,
+       RPC_L_SERVER,
+       RPC_L_PORT,
+       RPC_L_LIMIT,
+       __RPC_L_MAX,
+};
+
+static const struct blobmsg_policy rpc_lookup_policy[__RPC_L_MAX] = {
+       [RPC_L_ADDRS]   = { .name = "addrs",   .type = BLOBMSG_TYPE_ARRAY  },
+       [RPC_L_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32  },
+       [RPC_L_SERVER]  = { .name = "server",  .type = BLOBMSG_TYPE_STRING },
+       [RPC_L_PORT]    = { .name = "port",    .type = BLOBMSG_TYPE_INT16  },
+       [RPC_L_LIMIT]   = { .name = "limit",   .type = BLOBMSG_TYPE_INT32  },
+};
+
+
+static int
+rrdns_cmp_id(const void *k1, const void *k2, void *ptr)
+{
+       const uint16_t *id1 = k1, *id2 = k2;
+       return (*id1 - *id2);
+}
+
+static int
+rrdns_cmp_addr(const void *k1, const void *k2, void *ptr)
+{
+       const struct in6_addr *a1 = k1, *a2 = k2;
+       return memcmp(a1, a2, sizeof(*a1));
+}
+
+static int
+rrdns_parse_response(struct rrdns_context *rctx)
+{
+       int n, len;
+       uint16_t id;
+       struct rrdns_request *req;
+       unsigned char res[512];
+       char buf[INET6_ADDRSTRLEN], dname[MAXDNAME];
+       HEADER *hdr;
+       ns_msg handle;
+       ns_rr rr;
+
+       len = recv(rctx->socket.fd, res, sizeof(res), 0);
+
+       if (len < sizeof(*hdr))
+               return -ENODATA;
+
+       hdr = (HEADER *)res;
+       id  = hdr->id;
+       req = avl_find_element(&rctx->request_ids, &id, req, by_id);
+
+       if (!req)
+               return -ENOENT;
+
+       avl_delete(&rctx->request_ids, &req->by_id);
+
+       if (ns_initparse(res, len, &handle))
+               return -EINVAL;
+
+       for (n = 0; n < ns_msg_count(handle, ns_s_an); n++) {
+               if (ns_parserr(&handle, ns_s_an, n, &rr))
+                       return -EINVAL;
+
+               if (ns_rr_type(rr) != ns_t_ptr)
+                       continue;
+
+               if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
+                                      ns_rr_rdata(rr), dname, sizeof(dname)) < 0)
+                       return -EINVAL;
+
+               inet_ntop(req->family, &req->addr, buf, sizeof(buf));
+               blobmsg_add_string(&rctx->blob, buf, dname);
+       }
+
+       return 0;
+}
+
+static int
+rrdns_next_query(struct rrdns_context *rctx)
+{
+       const char *addr = NULL, *hex = "0123456789abcdef";
+       struct rrdns_request *req;
+       int i, alen, family;
+       char *p, dname[73];
+
+       union {
+               unsigned char uchar[4];
+               struct in6_addr in6;
+               struct in_addr in;
+       } a = { };
+
+       union {
+               unsigned char buf[512];
+               HEADER hdr;
+       } msg;
+
+       if (rctx->addr_rem > 0 &&
+           blob_pad_len(rctx->addr_cur) <= rctx->addr_rem &&
+           blob_pad_len(rctx->addr_cur) >= sizeof(struct blob_attr)) {
+
+               addr = blobmsg_get_string(rctx->addr_cur);
+               rctx->addr_rem -= blob_pad_len(rctx->addr_cur);
+               rctx->addr_cur = blob_next(rctx->addr_cur);
+       }
+
+       if (!addr)
+               return 0;
+
+       if (inet_pton(AF_INET6, addr, &a.in6)) {
+               memset(dname, 0, sizeof(dname));
+
+               for (i = 0, p = dname; i < 16; i++) {
+                       *p++ = hex[a.in6.s6_addr[15-i] % 16];
+                       *p++ = '.';
+                       *p++ = hex[a.in6.s6_addr[15-i] / 16];
+                       *p++ = '.';
+               }
+
+               p += snprintf(p, p - dname - 1, "ip6.arpa");
+
+               family = AF_INET6;
+               alen = p - dname;
+       }
+       else if (inet_pton(AF_INET, addr, &a.in)) {
+               family = AF_INET;
+               alen = snprintf(dname, sizeof(dname), "%u.%u.%u.%u.in-addr.arpa",
+                               a.uchar[3], a.uchar[2], a.uchar[1], a.uchar[0]);
+       }
+       else {
+               return -EINVAL;
+       }
+
+       alen = res_mkquery(QUERY, dname, C_IN, T_PTR, NULL, 0, NULL,
+                          msg.buf, sizeof(msg.buf));
+
+       if (alen < 0)
+               return alen;
+
+       if (avl_find(&rctx->request_addrs, &a.in6))
+               return -ENOTUNIQ;
+
+       if (send(rctx->socket.fd, msg.buf, alen, 0) != alen)
+               return -errno;
+
+       req = calloc(1, sizeof(*req));
+
+       if (!req)
+               return -ENOMEM;
+
+       req->id = msg.hdr.id;
+       req->by_id.key = &req->id;
+       avl_insert(&rctx->request_ids, &req->by_id);
+
+       req->family = family;
+       req->addr.in6 = a.in6;
+       req->by_addr.key = &req->addr.in6;
+       avl_insert(&rctx->request_addrs, &req->by_addr);
+
+       return 0;
+}
+
+static void
+rdns_shutdown(struct rrdns_context *rctx)
+{
+       struct rrdns_request *req, *tmp;
+
+       uloop_timeout_cancel(&rctx->timeout);
+       uloop_fd_delete(&rctx->socket);
+
+       close(rctx->socket.fd);
+
+       ubus_send_reply(rctx->context, &rctx->request, rctx->blob.head);
+       ubus_complete_deferred_request(rctx->context, &rctx->request,
+                                      UBUS_STATUS_OK);
+
+       avl_remove_all_elements(&rctx->request_addrs, req, by_addr, tmp)
+               free(req);
+
+       blob_buf_free(&rctx->blob);
+       free(rctx);
+}
+
+static void
+rrdns_handle_timeout(struct uloop_timeout *utm)
+{
+       struct rrdns_context *rctx =
+               container_of(utm, struct rrdns_context, timeout);
+
+       rdns_shutdown(rctx);
+}
+
+static void
+rrdns_handle_response(struct uloop_fd *ufd, unsigned int ev)
+{
+       struct rrdns_context *rctx =
+               container_of(ufd, struct rrdns_context, socket);
+
+       int err = rrdns_parse_response(rctx);
+
+       if (err != -ENODATA && err != -ENOENT)
+               rrdns_next_query(rctx);
+
+       if (avl_is_empty(&rctx->request_ids))
+               rdns_shutdown(rctx);
+}
+
+static char *
+rrdns_find_nameserver(void)
+{
+       static char line[2*INET6_ADDRSTRLEN];
+       struct in6_addr in6;
+       FILE *resolvconf;
+       char *p;
+
+       resolvconf = fopen("/etc/resolv.conf", "r");
+
+       if (!resolvconf)
+               return NULL;
+
+       while (fgets(line, sizeof(line), resolvconf)) {
+               p = strtok(line, " \t");
+
+               if (!p || strcmp(p, "nameserver"))
+                       continue;
+
+               p = strtok(NULL, " \t\r\n");
+
+               if (!p)
+                       continue;
+
+               if (!inet_pton(AF_INET6, p, &in6) && !inet_pton(AF_INET, p, &in6))
+                       continue;
+
+               fclose(resolvconf);
+               return p;
+       }
+
+       fclose(resolvconf);
+       return NULL;
+}
+
+static int
+rpc_rrdns_lookup(struct ubus_context *ctx, struct ubus_object *obj,
+                    struct ubus_request_data *req, const char *method,
+                    struct blob_attr *msg)
+{
+       int port = 53, limit = RRDNS_DEF_LIMIT, timeout = RRDNS_DEF_TIMEOUT;
+       struct blob_attr *tb[__RPC_L_MAX];
+       struct rrdns_context *rctx;
+       const char *server = NULL;
+
+       blobmsg_parse(rpc_lookup_policy, __RPC_L_MAX, tb,
+                     blob_data(msg), blob_len(msg));
+
+       if (tb[RPC_L_PORT])
+               port = blobmsg_get_u16(tb[RPC_L_PORT]);
+
+       if (tb[RPC_L_LIMIT])
+               limit = blobmsg_get_u32(tb[RPC_L_LIMIT]);
+
+       if (tb[RPC_L_TIMEOUT])
+               timeout = blobmsg_get_u32(tb[RPC_L_TIMEOUT]);
+
+       if (tb[RPC_L_SERVER])
+               server = blobmsg_get_string(tb[RPC_L_SERVER]);
+
+
+       if (!tb[RPC_L_ADDRS])
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
+       if (port <= 0)
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
+       if (limit <= 0 || limit > RRDNS_MAX_LIMIT)
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
+       if (timeout <= 0 || timeout > RRDNS_MAX_TIMEOUT)
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
+
+       if (!server || !*server)
+               server = rrdns_find_nameserver();
+
+       if (!server)
+               return UBUS_STATUS_NOT_FOUND;
+
+       rctx = calloc(1, sizeof(*rctx));
+
+       if (!rctx)
+               return UBUS_STATUS_UNKNOWN_ERROR;
+
+       rctx->socket.fd = usock(USOCK_UDP, server, usock_port(port));
+
+       if (rctx->socket.fd < 0) {
+               free(rctx);
+               return UBUS_STATUS_UNKNOWN_ERROR;
+       }
+
+       rctx->context = ctx;
+       rctx->addr_cur = blobmsg_data(tb[RPC_L_ADDRS]);
+       rctx->addr_rem = blobmsg_data_len(tb[RPC_L_ADDRS]);
+
+       avl_init(&rctx->request_ids, rrdns_cmp_id, false, NULL);
+       avl_init(&rctx->request_addrs, rrdns_cmp_addr, false, NULL);
+
+       rctx->timeout.cb = rrdns_handle_timeout;
+       uloop_timeout_set(&rctx->timeout, timeout);
+
+       rctx->socket.cb = rrdns_handle_response;
+       uloop_fd_add(&rctx->socket, ULOOP_READ);
+
+       blob_buf_init(&rctx->blob, 0);
+
+       while (limit--)
+               rrdns_next_query(rctx);
+
+       ubus_defer_request(ctx, req, &rctx->request);
+
+       return UBUS_STATUS_OK;
+}
+
+
+static int
+rpc_rrdns_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
+{
+       static const struct ubus_method rrdns_methods[] = {
+               UBUS_METHOD("lookup", rpc_rrdns_lookup, rpc_lookup_policy),
+       };
+
+       static struct ubus_object_type rrdns_type =
+               UBUS_OBJECT_TYPE("rpcd-rrdns", rrdns_methods);
+
+       static struct ubus_object obj = {
+               .name = "network.rrdns",
+               .type = &rrdns_type,
+               .methods = rrdns_methods,
+               .n_methods = ARRAY_SIZE(rrdns_methods),
+       };
+
+       return ubus_add_object(ctx, &obj);
+}
+
+struct rpc_plugin rpc_plugin = {
+       .init = rpc_rrdns_api_init
+};
diff --git a/libs/rpcd-mod-rrdns/src/rrdns.h b/libs/rpcd-mod-rrdns/src/rrdns.h
new file mode 100644 (file)
index 0000000..3f95116
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * rrdns - Rapid Reverse DNS lookup plugin for the UBUS RPC server
+ *
+ *   Copyright (C) 2016-2017 Jo-Philipp Wich <jo@mein.io>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <libubus.h>
+#include <libubox/avl.h>
+#include <libubox/uloop.h>
+
+#define RRDNS_MAX_TIMEOUT 5000
+#define RRDNS_DEF_TIMEOUT 250
+
+#define RRDNS_MAX_LIMIT 1000
+#define RRDNS_DEF_LIMIT 10
+
+
+struct rrdns_request {
+       struct avl_node by_id;
+       struct avl_node by_addr;
+       uint16_t id;
+       uint16_t family;
+       union {
+               struct in_addr in;
+               struct in6_addr in6;
+       } addr;
+};
+
+struct rrdns_context {
+       struct ubus_context *context;
+       struct ubus_request_data request;
+       struct uloop_timeout timeout;
+       struct blob_attr *addr_cur;
+       int addr_rem;
+       struct uloop_fd socket;
+       struct blob_buf blob;
+       struct avl_tree request_ids;
+       struct avl_tree request_addrs;
+};
index 0bd1945..1b684aa 100644 (file)
@@ -14,8 +14,6 @@ uci = require "luci.model.uci"
 i18n = require "luci.i18n"
 _M.fs = fs
 
-authenticator = {}
-
 -- Index table
 local index = nil
 
@@ -101,24 +99,6 @@ function error500(message)
        return false
 end
 
-function authenticator.htmlauth(validator, accs, default, template)
-       local user = http.formvalue("luci_username")
-       local pass = http.formvalue("luci_password")
-
-       if user and validator(user, pass) then
-               return user
-       end
-
-       require("luci.i18n")
-       require("luci.template")
-       context.path = {}
-       http.status(403, "Forbidden")
-       luci.template.render(template or "sysauth", {duser=default, fuser=user})
-
-       return false
-
-end
-
 function httpdispatch(request, prefix)
        http.context.request = request
 
@@ -188,6 +168,44 @@ function test_post_security()
        return true
 end
 
+local function session_retrieve(sid, allowed_users)
+       local sdat = util.ubus("session", "get", { ubus_rpc_session = sid })
+
+       if type(sdat) == "table" and
+          type(sdat.values) == "table" and
+          type(sdat.values.token) == "string" and
+          (not allowed_users or
+           util.contains(allowed_users, sdat.values.username))
+       then
+               return sid, sdat.values
+       end
+
+       return nil, nil
+end
+
+local function session_setup(user, pass, allowed_users)
+       if util.contains(allowed_users, user) then
+               local login = util.ubus("session", "login", {
+                       username = user,
+                       password = pass,
+                       timeout  = tonumber(luci.config.sauth.sessiontime)
+               })
+
+               if type(login) == "table" and
+                  type(login.ubus_rpc_session) == "string"
+               then
+                       util.ubus("session", "set", {
+                               ubus_rpc_session = login.ubus_rpc_session,
+                               values = { token = sys.uniqueid(16) }
+                       })
+
+                       return session_retrieve(login.ubus_rpc_session)
+               end
+       end
+
+       return nil, nil
+end
+
 function dispatch(request)
        --context._disable_memtrace = require "luci.debug".trap_memtrace("l")
        local ctx = context
@@ -332,74 +350,65 @@ function dispatch(request)
        )
 
        if track.sysauth then
-               local authen = type(track.sysauth_authenticator) == "function"
-                and track.sysauth_authenticator
-                or authenticator[track.sysauth_authenticator]
+               local authen = track.sysauth_authenticator
+               local _, sid, sdat, default_user, allowed_users
 
-               local def  = (type(track.sysauth) == "string") and track.sysauth
-               local accs = def and {track.sysauth} or track.sysauth
-               local sess = ctx.authsession
-               if not sess then
-                       sess = http.getcookie("sysauth")
-                       sess = sess and sess:match("^[a-f0-9]*$")
+               if type(authen) == "string" and authen ~= "htmlauth" then
+                       error500("Unsupported authenticator %q configured" % authen)
+                       return
                end
 
-               local sdat = (util.ubus("session", "get", { ubus_rpc_session = sess }) or { }).values
-               local user, token
+               if type(track.sysauth) == "table" then
+                       default_user, allowed_users = nil, track.sysauth
+               else
+                       default_user, allowed_users = track.sysauth, { track.sysauth }
+               end
 
-               if sdat then
-                       user = sdat.user
-                       token = sdat.token
+               if type(authen) == "function" then
+                       _, sid = authen(sys.user.checkpasswd, allowed_users)
                else
-                       local eu = http.getenv("HTTP_AUTH_USER")
-                       local ep = http.getenv("HTTP_AUTH_PASS")
-                       if eu and ep and sys.user.checkpasswd(eu, ep) then
-                               authen = function() return eu end
-                       end
+                       sid = http.getcookie("sysauth")
                end
 
-               if not util.contains(accs, user) then
-                       if authen then
-                               local user, sess = authen(sys.user.checkpasswd, accs, def, track.sysauth_template)
-                               local token
-                               if not user or not util.contains(accs, user) then
-                                       return
-                               else
-                                       if not sess then
-                                               local sdat = util.ubus("session", "create", { timeout = tonumber(luci.config.sauth.sessiontime) })
-                                               if sdat then
-                                                       token = sys.uniqueid(16)
-                                                       util.ubus("session", "set", {
-                                                               ubus_rpc_session = sdat.ubus_rpc_session,
-                                                               values = {
-                                                                       user = user,
-                                                                       token = token,
-                                                                       section = sys.uniqueid(16)
-                                                               }
-                                                       })
-                                                       sess = sdat.ubus_rpc_session
-                                               end
-                                       end
+               sid, sdat = session_retrieve(sid, allowed_users)
 
-                                       if sess and token then
-                                               http.header("Set-Cookie", 'sysauth=%s; path=%s' %{ sess, build_url() })
+               if not (sid and sdat) and authen == "htmlauth" then
+                       local user = http.getenv("HTTP_AUTH_USER")
+                       local pass = http.getenv("HTTP_AUTH_PASS")
 
-                                               ctx.authsession = sess
-                                               ctx.authtoken = token
-                                               ctx.authuser = user
+                       if user == nil and pass == nil then
+                               user = http.formvalue("luci_username")
+                               pass = http.formvalue("luci_password")
+                       end
+
+                       sid, sdat = session_setup(user, pass, allowed_users)
+
+                       if not sid then
+                               local tmpl = require "luci.template"
+
+                               context.path = {}
 
-                                               http.redirect(build_url(unpack(ctx.requestpath)))
-                                       end
-                               end
-                       else
                                http.status(403, "Forbidden")
+                               tmpl.render(track.sysauth_template or "sysauth", {
+                                       duser = default_user,
+                                       fuser = user
+                               })
+
                                return
                        end
-               else
-                       ctx.authsession = sess
-                       ctx.authtoken = token
-                       ctx.authuser = user
+
+                       http.header("Set-Cookie", 'sysauth=%s; path=%s' %{ sid, build_url() })
+                       http.redirect(build_url(unpack(ctx.requestpath)))
                end
+
+               if not sid or not sdat then
+                       http.status(403, "Forbidden")
+                       return
+               end
+
+               ctx.authsession = sid
+               ctx.authtoken = sdat.token
+               ctx.authuser = sdat.username
        end
 
        if c and require_post_security(c.target) then
index a972717..99f3ee2 100644 (file)
@@ -117,45 +117,12 @@ end
 
 net = {}
 
---                     The following fields are defined for arp entry objects:
---                     { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
-function net.arptable(callback)
-       local arp = (not callback) and {} or nil
-       local e, r, v
-       if fs.access("/proc/net/arp") then
-               for e in io.lines("/proc/net/arp") do
-                       local r = { }, v
-                       for v in e:gmatch("%S+") do
-                               r[#r+1] = v
-                       end
-
-                       if r[1] ~= "IP" then
-                               local x = {
-                                       ["IP address"] = r[1],
-                                       ["HW type"]    = r[2],
-                                       ["Flags"]      = r[3],
-                                       ["HW address"] = r[4],
-                                       ["Mask"]       = r[5],
-                                       ["Device"]     = r[6]
-                               }
-
-                               if callback then
-                                       callback(x)
-                               else
-                                       arp = arp or { }
-                                       arp[#arp+1] = x
-                               end
-                       end
-               end
-       end
-       return arp
-end
-
 local function _nethints(what, callback)
        local _, k, e, mac, ip, name
        local cur = uci.cursor()
        local ifn = { }
        local hosts = { }
+       local lookup = { }
 
        local function _add(i, ...)
                local k = select(i, ...)
@@ -224,8 +191,20 @@ local function _nethints(what, callback)
                end
        end
 
+       for _, e in pairs(hosts) do
+               lookup[#lookup+1] = (what > 1) and e[what] or (e[2] or e[3])
+       end
+
+       if #lookup > 0 then
+               lookup = luci.util.ubus("network.rrdns", "lookup", {
+                       addrs   = lookup,
+                       timeout = 250,
+                       limit   = 1000
+               }) or { }
+       end
+
        for _, e in luci.util.kspairs(hosts) do
-               callback(e[1], e[2], e[3], e[4])
+               callback(e[1], e[2], e[3], lookup[e[2]] or lookup[e[3]] or e[4])
        end
 end
 
@@ -234,17 +213,17 @@ end
 function net.mac_hints(callback)
        if callback then
                _nethints(1, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
+                       name = name or v4
                        if name and name ~= mac then
-                               callback(mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4)
+                               callback(mac, name or v4)
                        end
                end)
        else
                local rv = { }
                _nethints(1, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
+                       name = name or v4
                        if name and name ~= mac then
-                               rv[#rv+1] = { mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 }
+                               rv[#rv+1] = { mac, name or v4 }
                        end
                end)
                return rv
@@ -256,7 +235,7 @@ end
 function net.ipv4_hints(callback)
        if callback then
                _nethints(2, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v4, nil, 100) or mac
+                       name = name or mac
                        if name and name ~= v4 then
                                callback(v4, name)
                        end
@@ -264,7 +243,7 @@ function net.ipv4_hints(callback)
        else
                local rv = { }
                _nethints(2, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v4, nil, 100) or mac
+                       name = name or mac
                        if name and name ~= v4 then
                                rv[#rv+1] = { v4, name }
                        end
@@ -278,7 +257,7 @@ end
 function net.ipv6_hints(callback)
        if callback then
                _nethints(3, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v6, nil, 100) or mac
+                       name = name or mac
                        if name and name ~= v6 then
                                callback(v6, name)
                        end
@@ -286,7 +265,7 @@ function net.ipv6_hints(callback)
        else
                local rv = { }
                _nethints(3, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v6, nil, 100) or mac
+                       name = name or mac
                        if name and name ~= v6 then
                                rv[#rv+1] = { v6, name }
                        end
@@ -378,145 +357,6 @@ function net.devices()
 end
 
 
-function net.deviceinfo()
-       local devs = {}
-       for k, v in ipairs(nixio.getifaddrs()) do
-               if v.family == "packet" then
-                       local d = v.data
-                       d[1] = d.rx_bytes
-                       d[2] = d.rx_packets
-                       d[3] = d.rx_errors
-                       d[4] = d.rx_dropped
-                       d[5] = 0
-                       d[6] = 0
-                       d[7] = 0
-                       d[8] = d.multicast
-                       d[9] = d.tx_bytes
-                       d[10] = d.tx_packets
-                       d[11] = d.tx_errors
-                       d[12] = d.tx_dropped
-                       d[13] = 0
-                       d[14] = d.collisions
-                       d[15] = 0
-                       d[16] = 0
-                       devs[v.name] = d
-               end
-       end
-       return devs
-end
-
-
---                     The following fields are defined for route entry tables:
---                     { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
---                       "flags", "device" }
-function net.routes(callback)
-       local routes = { }
-
-       for line in io.lines("/proc/net/route") do
-
-               local dev, dst_ip, gateway, flags, refcnt, usecnt, metric,
-                         dst_mask, mtu, win, irtt = line:match(
-                       "([^%s]+)\t([A-F0-9]+)\t([A-F0-9]+)\t([A-F0-9]+)\t" ..
-                       "(%d+)\t(%d+)\t(%d+)\t([A-F0-9]+)\t(%d+)\t(%d+)\t(%d+)"
-               )
-
-               if dev then
-                       gateway  = luci.ip.Hex( gateway,  32, luci.ip.FAMILY_INET4 )
-                       dst_mask = luci.ip.Hex( dst_mask, 32, luci.ip.FAMILY_INET4 )
-                       dst_ip   = luci.ip.Hex(
-                               dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4
-                       )
-
-                       local rt = {
-                               dest     = dst_ip,
-                               gateway  = gateway,
-                               metric   = tonumber(metric),
-                               refcount = tonumber(refcnt),
-                               usecount = tonumber(usecnt),
-                               mtu      = tonumber(mtu),
-                               window   = tonumber(window),
-                               irtt     = tonumber(irtt),
-                               flags    = tonumber(flags, 16),
-                               device   = dev
-                       }
-
-                       if callback then
-                               callback(rt)
-                       else
-                               routes[#routes+1] = rt
-                       end
-               end
-       end
-
-       return routes
-end
-
---                     The following fields are defined for route entry tables:
---                     { "source", "dest", "nexthop", "metric", "refcount", "usecount",
---                       "flags", "device" }
-function net.routes6(callback)
-       if fs.access("/proc/net/ipv6_route", "r") then
-               local routes = { }
-
-               for line in io.lines("/proc/net/ipv6_route") do
-
-                       local dst_ip, dst_prefix, src_ip, src_prefix, nexthop,
-                                 metric, refcnt, usecnt, flags, dev = line:match(
-                               "([a-f0-9]+) ([a-f0-9]+) " ..
-                               "([a-f0-9]+) ([a-f0-9]+) " ..
-                               "([a-f0-9]+) ([a-f0-9]+) " ..
-                               "([a-f0-9]+) ([a-f0-9]+) " ..
-                               "([a-f0-9]+) +([^%s]+)"
-                       )
-
-                       if dst_ip and dst_prefix and
-                          src_ip and src_prefix and
-                          nexthop and metric and
-                          refcnt and usecnt and
-                          flags and dev
-                       then
-                               src_ip = luci.ip.Hex(
-                                       src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false
-                               )
-
-                               dst_ip = luci.ip.Hex(
-                                       dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false
-                               )
-
-                               nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false )
-
-                               local rt = {
-                                       source   = src_ip,
-                                       dest     = dst_ip,
-                                       nexthop  = nexthop,
-                                       metric   = tonumber(metric, 16),
-                                       refcount = tonumber(refcnt, 16),
-                                       usecount = tonumber(usecnt, 16),
-                                       flags    = tonumber(flags, 16),
-                                       device   = dev,
-
-                                       -- lua number is too small for storing the metric
-                                       -- add a metric_raw field with the original content
-                                       metric_raw = metric
-                               }
-
-                               if callback then
-                                       callback(rt)
-                               else
-                                       routes[#routes+1] = rt
-                               end
-                       end
-               end
-
-               return routes
-       end
-end
-
-function net.pingtest(host)
-       return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
-end
-
-
 process = {}
 
 function process.info(key)
index ad575e0..22e1b7e 100644 (file)
@@ -139,14 +139,12 @@ function action_connections()
 end
 
 function action_nameinfo(...)
-       local i
-       local rv = { }
-       for i = 1, select('#', ...) do
-               local addr = select(i, ...)
-               local fqdn = nixio.getnameinfo(addr)
-               rv[addr] = fqdn or (addr:match(":") and "[%s]" % addr or addr)
-       end
+       local util = require "luci.util"
 
        luci.http.prepare_content("application/json")
-       luci.http.write_json(rv)
+       luci.http.write_json(util.ubus("network.rrdns", "lookup", {
+               addrs = { ... },
+               timeout = 5000,
+               limit = 1000
+       }) or { })
 end
index c895430..7bc4df8 100644 (file)
@@ -5,15 +5,40 @@
 local wa  = require "luci.tools.webadmin"
 local sys = require "luci.sys"
 local fs  = require "nixio.fs"
+local nx  = require "nixio"
 
 local has_pptp  = fs.access("/usr/sbin/pptp")
 local has_pppoe = fs.glob("/usr/lib/pppd/*/rp-pppoe.so")()
 
 local network = luci.model.uci.cursor_state():get_all("network")
 
-local netstat = sys.net.deviceinfo()
+local netstat = {}
 local ifaces = {}
 
+local k, v
+for k, v in ipairs(nx.getifaddrs()) do
+       if v.family == "packet" then
+               local d = v.data
+               d[1] = d.rx_bytes
+               d[2] = d.rx_packets
+               d[3] = d.rx_errors
+               d[4] = d.rx_dropped
+               d[5] = 0
+               d[6] = 0
+               d[7] = 0
+               d[8] = d.multicast
+               d[9] = d.tx_bytes
+               d[10] = d.tx_packets
+               d[11] = d.tx_errors
+               d[12] = d.tx_dropped
+               d[13] = 0
+               d[14] = d.collisions
+               d[15] = 0
+               d[16] = 0
+               netstat[v.name] = d
+       end
+end
+
 for k, v in pairs(network) do
        if v[".type"] == "interface" and k ~= "loopback" then
                table.insert(ifaces, v)
index 84669dc..e2291e5 100644 (file)
@@ -70,7 +70,7 @@ function index()
        page.target = cbi("freifunk/basics")
        page.title  = _("Basic Settings")
        page.order  = 5
-       
+
        page        = node("admin", "freifunk", "basics", "profile")
        page.target = cbi("freifunk/profile")
        page.title  = _("Profile")
@@ -102,7 +102,7 @@ function zeroes()
        local zeroes = string.rep(string.char(0), 8192)
        local cnt = 0
        local lim = 1024 * 1024 * 1024
-       
+
        http.prepare_content("application/x-many-zeroes")
 
        while cnt < lim do
@@ -188,7 +188,6 @@ function jsonstatus()
        root.network = {}
        root.wireless = {devices = {}, interfaces = {}, status = {}}
        local wifs = root.wireless.interfaces
-       local netdata = luci.sys.net.deviceinfo() or {}
 
        for _, vif in ipairs(ffwifs) do
                root.network[vif] = cursor:get_all("network", vif)