diff options
24 files changed, 1263 insertions, 297 deletions
diff --git a/applications/luci-app-adblock/luasrc/model/cbi/adblock/overview_tab.lua b/applications/luci-app-adblock/luasrc/model/cbi/adblock/overview_tab.lua index 38086c9eb4..68f9c8897e 100644 --- a/applications/luci-app-adblock/luasrc/model/cbi/adblock/overview_tab.lua +++ b/applications/luci-app-adblock/luasrc/model/cbi/adblock/overview_tab.lua @@ -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 index 0000000000..3a886eb8de --- /dev/null +++ b/applications/luci-app-advanced-reboot/Makefile @@ -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 index 0000000000..ee87a00020 --- /dev/null +++ b/applications/luci-app-advanced-reboot/README.md @@ -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) + + +## 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 index 0000000000..2b55217b8e --- /dev/null +++ b/applications/luci-app-advanced-reboot/luasrc/controller/advanced_reboot.lua @@ -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 index 0000000000..206d250543 --- /dev/null +++ b/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/advanced_reboot.htm @@ -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 index 0000000000..6325934094 --- /dev/null +++ b/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/alternative_reboot.htm @@ -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 index 0000000000..0ddea11e65 --- /dev/null +++ b/applications/luci-app-advanced-reboot/luasrc/view/advanced_reboot/power_off.htm @@ -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 index 0000000000..9c810892c2 --- /dev/null +++ b/applications/luci-app-advanced-reboot/po/templates/luci-app-advanced-reboot.pot @@ -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 "" diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua index 76435e2f14..97ce83f771 100644 --- a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua +++ b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua @@ -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")) diff --git a/applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua b/applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua index 443f20b0f4..ec26f02d0d 100644 --- a/applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua +++ b/applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua @@ -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 index 0000000000..49e28c7cfd --- /dev/null +++ b/applications/luci-app-statistics/luasrc/model/cbi/luci_statistics/apcups.lua @@ -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 index 0000000000..04eee93051 --- /dev/null +++ b/applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua @@ -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 diff --git a/applications/luci-app-statistics/root/etc/config/luci_statistics b/applications/luci-app-statistics/root/etc/config/luci_statistics index c081a8e724..8cc918e3db 100644 --- a/applications/luci-app-statistics/root/etc/config/luci_statistics +++ b/applications/luci-app-statistics/root/etc/config/luci_statistics @@ -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' diff --git a/applications/luci-app-statistics/root/usr/bin/stat-genconfig b/applications/luci-app-statistics/root/usr/bin/stat-genconfig index 090344cee4..2bf63c1fe6 100755 --- a/applications/luci-app-statistics/root/usr/bin/stat-genconfig +++ b/applications/luci-app-statistics/root/usr/bin/stat-genconfig @@ -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 diff --git a/collections/luci/Makefile b/collections/luci/Makefile index 6f6c3a989d..9b495c3187 100644 --- a/collections/luci/Makefile +++ b/collections/luci/Makefile @@ -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 index 0000000000..f0bf140a87 --- /dev/null +++ b/libs/rpcd-mod-rrdns/Makefile @@ -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 index 0000000000..ace6ac8664 --- /dev/null +++ b/libs/rpcd-mod-rrdns/src/CMakeLists.txt @@ -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 index 0000000000..691db9c8fa --- /dev/null +++ b/libs/rpcd-mod-rrdns/src/rrdns.c @@ -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 index 0000000000..3f95116f08 --- /dev/null +++ b/libs/rpcd-mod-rrdns/src/rrdns.h @@ -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; +}; diff --git a/modules/luci-base/luasrc/dispatcher.lua b/modules/luci-base/luasrc/dispatcher.lua index 0bd19456f2..1b684aa79c 100644 --- a/modules/luci-base/luasrc/dispatcher.lua +++ b/modules/luci-base/luasrc/dispatcher.lua @@ -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 diff --git a/modules/luci-base/luasrc/sys.lua b/modules/luci-base/luasrc/sys.lua index a97271732a..99f3ee2919 100644 --- a/modules/luci-base/luasrc/sys.lua +++ b/modules/luci-base/luasrc/sys.lua @@ -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) diff --git a/modules/luci-mod-admin-full/luasrc/controller/admin/status.lua b/modules/luci-mod-admin-full/luasrc/controller/admin/status.lua index ad575e0d26..22e1b7e173 100644 --- a/modules/luci-mod-admin-full/luasrc/controller/admin/status.lua +++ b/modules/luci-mod-admin-full/luasrc/controller/admin/status.lua @@ -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 diff --git a/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/network.lua b/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/network.lua index c895430a3b..7bc4df859b 100644 --- a/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/network.lua +++ b/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/network.lua @@ -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) diff --git a/modules/luci-mod-freifunk/luasrc/controller/freifunk/freifunk.lua b/modules/luci-mod-freifunk/luasrc/controller/freifunk/freifunk.lua index 84669dce84..e2291e5ca6 100644 --- a/modules/luci-mod-freifunk/luasrc/controller/freifunk/freifunk.lua +++ b/modules/luci-mod-freifunk/luasrc/controller/freifunk/freifunk.lua @@ -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) |