summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-mod-status
diff options
context:
space:
mode:
authorDaniel F. Dickinson <cshored@thecshore.com>2018-08-03 12:36:51 -0400
committerJo-Philipp Wich <jo@mein.io>2018-09-19 20:08:19 +0200
commit58d97b5e271bc0d7507eab5b9bd2902181864e02 (patch)
tree80e250346ad33c79b3f821daf7b7d9be90d99240 /modules/luci-mod-status
parent6ec0353201435e0d0d7d32820d8ba600b4ca7b5b (diff)
modules: Split luci-mod-full
Move some common elements to luci-base, and otherwise make three packages out of status, system, and network. They were mostly separated already, but there were some shared elements between status and network that are now in luci-base. Signed-off-by: Daniel F. Dickinson <cshored@thecshore.com>
Diffstat (limited to 'modules/luci-mod-status')
-rw-r--r--modules/luci-mod-status/Makefile18
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/bandwidth.svg16
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/connections.svg17
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/load.svg17
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/wifirate.svg15
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/wireless.svg16
-rw-r--r--modules/luci-mod-status/luasrc/controller/admin/status.lua153
-rw-r--r--modules/luci-mod-status/luasrc/model/cbi/admin_status/processes.lua34
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/bandwidth.htm305
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/connections.htm376
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/dmesg.htm12
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/index.htm487
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/iptables.htm155
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/load.htm285
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/routes.htm156
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/syslog.htm12
-rw-r--r--modules/luci-mod-status/luasrc/view/admin_status/wireless.htm371
-rw-r--r--modules/luci-mod-status/src/Makefile14
-rw-r--r--modules/luci-mod-status/src/luci-bwc.c778
19 files changed, 3237 insertions, 0 deletions
diff --git a/modules/luci-mod-status/Makefile b/modules/luci-mod-status/Makefile
new file mode 100644
index 0000000000..cf8c8ddf83
--- /dev/null
+++ b/modules/luci-mod-status/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2008-2014 The LuCI Team <luci@lists.subsignal.org>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI Status Pages
+LUCI_DEPENDS:=+luci-base +libiwinfo +libiwinfo-lua
+
+PKG_BUILD_DEPENDS:=iwinfo
+PKG_LICENSE:=Apache-2.0
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/bandwidth.svg b/modules/luci-mod-status/htdocs/luci-static/resources/bandwidth.svg
new file mode 100644
index 0000000000..5a121b85c6
--- /dev/null
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/bandwidth.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <polyline id="rx" points="" style="fill:blue;fill-opacity:0.4;stroke:blue;stroke-width:1" />
+ <polyline id="tx" points="" style="fill:green;fill-opacity:0.4;stroke:green;stroke-width:1" />
+
+ <line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+</svg>
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/connections.svg b/modules/luci-mod-status/htdocs/luci-static/resources/connections.svg
new file mode 100644
index 0000000000..5794e79426
--- /dev/null
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/connections.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <polyline id="tcp" points="" style="fill:green;fill-opacity:0.4;stroke:green;stroke-width:1" />
+ <polyline id="udp" points="" style="fill:blue;fill-opacity:0.4;stroke:blue;stroke-width:1" />
+ <polyline id="other" points="" style="fill:red;fill-opacity:0.4;stroke:red;stroke-width:1" />
+
+ <line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+</svg>
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/load.svg b/modules/luci-mod-status/htdocs/luci-static/resources/load.svg
new file mode 100644
index 0000000000..716d37617f
--- /dev/null
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/load.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <polyline id="load01" points="" style="fill:#ff0000;fill-opacity:0.4;stroke:#ff0000;stroke-width:1" />
+ <polyline id="load05" points="" style="fill:#ff6600;fill-opacity:0.4;stroke:#ff6600;stroke-width:1" />
+ <polyline id="load15" points="" style="fill:#ffaa00;fill-opacity:0.4;stroke:#ffaa00;stroke-width:1" />
+
+ <line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+</svg>
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/wifirate.svg b/modules/luci-mod-status/htdocs/luci-static/resources/wifirate.svg
new file mode 100644
index 0000000000..e75ea614c9
--- /dev/null
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/wifirate.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <polyline id="rate" points="" style="fill:green;fill-opacity:0.4;stroke:green;stroke-width:1" />
+
+ <line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+</svg>
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/wireless.svg b/modules/luci-mod-status/htdocs/luci-static/resources/wireless.svg
new file mode 100644
index 0000000000..00cc2a12f1
--- /dev/null
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/wireless.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <polyline id="rssi" points="" style="fill:blue;fill-opacity:0.4;stroke:blue;stroke-width:1" />
+ <polyline id="noise" points="" style="fill:red;fill-opacity:0.4;stroke:red;stroke-width:1" />
+
+ <line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+
+ <line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
+ <text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
+</svg>
diff --git a/modules/luci-mod-status/luasrc/controller/admin/status.lua b/modules/luci-mod-status/luasrc/controller/admin/status.lua
new file mode 100644
index 0000000000..4f04cce545
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/controller/admin/status.lua
@@ -0,0 +1,153 @@
+-- Copyright 2008 Steven Barth <steven@midlink.org>
+-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
+-- Licensed to the public under the Apache License 2.0.
+
+module("luci.controller.admin.status", package.seeall)
+
+function index()
+ entry({"admin", "status", "overview"}, template("admin_status/index"), _("Overview"), 1)
+
+ entry({"admin", "status", "iptables"}, template("admin_status/iptables"), _("Firewall"), 2).leaf = true
+ entry({"admin", "status", "iptables_action"}, post("action_iptables")).leaf = true
+
+ entry({"admin", "status", "routes"}, template("admin_status/routes"), _("Routes"), 3)
+ entry({"admin", "status", "syslog"}, call("action_syslog"), _("System Log"), 4)
+ entry({"admin", "status", "dmesg"}, call("action_dmesg"), _("Kernel Log"), 5)
+ entry({"admin", "status", "processes"}, form("admin_status/processes"), _("Processes"), 6)
+
+ entry({"admin", "status", "realtime"}, alias("admin", "status", "realtime", "load"), _("Realtime Graphs"), 7)
+
+ entry({"admin", "status", "realtime", "load"}, template("admin_status/load"), _("Load"), 1).leaf = true
+ entry({"admin", "status", "realtime", "load_status"}, call("action_load")).leaf = true
+
+ entry({"admin", "status", "realtime", "bandwidth"}, template("admin_status/bandwidth"), _("Traffic"), 2).leaf = true
+ entry({"admin", "status", "realtime", "bandwidth_status"}, call("action_bandwidth")).leaf = true
+
+ if nixio.fs.access("/etc/config/wireless") then
+ entry({"admin", "status", "realtime", "wireless"}, template("admin_status/wireless"), _("Wireless"), 3).leaf = true
+ entry({"admin", "status", "realtime", "wireless_status"}, call("action_wireless")).leaf = true
+ end
+
+ entry({"admin", "status", "realtime", "connections"}, template("admin_status/connections"), _("Connections"), 4).leaf = true
+ entry({"admin", "status", "realtime", "connections_status"}, call("action_connections")).leaf = true
+
+ entry({"admin", "status", "nameinfo"}, call("action_nameinfo")).leaf = true
+end
+
+function action_syslog()
+ local syslog = luci.sys.syslog()
+ luci.template.render("admin_status/syslog", {syslog=syslog})
+end
+
+function action_dmesg()
+ local dmesg = luci.sys.dmesg()
+ luci.template.render("admin_status/dmesg", {dmesg=dmesg})
+end
+
+function action_iptables()
+ if luci.http.formvalue("zero") then
+ if luci.http.formvalue("family") == "6" then
+ luci.util.exec("/usr/sbin/ip6tables -Z")
+ else
+ luci.util.exec("/usr/sbin/iptables -Z")
+ end
+ elseif luci.http.formvalue("restart") then
+ luci.util.exec("/etc/init.d/firewall restart")
+ end
+
+ luci.http.redirect(luci.dispatcher.build_url("admin/status/iptables"))
+end
+
+function action_bandwidth(iface)
+ luci.http.prepare_content("application/json")
+
+ local bwc = io.popen("luci-bwc -i %s 2>/dev/null"
+ % luci.util.shellquote(iface))
+
+ if bwc then
+ luci.http.write("[")
+
+ while true do
+ local ln = bwc:read("*l")
+ if not ln then break end
+ luci.http.write(ln)
+ end
+
+ luci.http.write("]")
+ bwc:close()
+ end
+end
+
+function action_wireless(iface)
+ luci.http.prepare_content("application/json")
+
+ local bwc = io.popen("luci-bwc -r %s 2>/dev/null"
+ % luci.util.shellquote(iface))
+
+ if bwc then
+ luci.http.write("[")
+
+ while true do
+ local ln = bwc:read("*l")
+ if not ln then break end
+ luci.http.write(ln)
+ end
+
+ luci.http.write("]")
+ bwc:close()
+ end
+end
+
+function action_load()
+ luci.http.prepare_content("application/json")
+
+ local bwc = io.popen("luci-bwc -l 2>/dev/null")
+ if bwc then
+ luci.http.write("[")
+
+ while true do
+ local ln = bwc:read("*l")
+ if not ln then break end
+ luci.http.write(ln)
+ end
+
+ luci.http.write("]")
+ bwc:close()
+ end
+end
+
+function action_connections()
+ local sys = require "luci.sys"
+
+ luci.http.prepare_content("application/json")
+
+ luci.http.write('{ "connections": ')
+ luci.http.write_json(sys.net.conntrack())
+
+ local bwc = io.popen("luci-bwc -c 2>/dev/null")
+ if bwc then
+ luci.http.write(', "statistics": [')
+
+ while true do
+ local ln = bwc:read("*l")
+ if not ln then break end
+ luci.http.write(ln)
+ end
+
+ luci.http.write("]")
+ bwc:close()
+ end
+
+ luci.http.write(" }")
+end
+
+function action_nameinfo(...)
+ local util = require "luci.util"
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(util.ubus("network.rrdns", "lookup", {
+ addrs = { ... },
+ timeout = 5000,
+ limit = 1000
+ }) or { })
+end
diff --git a/modules/luci-mod-status/luasrc/model/cbi/admin_status/processes.lua b/modules/luci-mod-status/luasrc/model/cbi/admin_status/processes.lua
new file mode 100644
index 0000000000..0a6e48fd8e
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/model/cbi/admin_status/processes.lua
@@ -0,0 +1,34 @@
+-- Copyright 2008 Steven Barth <steven@midlink.org>
+-- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
+-- Licensed to the public under the Apache License 2.0.
+
+f = SimpleForm("processes", translate("Processes"), translate("This list gives an overview over currently running system processes and their status."))
+f.reset = false
+f.submit = false
+
+t = f:section(Table, luci.sys.process.list())
+t:option(DummyValue, "PID", translate("PID"))
+t:option(DummyValue, "USER", translate("Owner"))
+t:option(DummyValue, "COMMAND", translate("Command"))
+t:option(DummyValue, "%CPU", translate("CPU usage (%)"))
+t:option(DummyValue, "%MEM", translate("Memory usage (%)"))
+
+hup = t:option(Button, "_hup", translate("Hang Up"))
+hup.inputstyle = "reload"
+function hup.write(self, section)
+ null, self.tag_error[section] = luci.sys.process.signal(section, 1)
+end
+
+term = t:option(Button, "_term", translate("Terminate"))
+term.inputstyle = "remove"
+function term.write(self, section)
+ null, self.tag_error[section] = luci.sys.process.signal(section, 15)
+end
+
+kill = t:option(Button, "_kill", translate("Kill"))
+kill.inputstyle = "reset"
+function kill.write(self, section)
+ null, self.tag_error[section] = luci.sys.process.signal(section, 9)
+end
+
+return f \ No newline at end of file
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/bandwidth.htm b/modules/luci-mod-status/luasrc/view/admin_status/bandwidth.htm
new file mode 100644
index 0000000000..3bb55f9054
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/bandwidth.htm
@@ -0,0 +1,305 @@
+<%#
+ Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%-
+ local ntm = require "luci.model.network".init()
+
+ local dev
+ local devices = { }
+ for _, dev in luci.util.vspairs(luci.sys.net.devices()) do
+ if dev ~= "lo" and not ntm:ignore_interface(dev) then
+ devices[#devices+1] = dev
+ end
+ end
+
+ local curdev = luci.http.formvalue("dev") or devices[1]
+-%>
+
+<%+header%>
+
+<script type="text/javascript">//<![CDATA[
+ var bwxhr = new XHR();
+
+ var G;
+ var TIME = 0;
+ var RXB = 1;
+ var RXP = 2;
+ var TXB = 3;
+ var TXP = 4;
+
+ var width = 760;
+ var height = 300;
+ var step = 5;
+
+ var data_wanted = Math.floor(width / step);
+ var data_fill = 0;
+ var data_stamp = 0;
+
+ var data_rx = [ ];
+ var data_tx = [ ];
+
+ var line_rx;
+ var line_tx;
+
+ var label_25;
+ var label_50;
+ var label_75;
+
+ var label_rx_cur;
+ var label_rx_avg;
+ var label_rx_peak;
+
+ var label_tx_cur;
+ var label_tx_avg;
+ var label_tx_peak;
+
+ var label_scale;
+
+
+ Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; };
+
+ function bandwidth_label(bytes, br)
+ {
+ var uby = '<%:kB/s%>';
+ var kby = (bytes / 1024);
+
+ if (kby >= 1024)
+ {
+ uby = '<%:MB/s%>';
+ kby = kby / 1024;
+ }
+
+ var ubi = '<%:kbit/s%>';
+ var kbi = (bytes * 8 / 1024);
+
+ if (kbi >= 1024)
+ {
+ ubi = '<%:Mbit/s%>';
+ kbi = kbi / 1024;
+ }
+
+ return String.format("%f %s%s(%f %s)",
+ kbi.toFixed(2), ubi,
+ br ? '<br />' : ' ',
+ kby.toFixed(2), uby
+ );
+ }
+
+ /* wait for SVG */
+ window.setTimeout(
+ function() {
+ var svg = document.getElementById('bwsvg');
+
+ try {
+ G = svg.getSVGDocument
+ ? svg.getSVGDocument() : svg.contentDocument;
+ }
+ catch(e) {
+ G = document.embeds['bwsvg'].getSVGDocument();
+ }
+
+ if (!G)
+ {
+ window.setTimeout(arguments.callee, 1000);
+ }
+ else
+ {
+ /* find sizes */
+ width = svg.offsetWidth - 2;
+ height = svg.offsetHeight - 2;
+ data_wanted = Math.ceil(width / step);
+
+ /* prefill datasets */
+ for (var i = 0; i < data_wanted; i++)
+ {
+ data_rx[i] = 0;
+ data_tx[i] = 0;
+ }
+
+ /* find svg elements */
+ line_rx = G.getElementById('rx');
+ line_tx = G.getElementById('tx');
+
+ label_25 = G.getElementById('label_25');
+ label_50 = G.getElementById('label_50');
+ label_75 = G.getElementById('label_75');
+
+ label_rx_cur = document.getElementById('rx_bw_cur');
+ label_rx_avg = document.getElementById('rx_bw_avg');
+ label_rx_peak = document.getElementById('rx_bw_peak');
+
+ label_tx_cur = document.getElementById('tx_bw_cur');
+ label_tx_avg = document.getElementById('tx_bw_avg');
+ label_tx_peak = document.getElementById('tx_bw_peak');
+
+ label_scale = document.getElementById('scale');
+
+
+ /* plot horizontal time interval lines */
+ for (var i = width % (step * 60); i < width; i += step * 60)
+ {
+ var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
+ line.setAttribute('x1', i);
+ line.setAttribute('y1', 0);
+ line.setAttribute('x2', i);
+ line.setAttribute('y2', '100%');
+ line.setAttribute('style', 'stroke:black;stroke-width:0.1');
+
+ var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
+ text.setAttribute('x', i + 5);
+ text.setAttribute('y', 15);
+ text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
+ text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
+
+ label_25.parentNode.appendChild(line);
+ label_25.parentNode.appendChild(text);
+ }
+
+ label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
+
+ /* render datasets, start update interval */
+ XHR.poll(3, '<%=build_url("admin/status/realtime/bandwidth_status", curdev)%>', null,
+ function(x, data)
+ {
+ var data_max = 0;
+ var data_scale = 0;
+
+ var data_rx_avg = 0;
+ var data_tx_avg = 0;
+
+ var data_rx_peak = 0;
+ var data_tx_peak = 0;
+
+ for (var i = data_stamp ? 0 : 1; i < data.length; i++)
+ {
+ /* skip overlapping entries */
+ if (data[i][TIME] <= data_stamp)
+ continue;
+
+ /* normalize difference against time interval */
+ if (i > 0)
+ {
+ var time_delta = data[i][TIME] - data[i-1][TIME];
+ if (time_delta)
+ {
+ data_rx.push((data[i][RXB] - data[i-1][RXB]) / time_delta);
+ data_tx.push((data[i][TXB] - data[i-1][TXB]) / time_delta);
+ }
+ }
+ }
+
+ /* cut off outdated entries */
+ data_rx = data_rx.slice(data_rx.length - data_wanted, data_rx.length);
+ data_tx = data_tx.slice(data_tx.length - data_wanted, data_tx.length);
+
+ /* find peak */
+ for (var i = 0; i < data_rx.length; i++)
+ {
+ data_max = Math.max(data_max, data_rx[i]);
+ data_max = Math.max(data_max, data_tx[i]);
+
+ data_rx_peak = Math.max(data_rx_peak, data_rx[i]);
+ data_tx_peak = Math.max(data_tx_peak, data_tx[i]);
+
+ data_rx_avg += data_rx[i];
+ data_tx_avg += data_tx[i];
+ }
+
+ data_rx_avg = (data_rx_avg / Math.max(data_rx.length, 1));
+ data_tx_avg = (data_tx_avg / Math.max(data_tx.length, 1));
+
+ var size = Math.floor(Math.log2(data_max)),
+ div = Math.pow(2, size - (size % 10)),
+ mult = data_max / div,
+ mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000));
+
+ data_max = data_max + (mult * div) - (data_max % (mult * div));
+
+ /* remember current timestamp, calculate horizontal scale */
+ data_stamp = data[data.length-1][TIME];
+ data_scale = height / data_max;
+
+ /* plot data */
+ var pt_rx = '0,' + height;
+ var pt_tx = '0,' + height;
+
+ var y_rx = 0;
+ var y_tx = 0;
+
+ for (var i = 0; i < data_rx.length; i++)
+ {
+ var x = i * step;
+
+ y_rx = height - Math.floor(data_rx[i] * data_scale);
+ y_tx = height - Math.floor(data_tx[i] * data_scale);
+
+ pt_rx += ' ' + x + ',' + y_rx;
+ pt_tx += ' ' + x + ',' + y_tx;
+ }
+
+ pt_rx += ' ' + width + ',' + y_rx + ' ' + width + ',' + height;
+ pt_tx += ' ' + width + ',' + y_tx + ' ' + width + ',' + height;
+
+
+ line_rx.setAttribute('points', pt_rx);
+ line_tx.setAttribute('points', pt_tx);
+
+ label_25.firstChild.data = bandwidth_label(0.25 * data_max);
+ label_50.firstChild.data = bandwidth_label(0.50 * data_max);
+ label_75.firstChild.data = bandwidth_label(0.75 * data_max);
+
+ label_rx_cur.innerHTML = bandwidth_label(data_rx[data_rx.length-1], true);
+ label_tx_cur.innerHTML = bandwidth_label(data_tx[data_tx.length-1], true);
+
+ label_rx_avg.innerHTML = bandwidth_label(data_rx_avg, true);
+ label_tx_avg.innerHTML = bandwidth_label(data_tx_avg, true);
+
+ label_rx_peak.innerHTML = bandwidth_label(data_rx_peak, true);
+ label_tx_peak.innerHTML = bandwidth_label(data_tx_peak, true);
+ }
+ );
+
+ XHR.run();
+ }
+ }, 1000
+ );
+//]]></script>
+
+<h2 name="content"><%:Realtime Traffic%></h2>
+
+<ul class="cbi-tabmenu">
+ <% for _, dev in ipairs(devices) do %>
+ <li class="cbi-tab<%= dev == curdev and "" or "-disabled" %>"><a href="?dev=<%=pcdata(dev)%>"><%=pcdata(dev)%></a></li>
+ <% end %>
+</ul>
+
+<embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/bandwidth.svg" />
+<div style="text-align:right"><small id="scale">-</small></div>
+<br />
+
+<div class="table" style="width:100%; table-layout:fixed" cellspacing="5">
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:Inbound:%></strong></div>
+ <div class="td" id="rx_bw_cur">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="rx_bw_avg">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="rx_bw_peak">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
+ </div>
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:Outbound:%></strong></div>
+ <div class="td" id="tx_bw_cur">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="tx_bw_avg">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="tx_bw_peak">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
+ </div>
+</div>
+
+<%+footer%>
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/connections.htm b/modules/luci-mod-status/luasrc/view/admin_status/connections.htm
new file mode 100644
index 0000000000..0a0db3be78
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/connections.htm
@@ -0,0 +1,376 @@
+<%#
+ Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+
+<script type="text/javascript">//<![CDATA[
+ var bwxhr = new XHR();
+
+ var G;
+ var TIME = 0;
+ var UDP = 1;
+ var TCP = 2;
+ var OTHER = 3;
+
+ var width = 760;
+ var height = 300;
+ var step = 5;
+
+ var data_wanted = Math.floor(width / step);
+ var data_fill = 0;
+ var data_stamp = 0;
+
+ var data_udp = [ ];
+ var data_tcp = [ ];
+ var data_otr = [ ];
+
+ var line_udp;
+ var line_tcp;
+
+ var label_25;
+ var label_50;
+ var label_75;
+
+ var label_udp_cur;
+ var label_udp_avg;
+ var label_udp_peak;
+
+ var label_tcp_cur;
+ var label_tcp_avg;
+ var label_tcp_peak;
+
+ var label_otr_cur;
+ var label_otr_avg;
+ var label_otr_peak;
+
+ var label_scale;
+
+ var conn_table;
+
+ var dns_cache = { };
+
+
+ /* wait for SVG */
+ window.setTimeout(
+ function() {
+ var svg = document.getElementById('bwsvg');
+
+ try {
+ G = svg.getSVGDocument
+ ? svg.getSVGDocument() : svg.contentDocument;
+ }
+ catch(e) {
+ G = document.embeds['bwsvg'].getSVGDocument();
+ }
+
+ if (!G)
+ {
+ window.setTimeout(arguments.callee, 1000);
+ }
+ else
+ {
+ /* find sizes */
+ width = svg.offsetWidth - 2;
+ height = svg.offsetHeight - 2;
+ data_wanted = Math.ceil(width / step);
+
+ /* prefill datasets */
+ for (var i = 0; i < data_wanted; i++)
+ {
+ data_udp[i] = 0;
+ data_tcp[i] = 0;
+ data_otr[i] = 0;
+ }
+
+ /* find svg elements */
+ line_udp = G.getElementById('udp');
+ line_tcp = G.getElementById('tcp');
+ line_otr = G.getElementById('other');
+
+ label_25 = G.getElementById('label_25');
+ label_50 = G.getElementById('label_50');
+ label_75 = G.getElementById('label_75');
+
+ label_udp_cur = document.getElementById('lb_udp_cur');
+ label_udp_avg = document.getElementById('lb_udp_avg');
+ label_udp_peak = document.getElementById('lb_udp_peak');
+
+ label_tcp_cur = document.getElementById('lb_tcp_cur');
+ label_tcp_avg = document.getElementById('lb_tcp_avg');
+ label_tcp_peak = document.getElementById('lb_tcp_peak');
+
+ label_otr_cur = document.getElementById('lb_otr_cur');
+ label_otr_avg = document.getElementById('lb_otr_avg');
+ label_otr_peak = document.getElementById('lb_otr_peak');
+
+ label_scale = document.getElementById('scale');
+
+ conn_table = document.getElementById('connections');
+
+
+ /* plot horizontal time interval lines */
+ for (var i = width % (step * 60); i < width; i += step * 60)
+ {
+ var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
+ line.setAttribute('x1', i);
+ line.setAttribute('y1', 0);
+ line.setAttribute('x2', i);
+ line.setAttribute('y2', '100%');
+ line.setAttribute('style', 'stroke:black;stroke-width:0.1');
+
+ var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
+ text.setAttribute('x', i + 5);
+ text.setAttribute('y', 15);
+ text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
+ text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
+
+ label_25.parentNode.appendChild(line);
+ label_25.parentNode.appendChild(text);
+ }
+
+ label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
+
+ /* render datasets, start update interval */
+ XHR.poll(3, '<%=build_url("admin/status/realtime/connections_status")%>', null,
+ function(x, json)
+ {
+ var rows = [];
+ var conn = json.connections;
+
+ var lookup_queue = [ ];
+
+ conn.sort(function(a, b) {
+ return b.bytes - a.bytes;
+ });
+
+ for (var i = 0; i < conn.length; i++)
+ {
+ var c = conn[i];
+
+ if ((c.src == '127.0.0.1' && c.dst == '127.0.0.1') ||
+ (c.src == '::1' && c.dst == '::1'))
+ continue;
+
+ if (!dns_cache[c.src])
+ lookup_queue.push(c.src);
+
+ if (!dns_cache[c.dst])
+ lookup_queue.push(c.dst);
+
+ var src = dns_cache[c.src] || (c.layer3 == 'ipv6' ? '[' + c.src + ']' : c.src);
+ var dst = dns_cache[c.dst] || (c.layer3 == 'ipv6' ? '[' + c.dst + ']' : c.dst);
+
+ rows.push([
+ c.layer3.toUpperCase(),
+ c.layer4.toUpperCase(),
+ src + ':' + c.sport,
+ dst + ':' + c.dport,
+ '%1024.2mB (%d <%:Pkts.%>)'.format(c.bytes, c.packets)
+ ]);
+ }
+
+ cbi_update_table(conn_table, rows, '<em><%:No information available%></em>');
+
+ if (lookup_queue.length > 0)
+ XHR.get('<%=build_url("admin/status/nameinfo")%>/' + lookup_queue.slice(0, 100).join('/'), null,
+ function(x, json)
+ {
+ for (var addr in json)
+ dns_cache[addr] = json[addr];
+ }
+ );
+
+
+ var data = json.statistics;
+
+ var data_max = 0;
+ var data_scale = 0;
+
+ var data_udp_avg = 0;
+ var data_tcp_avg = 0;
+ var data_otr_avg = 0;
+
+ var data_udp_peak = 0;
+ var data_tcp_peak = 0;
+ var data_otr_peak = 0;
+
+ for (var i = data_stamp ? 0 : 1; i < data.length; i++)
+ {
+ /* skip overlapping entries */
+ if (data[i][TIME] <= data_stamp)
+ continue;
+
+ data_udp.push(data[i][UDP]);
+ data_tcp.push(data[i][TCP]);
+ data_otr.push(data[i][OTHER]);
+ }
+
+ /* cut off outdated entries */
+ data_udp = data_udp.slice(data_udp.length - data_wanted, data_udp.length);
+ data_tcp = data_tcp.slice(data_tcp.length - data_wanted, data_tcp.length);
+ data_otr = data_otr.slice(data_otr.length - data_wanted, data_otr.length);
+
+ /* find peak */
+ for (var i = 0; i < data_udp.length; i++)
+ {
+ data_max = Math.max(data_max, data_udp[i]);
+ data_max = Math.max(data_max, data_tcp[i]);
+ data_max = Math.max(data_max, data_otr[i]);
+
+ data_udp_peak = Math.max(data_udp_peak, data_udp[i]);
+ data_tcp_peak = Math.max(data_tcp_peak, data_tcp[i]);
+ data_otr_peak = Math.max(data_otr_peak, data_otr[i]);
+
+ if (i > 0)
+ {
+ data_udp_avg = (data_udp_avg + data_udp[i]) / 2;
+ data_tcp_avg = (data_tcp_avg + data_tcp[i]) / 2;
+ data_otr_avg = (data_otr_avg + data_otr[i]) / 2;
+ }
+ else
+ {
+ data_udp_avg = data_udp[i];
+ data_tcp_avg = data_tcp[i];
+ data_otr_avg = data_otr[i];
+ }
+ }
+
+ /* remember current timestamp, calculate horizontal scale */
+ data_stamp = data[data.length-1][TIME];
+ data_scale = height / (data_max * 1.1);
+
+
+ /* plot data */
+ var pt_udp = '0,' + height;
+ var pt_tcp = '0,' + height;
+ var pt_otr = '0,' + height;
+
+ var y_udp = 0;
+ var y_tcp = 0;
+ var y_otr = 0;
+
+ for (var i = 0; i < data_udp.length; i++)
+ {
+ var x = i * step;
+
+ y_udp = height - Math.floor(data_udp[i] * data_scale);
+ y_tcp = height - Math.floor(data_tcp[i] * data_scale);
+ y_otr = height - Math.floor(data_otr[i] * data_scale);
+
+ pt_udp += ' ' + x + ',' + y_udp;
+ pt_tcp += ' ' + x + ',' + y_tcp;
+ pt_otr += ' ' + x + ',' + y_otr;
+ }
+
+ pt_udp += ' ' + width + ',' + y_udp + ' ' + width + ',' + height;
+ pt_tcp += ' ' + width + ',' + y_tcp + ' ' + width + ',' + height;
+ pt_otr += ' ' + width + ',' + y_otr + ' ' + width + ',' + height;
+
+
+ var order = [
+ [ line_udp, data_udp[data_udp.length-1] ],
+ [ line_tcp, data_tcp[data_tcp.length-1] ],
+ [ line_otr, data_otr[data_otr.length-1] ]
+ ];
+
+ order.sort(function(a, b) { return b[1] - a[1] });
+
+ for (var i = 0; i < order.length; i++)
+ order[i][0].parentNode.appendChild(order[i][0]);
+
+
+ line_udp.setAttribute('points', pt_udp);
+ line_tcp.setAttribute('points', pt_tcp);
+ line_otr.setAttribute('points', pt_otr);
+
+ label_25.firstChild.data = Math.floor(1.1 * 0.25 * data_max);
+ label_50.firstChild.data = Math.floor(1.1 * 0.50 * data_max);
+ label_75.firstChild.data = Math.floor(1.1 * 0.75 * data_max);
+
+ label_udp_cur.innerHTML = Math.floor(data_udp[data_udp.length-1]);
+ label_tcp_cur.innerHTML = Math.floor(data_tcp[data_tcp.length-1]);
+ label_otr_cur.innerHTML = Math.floor(data_otr[data_otr.length-1]);
+
+ label_udp_avg.innerHTML = Math.floor(data_udp_avg);
+ label_tcp_avg.innerHTML = Math.floor(data_tcp_avg);
+ label_otr_avg.innerHTML = Math.floor(data_otr_avg);
+
+ label_udp_peak.innerHTML = Math.floor(data_udp_peak);
+ label_tcp_peak.innerHTML = Math.floor(data_tcp_peak);
+ label_otr_peak.innerHTML = Math.floor(data_otr_peak);
+ }
+ );
+
+ XHR.run();
+ }
+ }, 1000
+ );
+//]]></script>
+
+<h2 name="content"><%:Realtime Connections%></h2>
+
+<div class="cbi-map-descr"><%:This page gives an overview over currently active network connections.%></div>
+
+<fieldset class="cbi-section" id="cbi-table-table">
+ <legend><%:Active Connections%></legend>
+
+ <embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/connections.svg" />
+ <div style="text-align:right"><small id="scale">-</small></div>
+ <br />
+
+ <div class="table">
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:UDP:%></strong></div>
+ <div class="td" id="lb_udp_cur">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="lb_udp_avg">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="lb_udp_peak">0</div>
+ </div>
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:TCP:%></strong></div>
+ <div class="td" id="lb_tcp_cur">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="lb_tcp_avg">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="lb_tcp_peak">0</div>
+ </div>
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid red"><%:Other:%></strong></div>
+ <div class="td" id="lb_otr_cur">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="lb_otr_avg">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="lb_otr_peak">0</div>
+ </div>
+ </div>
+ <br />
+
+ <div class="cbi-section-node">
+ <div class="table" id="connections">
+ <div class="tr table-titles">
+ <div class="th col-2 hide-xs"><%:Network%></div>
+ <div class="th col-2"><%:Protocol%></div>
+ <div class="th col-7"><%:Source%></div>
+ <div class="th col-7"><%:Destination%></div>
+ <div class="th col-4"><%:Transfer%></div>
+ </div>
+
+ <div class="tr placeholder">
+ <div class="td">
+ <em><%:Collecting data...%></em>
+ </div>
+ </div>
+ </div>
+ </div>
+</fieldset>
+
+<%+footer%>
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/dmesg.htm b/modules/luci-mod-status/luasrc/view/admin_status/dmesg.htm
new file mode 100644
index 0000000000..1a8770ef88
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/dmesg.htm
@@ -0,0 +1,12 @@
+<%#
+ Copyright 2008 Steven Barth <steven@midlink.org>
+ Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+<h2 name="content"><%:Kernel Log%></h2>
+<div id="content_syslog">
+<textarea style="font-size: 12px;" readonly="readonly" wrap="off" rows="<%=dmesg:cmatch("\n")+2%>" id="syslog"><%=dmesg:pcdata()%></textarea>
+</div>
+<%+footer%>
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/index.htm b/modules/luci-mod-status/luasrc/view/admin_status/index.htm
new file mode 100644
index 0000000000..3edfd92047
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/index.htm
@@ -0,0 +1,487 @@
+<%#
+ Copyright 2008 Steven Barth <steven@midlink.org>
+ Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%
+ local fs = require "nixio.fs"
+ local ipc = require "luci.ip"
+ local util = require "luci.util"
+ local stat = require "luci.tools.status"
+ local ver = require "luci.version"
+
+ local has_ipv6 = fs.access("/proc/net/ipv6_route")
+ local has_dhcp = fs.access("/etc/config/dhcp")
+ local has_wifi = ((fs.stat("/etc/config/wireless", "size") or 0) > 0)
+
+ local sysinfo = luci.util.ubus("system", "info") or { }
+ local boardinfo = luci.util.ubus("system", "board") or { }
+ local unameinfo = nixio.uname() or { }
+
+ local meminfo = sysinfo.memory or {
+ total = 0,
+ free = 0,
+ buffered = 0,
+ shared = 0
+ }
+
+ local swapinfo = sysinfo.swap or {
+ total = 0,
+ free = 0
+ }
+
+ local has_dsl = fs.access("/etc/init.d/dsl_control")
+
+ if luci.http.formvalue("status") == "1" then
+ local ntm = require "luci.model.network".init()
+ local wan_nets = ntm:get_wan_networks()
+ local wan6_nets = ntm:get_wan6_networks()
+
+ local conn_count = tonumber(
+ fs.readfile("/proc/sys/net/netfilter/nf_conntrack_count") or "") or 0
+
+ local conn_max = tonumber(luci.sys.exec(
+ "sysctl -n -e net.nf_conntrack_max net.ipv4.netfilter.ip_conntrack_max"
+ ):match("%d+")) or 4096
+
+ local rv = {
+ uptime = sysinfo.uptime or 0,
+ localtime = os.date(),
+ loadavg = sysinfo.load or { 0, 0, 0 },
+ memory = meminfo,
+ swap = swapinfo,
+ connmax = conn_max,
+ conncount = conn_count,
+ wifinets = stat.wifi_networks()
+ }
+
+ if #wan_nets > 0 then
+ local k, v
+
+ rv.wan = { }
+
+ for k, v in pairs(wan_nets) do
+ local dev = v:get_interface()
+ local link = dev and ipc.link(dev:name())
+
+ local wan_info = {
+ ipaddr = v:ipaddr(),
+ gwaddr = v:gwaddr(),
+ netmask = v:netmask(),
+ dns = v:dnsaddrs(),
+ expires = v:expires(),
+ uptime = v:uptime(),
+ proto = v:proto(),
+ i18n = v:get_i18n(),
+ ifname = v:ifname(),
+ link = v:adminlink(),
+ mac = dev and dev:mac(),
+ type = dev and dev:type(),
+ name = dev and dev:get_i18n(),
+ ether = link and link.type == 1
+ }
+
+ rv.wan[#rv.wan+1] = wan_info
+ end
+ end
+
+ if #wan6_nets > 0 then
+ local k, v
+
+ rv.wan6 = { }
+
+ for k, v in pairs(wan6_nets) do
+ local dev = v:get_interface()
+ local link = dev and ipc.link(dev:name())
+ local wan6_info = {
+ ip6addr = v:ip6addr(),
+ gw6addr = v:gw6addr(),
+ dns = v:dns6addrs(),
+ ip6prefix = v:ip6prefix(),
+ uptime = v:uptime(),
+ proto = v:proto(),
+ i18n = v:get_i18n(),
+ ifname = v:ifname(),
+ link = v:adminlink(),
+ mac = dev and dev:mac(),
+ type = dev and dev:type(),
+ name = dev and dev:get_i18n(),
+ ether = link and link.type == 1
+ }
+
+ rv.wan6[#rv.wan6+1] = wan6_info
+ end
+ end
+
+ if has_dsl then
+ local dsl_stat = luci.sys.exec("/etc/init.d/dsl_control lucistat")
+ local dsl_func = loadstring(dsl_stat)
+ if dsl_func then
+ rv.dsl = dsl_func()
+ end
+ end
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(rv)
+
+ return
+ end
+-%>
+
+<%+header%>
+
+<script type="text/javascript">//<![CDATA[
+ function progressbar(v, m)
+ {
+ var vn = parseInt(v) || 0;
+ var mn = parseInt(m) || 100;
+ var pc = Math.floor((100 / mn) * vn);
+
+ return String.format(
+ '<div style="width:100%%; max-width:200px; position:relative; border:1px solid #999999">' +
+ '<div style="background-color:#CCCCCC; width:%d%%; height:15px">' +
+ '<div style="position:absolute; left:0; top:0; text-align:center; width:100%%; color:#000000">' +
+ '<small>%s / %s (%d%%)</small>' +
+ '</div>' +
+ '</div>' +
+ '</div>', pc, v, m, pc
+ );
+ }
+
+ function labelList(items, offset) {
+ var rv = [ ];
+
+ for (var i = offset || 0; i < items.length; i += 2) {
+ var label = items[i],
+ value = items[i+1];
+
+ if (value === undefined || value === null)
+ continue;
+
+ if (label)
+ rv.push(E('strong', [label, ': ']));
+
+ rv.push(value, E('br'));
+ }
+
+ return rv;
+ }
+
+ function renderBox(title, active, childs) {
+ childs = childs || [];
+ childs.unshift(E('span', labelList(arguments, 3)));
+
+ return E('div', { class: 'ifacebox' }, [
+ E('div', { class: 'ifacebox-head center ' + (active ? 'active' : '') },
+ E('strong', title)),
+ E('div', { class: 'ifacebox-body left' }, childs)
+ ]);
+ }
+
+ function renderBadge(icon, title) {
+ return E('span', { class: 'ifacebadge' }, [
+ E('img', { src: icon, title: title || '' }),
+ E('span', labelList(arguments, 2))
+ ]);
+ }
+
+ XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 },
+ function(x, info)
+ {
+ var us = document.getElementById('upstream_status_table');
+
+ while (us.lastElementChild)
+ us.removeChild(us.lastElementChild);
+
+ var wan_list = info.wan || [];
+
+ for (var i = 0; i < wan_list.length; i++) {
+ var ifc = wan_list[i];
+
+ us.appendChild(renderBox(
+ '<%:IPv4 Upstream%>',
+ (ifc.ifname && ifc.proto != 'none'),
+ [ E('div', {}, renderBadge(
+ '<%=resource%>' + '/icons/%s.png'.format((ifc && ifc.type) ? ifc.type : 'ethernet_disabled'), null,
+ '<%:Device%>', ifc ? (ifc.name || ifc.ifname || '-') : '-',
+ '<%:MAC-Address%>', (ifc && ifc.ether) ? ifc.mac : null)) ],
+ '<%:Protocol%>', ifc.i18n || E('em', '<%:Not connected%>'),
+ '<%:Address%>', (ifc.ipaddr) ? ifc.ipaddr : '0.0.0.0',
+ '<%:Netmask%>', (ifc.netmask && ifc.netmask != ifc.ipaddr) ? ifc.netmask : '255.255.255.255',
+ '<%:Gateway%>', (ifc.gwaddr) ? ifc.gwaddr : '0.0.0.0',
+ '<%:DNS%> 1', (ifc.dns) ? ifc.dns[0] : null,
+ '<%:DNS%> 2', (ifc.dns) ? ifc.dns[1] : null,
+ '<%:DNS%> 3', (ifc.dns) ? ifc.dns[2] : null,
+ '<%:DNS%> 4', (ifc.dns) ? ifc.dns[3] : null,
+ '<%:DNS%> 5', (ifc.dns) ? ifc.dns[4] : null,
+ '<%:Expires%>', (ifc.expires > -1) ? '%t'.format(ifc.expires) : null,
+ '<%:Connected%>', (ifc.uptime > 0) ? '%t'.format(ifc.uptime) : null));
+ }
+
+ <% if has_ipv6 then %>
+ var wan6_list = info.wan6 || [];
+
+ for (var i = 0; i < wan6_list.length; i++) {
+ var ifc6 = wan6_list[i];
+
+ us.appendChild(renderBox(
+ '<%:IPv6 Upstream%>',
+ (ifc6.ifname && ifc6.proto != 'none'),
+ [ E('div', {}, renderBadge(
+ '<%=resource%>/icons/%s.png'.format(ifc6.type || 'ethernet_disabled'), null,
+ '<%:Device%>', ifc6 ? (ifc6.name || ifc6.ifname || '-') : '-',
+ '<%:MAC-Address%>', (ifc6 && ifc6.ether) ? ifc6.mac : null)) ],
+ '<%:Protocol%>', ifc6.i18n ? (ifc6.i18n + (ifc6.proto === 'dhcp' && ifc6.ip6prefix ? '-PD' : '')) : E('em', '<%:Not connected%>'),
+ '<%:Prefix Delegated%>', ifc6.ip6prefix,
+ '<%:Address%>', (ifc6.ip6prefix) ? (ifc6.ip6addr || null) : (ifc6.ipaddr || '::'),
+ '<%:Gateway%>', (ifc6.gw6addr) ? ifc6.gw6addr : '::',
+ '<%:DNS%> 1', (ifc6.dns) ? ifc6.dns[0] : null,
+ '<%:DNS%> 2', (ifc6.dns) ? ifc6.dns[1] : null,
+ '<%:DNS%> 3', (ifc6.dns) ? ifc6.dns[2] : null,
+ '<%:DNS%> 4', (ifc6.dns) ? ifc6.dns[3] : null,
+ '<%:DNS%> 5', (ifc6.dns) ? ifc6.dns[4] : null,
+ '<%:Connected%>', (ifc6.uptime > 0) ? '%t'.format(ifc6.uptime) : null));
+ }
+ <% end %>
+
+ <% if has_dsl then %>
+ var ds = document.getElementById('dsl_status_table');
+
+ while (ds.lastElementChild)
+ ds.removeChild(ds.lastElementChild);
+
+ ds.appendChild(renderBox(
+ '<%:DSL Status%>',
+ (info.dsl.line_state === 'UP'), [ ],
+ '<%:Line State%>', '%s [0x%x]'.format(info.dsl.line_state, info.dsl.line_state_detail),
+ '<%:Line Mode%>', info.dsl.line_mode_s || '-',
+ '<%:Line Uptime%>', info.dsl.line_uptime_s || '-',
+ '<%:Annex%>', info.dsl.annex_s || '-',
+ '<%:Profile%>', info.dsl.profile_s || '-',
+ '<%:Data Rate%>', '%s/s / %s/s'.format(info.dsl.data_rate_down_s, info.dsl.data_rate_up_s),
+ '<%:Max. Attainable Data Rate (ATTNDR)%>', '%s/s / %s/s'.format(info.dsl.max_data_rate_down_s, info.dsl.max_data_rate_up_s),
+ '<%:Latency%>', '%s / %s'.format(info.dsl.latency_num_down, info.dsl.latency_num_up),
+ '<%:Line Attenuation (LATN)%>', '%.1f dB / %.1f dB'.format(info.dsl.line_attenuation_down, info.dsl.line_attenuation_up),
+ '<%:Signal Attenuation (SATN)%>', '%.1f dB / %.1f dB'.format(info.dsl.signal_attenuation_down, info.dsl.signal_attenuation_up),
+ '<%:Noise Margin (SNR)%>', '%.1f dB / %.1f dB'.format(info.dsl.noise_margin_down, info.dsl.noise_margin_up),
+ '<%:Aggregate Transmit Power(ACTATP)%>', '%.1f dB / %.1f dB'.format(info.dsl.actatp_down, info.dsl.actatp_up),
+ '<%:Forward Error Correction Seconds (FECS)%>', '%d / %d'.format(info.dsl.errors_fec_near, info.dsl.errors_fec_far),
+ '<%:Errored seconds (ES)%>', '%d / %d'.format(info.dsl.errors_es_near, info.dsl.errors_es_far),
+ '<%:Severely Errored Seconds (SES)%>', '%d / %d'.format(info.dsl.errors_ses_near, info.dsl.errors_ses_far),
+ '<%:Loss of Signal Seconds (LOSS)%>', '%d / %d'.format(info.dsl.errors_loss_near, info.dsl.errors_loss_far),
+ '<%:Unavailable Seconds (UAS)%>', '%d / %d'.format(info.dsl.errors_uas_near, info.dsl.errors_uas_far),
+ '<%:Header Error Code Errors (HEC)%>', '%d / %d'.format(info.dsl.errors_hec_near, info.dsl.errors_hec_far),
+ '<%:Non Pre-emtive CRC errors (CRC_P)%>', '%d / %d'.format(info.dsl.errors_crc_p_near, info.dsl.errors_crc_p_far),
+ '<%:Pre-emtive CRC errors (CRCP_P)%>', '%d / %d'.format(info.dsl.errors_crcp_p_near, info.dsl.errors_crcp_p_far),
+ '<%:ATU-C System Vendor ID%>', info.dsl.atuc_vendor_id,
+ '<%:Power Management Mode%>', info.dsl.power_mode_s));
+ <% end %>
+
+ <% if has_wifi then %>
+ var ws = document.getElementById('wifi_status_table');
+ if (ws)
+ {
+ while (ws.lastElementChild)
+ ws.removeChild(ws.lastElementChild);
+
+ for (var didx = 0; didx < info.wifinets.length; didx++)
+ {
+ var dev = info.wifinets[didx];
+ var net0 = (dev.networks && dev.networks[0]) ? dev.networks[0] : {};
+ var vifs = [];
+
+ for (var nidx = 0; nidx < dev.networks.length; nidx++)
+ {
+ var net = dev.networks[nidx];
+ var is_assoc = (net.bssid != '00:00:00:00:00:00' && net.channel && !net.disabled);
+
+ var icon;
+ if (net.disabled)
+ icon = "<%=resource%>/icons/signal-none.png";
+ else if (net.quality <= 0)
+ icon = "<%=resource%>/icons/signal-0.png";
+ else if (net.quality < 25)
+ icon = "<%=resource%>/icons/signal-0-25.png";
+ else if (net.quality < 50)
+ icon = "<%=resource%>/icons/signal-25-50.png";
+ else if (net.quality < 75)
+ icon = "<%=resource%>/icons/signal-50-75.png";
+ else
+ icon = "<%=resource%>/icons/signal-75-100.png";
+
+ vifs.push(renderBadge(
+ icon,
+ '<%:Signal%>: %d dBm / <%:Quality%>: %d%%'.format(net.signal, net.quality),
+ '<%:SSID%>', E('a', { href: net.link }, [ net.ssid || '?' ]),
+ '<%:Mode%>', net.mode,
+ '<%:BSSID%>', is_assoc ? (net.bssid || '-') : null,
+ '<%:Encryption%>', is_assoc ? net.encryption : null,
+ '<%:Associations%>', is_assoc ? (net.num_assoc || '-') : null,
+ null, is_assoc ? null : E('em', net.disabled ? '<%:Wireless is disabled%>' : '<%:Wireless is not associated%>')));
+ }
+
+ ws.appendChild(renderBox(
+ dev.device, dev.up || net0.up,
+ [ E('div', vifs) ],
+ '<%:Type%>', dev.name.replace(/^Generic | Wireless Controller .+$/g, ''),
+ '<%:Channel%>', net0.channel ? '%d (%.3f <%:GHz%>)'.format(net0.channel, net0.frequency) : '-',
+ '<%:Bitrate%>', net0.bitrate ? '%d <%:Mbit/s%>'.format(net0.bitrate) : '-'));
+ }
+
+ if (!ws.lastElementChild)
+ ws.appendChild(E('<em><%:No information available%></em>'));
+ }
+ <% end %>
+
+ var e;
+
+ if (e = document.getElementById('localtime'))
+ e.innerHTML = info.localtime;
+
+ if (e = document.getElementById('uptime'))
+ e.innerHTML = String.format('%t', info.uptime);
+
+ if (e = document.getElementById('loadavg'))
+ e.innerHTML = String.format(
+ '%.02f, %.02f, %.02f',
+ info.loadavg[0] / 65535.0,
+ info.loadavg[1] / 65535.0,
+ info.loadavg[2] / 65535.0
+ );
+
+ if (e = document.getElementById('memtotal'))
+ e.innerHTML = progressbar(
+ ((info.memory.free + info.memory.buffered) / 1024) + " <%:kB%>",
+ (info.memory.total / 1024) + " <%:kB%>"
+ );
+
+ if (e = document.getElementById('memfree'))
+ e.innerHTML = progressbar(
+ (info.memory.free / 1024) + " <%:kB%>",
+ (info.memory.total / 1024) + " <%:kB%>"
+ );
+
+ if (e = document.getElementById('membuff'))
+ e.innerHTML = progressbar(
+ (info.memory.buffered / 1024) + " <%:kB%>",
+ (info.memory.total / 1024) + " <%:kB%>"
+ );
+
+ if (e = document.getElementById('swaptotal'))
+ e.innerHTML = progressbar(
+ (info.swap.free / 1024) + " <%:kB%>",
+ (info.swap.total / 1024) + " <%:kB%>"
+ );
+
+ if (e = document.getElementById('swapfree'))
+ e.innerHTML = progressbar(
+ (info.swap.free / 1024) + " <%:kB%>",
+ (info.swap.total / 1024) + " <%:kB%>"
+ );
+
+ if (e = document.getElementById('conns'))
+ e.innerHTML = progressbar(info.conncount, info.connmax);
+
+ }
+ );
+//]]></script>
+
+<h2 name="content"><%:Status%></h2>
+
+<div class="cbi-section">
+ <h3><%:System%></h3>
+
+ <div class="table" width="100%">
+ <div class="tr"><div class="td left" width="33%"><%:Hostname%></div><div class="td left"><%=luci.sys.hostname() or "?"%></div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Model%></div><div class="td left"><%=pcdata(boardinfo.model or "?")%></div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Architecture%></div><div class="td left"><%=pcdata(boardinfo.system or "?")%></div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Firmware Version%></div><div class="td left">
+ <%=pcdata(ver.distname)%> <%=pcdata(ver.distversion)%> /
+ <%=pcdata(ver.luciname)%> (<%=pcdata(ver.luciversion)%>)
+ </div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Kernel Version%></div><div class="td left"><%=unameinfo.release or "?"%></div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Local Time%></div><div class="td left" id="localtime">-</div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Uptime%></div><div class="td left" id="uptime">-</div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Load Average%></div><div class="td left" id="loadavg">-</div></div>
+ </div>
+</div>
+
+<div class="cbi-section">
+ <h3><%:Memory%></h3>
+
+ <div class="table" width="100%">
+ <div class="tr"><div class="td left" width="33%"><%:Total Available%></div><div class="td left" id="memtotal">-</div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Free%></div><div class="td left" id="memfree">-</div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Buffered%></div><div class="td left" id="membuff">-</div></div>
+ </div>
+</div>
+
+<% if swapinfo.total > 0 then %>
+<div class="cbi-section">
+ <h3><%:Swap%></h3>
+
+ <div class="table" width="100%">
+ <div class="tr"><div class="td left" width="33%"><%:Total Available%></div><div class="td left" id="swaptotal">-</div></div>
+ <div class="tr"><div class="td left" width="33%"><%:Free%></div><div class="td left" id="swapfree">-</div></div>
+ </div>
+</div>
+<% end %>
+
+<div class="cbi-section">
+ <h3><%:Network%></h3>
+
+ <div id="upstream_status_table" class="network-status-table">
+ <p><em><%:Collecting data...%></em></p>
+ </div>
+
+ <div class="table" width="100%">
+ <div class="tr"><div class="td left" width="33%"><%:Active Connections%></div><div class="td left" id="conns">-</div></div>
+ </div>
+</div>
+
+<%
+ if has_dhcp then
+ include("lease_status")
+ end
+%>
+
+<% if has_dsl then %>
+<div class="cbi-section">
+ <h3><%:DSL%></h3>
+
+ <div id="dsl_status_table" class="network-status-table">
+ <p><em><%:Collecting data...%></em></p>
+ </div>
+</div>
+<% end %>
+
+<% if has_wifi then %>
+<div class="cbi-section">
+ <h3><%:Wireless%></h3>
+
+ <div id="wifi_status_table" class="network-status-table">
+ <p><em><%:Collecting data...%></em></p>
+ </div>
+</div>
+
+<div class="cbi-section">
+ <h3><%:Associated Stations%></h3>
+
+ <%+wifi_assoclist%>
+</div>
+<% end %>
+
+<%-
+ local incdir = util.libpath() .. "/view/admin_status/index/"
+ if fs.access(incdir) then
+ local inc
+ for inc in fs.dir(incdir) do
+ if inc:match("%.htm$") then
+ include("admin_status/index/" .. inc:gsub("%.htm$", ""))
+ end
+ end
+ end
+-%>
+
+<%+footer%>
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/iptables.htm b/modules/luci-mod-status/luasrc/view/admin_status/iptables.htm
new file mode 100644
index 0000000000..51e428e40e
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/iptables.htm
@@ -0,0 +1,155 @@
+<%#
+ Copyright 2008-2009 Steven Barth <steven@midlink.org>
+ Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%-
+
+ require "luci.sys.iptparser"
+ local wba = require "luci.tools.webadmin"
+ local fs = require "nixio.fs"
+ local io = require "io"
+
+ local has_ip6tables = fs.access("/usr/sbin/ip6tables")
+ local mode = 4
+
+ if has_ip6tables then
+ mode = luci.dispatcher.context.requestpath
+ mode = tonumber(mode[#mode] ~= "iptables" and mode[#mode]) or 4
+ end
+
+ local ipt = luci.sys.iptparser.IptParser(mode)
+
+ local rowcnt = 1
+ function rowstyle()
+ rowcnt = rowcnt + 1
+ return (rowcnt % 2) + 1
+ end
+
+ function link_target(t,c)
+ if ipt:is_custom_target(c) then
+ return '<a href="#rule_%s_%s">%s</a>' %{ t:lower(), c, c }
+ end
+ return c
+ end
+
+ function link_iface(i)
+ local net = wba.iface_get_network(i)
+ if net and i ~= "lo" then
+ return '<a href="%s">%s</a>' %{
+ url("admin/network/network", net), i
+ }
+
+ end
+ return i
+ end
+
+ local tables = { "Filter", "NAT", "Mangle", "Raw" }
+ if mode == 6 then
+ tables = { "Filter", "Mangle", "Raw" }
+ local ok, lines = pcall(io.lines, "/proc/net/ip6_tables_names")
+ if ok and lines then
+ local line
+ for line in lines do
+ if line == "nat" then
+ tables = { "Filter", "NAT", "Mangle", "Raw" }
+ end
+ end
+ end
+ end
+-%>
+
+<%+header%>
+
+<style type="text/css">
+ span:target {
+ color: blue;
+ text-decoration: underline;
+ }
+</style>
+
+<h2 name="content"><%:Firewall Status%></h2>
+
+<% if has_ip6tables then %>
+<ul class="cbi-tabmenu">
+ <li class="cbi-tab<%= mode ~= 4 and "-disabled" %>"><a href="<%=url("admin/status/iptables/4")%>"><%:IPv4 Firewall%></a></li>
+ <li class="cbi-tab<%= mode ~= 6 and "-disabled" %>"><a href="<%=url("admin/status/iptables/6")%>"><%:IPv6 Firewall%></a></li>
+</ul>
+<% end %>
+
+<div class="cbi-map" style="position: relative">
+
+ <form method="post" action="<%=url("admin/status/iptables_action")%>" style="position: absolute; right: 0">
+ <input type="hidden" name="token" value="<%=token%>" />
+ <input type="hidden" name="family" value="<%=mode%>" />
+ <input type="submit" class="cbi-button" name="zero" value="<%:Reset Counters%>" />
+ <input type="submit" class="cbi-button" name="restart" value="<%:Restart Firewall%>" />
+ </form>
+
+ <div class="cbi-section">
+
+ <% for _, tbl in ipairs(tables) do chaincnt = 0 %>
+ <h3><%:Table%>: <%=tbl%></h3>
+
+ <% for _, chain in ipairs(ipt:chains(tbl)) do
+ rowcnt = 0
+ chaincnt = chaincnt + 1
+ chaininfo = ipt:chain(tbl, chain)
+ %>
+ <h4 id="rule_<%=tbl:lower()%>_<%=chain%>">
+ <%:Chain%> <em><%=chain%></em>
+ (<%- if chaininfo.policy then -%>
+ <%:Policy%>: <em><%=chaininfo.policy%></em>, <%:Packets%>: <%=chaininfo.packets%>, <%:Traffic%>: <%=wba.byte_format(chaininfo.bytes)-%>
+ <%- else -%>
+ <%:References%>: <%=chaininfo.references-%>
+ <%- end -%>)
+ </h4>
+
+ <div class="cbi-section-node">
+ <div class="table" style="font-size:90%">
+ <div class="tr table-titles cbi-rowstyle-<%=rowstyle()%>">
+ <div class="th hide-xs"><%:Pkts.%></div>
+ <div class="th nowrap"><%:Traffic%></div>
+ <div class="th col-5"><%:Target%></div>
+ <div class="th"><%:Prot.%></div>
+ <div class="th"><%:In%></div>
+ <div class="th"><%:Out%></div>
+ <div class="th"><%:Source%></div>
+ <div class="th"><%:Destination%></div>
+ <div class="th col-9 hide-xs"><%:Options%></div>
+ </div>
+
+ <% for _, rule in ipairs(ipt:find({table=tbl, chain=chain})) do %>
+ <div class="tr cbi-rowstyle-<%=rowstyle()%>">
+ <div class="td"><%=rule.packets%></div>
+ <div class="td nowrap"><%=wba.byte_format(rule.bytes)%></div>
+ <div class="td col-5"><%=rule.target and link_target(tbl, rule.target) or "-"%></div>
+ <div class="td"><%=rule.protocol%></div>
+ <div class="td"><%=link_iface(rule.inputif)%></div>
+ <div class="td"><%=link_iface(rule.outputif)%></div>
+ <div class="td"><%=rule.source%></div>
+ <div class="td"><%=rule.destination%></div>
+ <div class="td col-9 hide-xs"><%=#rule.options > 0 and luci.util.pcdata(table.concat(rule.options, " ")) or "-"%></div>
+ </div>
+ <% end %>
+
+ <% if rowcnt == 1 then %>
+ <div class="tr cbi-rowstyle-<%=rowstyle()%>">
+ <div class="td" colspan="9"><em><%:No rules in this chain%></em></div>
+ </div>
+ <% end %>
+ </div>
+ </div>
+ <% end %>
+
+ <% if chaincnt == 0 then %>
+ <em><%:No chains in this table%></em>
+ <% end %>
+
+ <br /><br />
+ <% end %>
+ </div>
+</div>
+
+<%+footer%>
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/load.htm b/modules/luci-mod-status/luasrc/view/admin_status/load.htm
new file mode 100644
index 0000000000..bced06fa22
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/load.htm
@@ -0,0 +1,285 @@
+<%#
+ Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+
+<script type="text/javascript">//<![CDATA[
+ var bwxhr = new XHR();
+
+ var G;
+ var TIME = 0;
+ var L01 = 1;
+ var L05 = 2;
+ var L15 = 3;
+
+ var width = 760;
+ var height = 300;
+ var step = 5;
+
+ var data_wanted = Math.floor(width / step);
+ var data_fill = 0;
+ var data_stamp = 0;
+
+ var data_01 = [ ];
+ var data_05 = [ ];
+ var data_15 = [ ];
+
+ var line_01;
+ var line_05;
+ var line_15;
+
+ var label_25;
+ var label_050;
+ var label_75;
+
+ var label_01_cur;
+ var label_01_avg;
+ var label_01_peak;
+
+ var label_05_cur;
+ var label_05_avg;
+ var label_05_peak;
+
+ var label_15_cur;
+ var label_15_avg;
+ var label_15_peak;
+
+ var label_scale;
+
+
+ /* wait for SVG */
+ window.setTimeout(
+ function() {
+ var svg = document.getElementById('bwsvg');
+
+ try {
+ G = svg.getSVGDocument
+ ? svg.getSVGDocument() : svg.contentDocument;
+ }
+ catch(e) {
+ G = document.embeds['bwsvg'].getSVGDocument();
+ }
+
+ if (!G)
+ {
+ window.setTimeout(arguments.callee, 1000);
+ }
+ else
+ {
+ /* find sizes */
+ width = svg.offsetWidth - 2;
+ height = svg.offsetHeight - 2;
+ data_wanted = Math.ceil(width / step);
+
+ /* prefill datasets */
+ for (var i = 0; i < data_wanted; i++)
+ {
+ data_01[i] = 0;
+ data_05[i] = 0;
+ data_15[i] = 0;
+ }
+
+ /* find svg elements */
+ line_01 = G.getElementById('load01');
+ line_05 = G.getElementById('load05');
+ line_15 = G.getElementById('load15');
+
+ label_25 = G.getElementById('label_25');
+ label_50 = G.getElementById('label_50');
+ label_75 = G.getElementById('label_75');
+
+ label_01_cur = document.getElementById('lb_load01_cur');
+ label_01_avg = document.getElementById('lb_load01_avg');
+ label_01_peak = document.getElementById('lb_load01_peak');
+
+ label_05_cur = document.getElementById('lb_load05_cur');
+ label_05_avg = document.getElementById('lb_load05_avg');
+ label_05_peak = document.getElementById('lb_load05_peak');
+
+ label_15_cur = document.getElementById('lb_load15_cur');
+ label_15_avg = document.getElementById('lb_load15_avg');
+ label_15_peak = document.getElementById('lb_load15_peak');
+
+ label_scale = document.getElementById('scale');
+
+
+ /* plot horizontal time interval lines */
+ for (var i = width % (step * 60); i < width; i += step * 60)
+ {
+ var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
+ line.setAttribute('x1', i);
+ line.setAttribute('y1', 0);
+ line.setAttribute('x2', i);
+ line.setAttribute('y2', '100%');
+ line.setAttribute('style', 'stroke:black;stroke-width:0.1');
+
+ var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
+ text.setAttribute('x', i + 5);
+ text.setAttribute('y', 15);
+ text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
+ text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
+
+ label_25.parentNode.appendChild(line);
+ label_25.parentNode.appendChild(text);
+ }
+
+ label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
+
+ /* render datasets, start update interval */
+ XHR.poll(3, '<%=build_url("admin/status/realtime/load_status")%>', null,
+ function(x, data)
+ {
+ var data_max = 0;
+ var data_scale = 0;
+
+ var data_01_avg = 0;
+ var data_05_avg = 0;
+ var data_15_avg = 0;
+
+ var data_01_peak = 0;
+ var data_05_peak = 0;
+ var data_15_peak = 0;
+
+ for (var i = data_stamp ? 0 : 1; i < data.length; i++)
+ {
+ /* skip overlapping entries */
+ if (data[i][TIME] <= data_stamp)
+ continue;
+
+ data_01.push(data[i][L01]);
+ data_05.push(data[i][L05]);
+ data_15.push(data[i][L15]);
+ }
+
+ /* cut off outdated entries */
+ data_01 = data_01.slice(data_01.length - data_wanted, data_01.length);
+ data_05 = data_05.slice(data_05.length - data_wanted, data_05.length);
+ data_15 = data_15.slice(data_15.length - data_wanted, data_15.length);
+
+ /* find peak */
+ for (var i = 0; i < data_01.length; i++)
+ {
+ data_max = Math.max(data_max, data_01[i]);
+ data_max = Math.max(data_max, data_05[i]);
+ data_max = Math.max(data_max, data_15[i]);
+
+ data_01_peak = Math.max(data_01_peak, data_01[i]);
+ data_05_peak = Math.max(data_05_peak, data_05[i]);
+ data_15_peak = Math.max(data_15_peak, data_15[i]);
+
+ if (i > 0)
+ {
+ data_01_avg = (data_01_avg + data_01[i]) / 2;
+ data_05_avg = (data_05_avg + data_05[i]) / 2;
+ data_15_avg = (data_15_avg + data_15[i]) / 2;
+ }
+ else
+ {
+ data_01_avg = data_01[i];
+ data_05_avg = data_05[i];
+ data_15_avg = data_15[i];
+ }
+ }
+
+ /* remember current timestamp, calculate horizontal scale */
+ data_stamp = data[data.length-1][TIME];
+ data_scale = height / (data_max * 1.1);
+
+
+ /* plot data */
+ var pt_01 = '0,' + height;
+ var pt_05 = '0,' + height;
+ var pt_15 = '0,' + height;
+
+ var y_01 = 0;
+ var y_05 = 0;
+ var y_15 = 0;
+
+ for (var i = 0; i < data_01.length; i++)
+ {
+ var x = i * step;
+
+ y_01 = height - Math.floor(data_01[i] * data_scale);
+ y_05 = height - Math.floor(data_05[i] * data_scale);
+ y_15 = height - Math.floor(data_15[i] * data_scale);
+
+ pt_01 += ' ' + x + ',' + y_01;
+ pt_05 += ' ' + x + ',' + y_05;
+ pt_15 += ' ' + x + ',' + y_15;
+ }
+
+ pt_01 += ' ' + width + ',' + y_01 + ' ' + width + ',' + height;
+ pt_05 += ' ' + width + ',' + y_05 + ' ' + width + ',' + height;
+ pt_15 += ' ' + width + ',' + y_15 + ' ' + width + ',' + height;
+
+
+ line_01.setAttribute('points', pt_01);
+ line_05.setAttribute('points', pt_05);
+ line_15.setAttribute('points', pt_15);
+
+ label_25.firstChild.data = (1.1 * 0.25 * data_max / 100).toFixed(2);
+ label_50.firstChild.data = (1.1 * 0.50 * data_max / 100).toFixed(2);
+ label_75.firstChild.data = (1.1 * 0.75 * data_max / 100).toFixed(2);
+
+ label_01_cur.innerHTML = (data_01[data_01.length-1] / 100).toFixed(2);
+ label_05_cur.innerHTML = (data_05[data_05.length-1] / 100).toFixed(2);
+ label_15_cur.innerHTML = (data_15[data_15.length-1] / 100).toFixed(2);
+
+ label_01_avg.innerHTML = (data_01_avg / 100).toFixed(2);
+ label_05_avg.innerHTML = (data_05_avg / 100).toFixed(2);
+ label_15_avg.innerHTML = (data_15_avg / 100).toFixed(2);
+
+ label_01_peak.innerHTML = (data_01_peak / 100).toFixed(2);
+ label_05_peak.innerHTML = (data_05_peak / 100).toFixed(2);
+ label_15_peak.innerHTML = (data_15_peak / 100).toFixed(2);
+ }
+ );
+
+ XHR.run();
+ }
+ }, 1000
+ );
+//]]></script>
+
+<h2 name="content"><%:Realtime Load%></h2>
+
+<embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/load.svg" />
+<div style="text-align:right"><small id="scale">-</small></div>
+<br />
+
+<div class="table" style="width:100%; table-layout:fixed" cellspacing="5">
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ff0000; white-space:nowrap"><%:1 Minute Load:%></strong></div>
+ <div class="td" id="lb_load01_cur">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="lb_load01_avg">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="lb_load01_peak">0</div>
+ </div>
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ff6600; white-space:nowrap"><%:5 Minute Load:%></strong></div>
+ <div class="td" id="lb_load05_cur">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="lb_load05_avg">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="lb_load05_peak">0</div>
+ </div>
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ffaa00; white-space:nowrap"><%:15 Minute Load:%></strong></div>
+ <div class="td" id="lb_load15_cur">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="lb_load15_avg">0</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="lb_load15_peak">0</div>
+ </div>
+</div>
+
+<%+footer%>
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/routes.htm b/modules/luci-mod-status/luasrc/view/admin_status/routes.htm
new file mode 100644
index 0000000000..74779f6ad8
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/routes.htm
@@ -0,0 +1,156 @@
+<%#
+ Copyright 2008-2009 Steven Barth <steven@midlink.org>
+ Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%-
+ require "luci.tools.webadmin"
+ require "nixio.fs"
+
+ local ip = require "luci.ip"
+ local style = true
+ local _, v
+
+ local rtn = {
+ [255] = "local",
+ [254] = "main",
+ [253] = "default",
+ [0] = "unspec"
+ }
+
+ if nixio.fs.access("/etc/iproute2/rt_tables") then
+ local ln
+ for ln in io.lines("/etc/iproute2/rt_tables") do
+ local i, n = ln:match("^(%d+)%s+(%S+)")
+ if i and n then
+ rtn[tonumber(i)] = n
+ end
+ end
+ end
+-%>
+
+<%+header%>
+
+
+<div class="cbi-map" id="cbi-network">
+ <h2 name="content"><%:Routes%></h2>
+ <div class="cbi-map-descr"><%:The following rules are currently active on this system.%></div>
+
+ <div class="cbi-section">
+ <legend>ARP</legend>
+ <div class="cbi-section-node">
+ <div class="table">
+ <div class="tr table-titles">
+ <div class="th"><%_<abbr title="Internet Protocol Version 4">IPv4</abbr>-Address%></div>
+ <div class="th"><%_<abbr title="Media Access Control">MAC</abbr>-Address%></div>
+ <div class="th"><%:Interface%></div>
+ </div>
+
+ <%
+ for _, v in ipairs(ip.neighbors({ family = 4 })) do
+ if v.mac then
+ %>
+ <div class="tr cbi-rowstyle-<%=(style and 1 or 2)%>">
+ <div class="td"><%=v.dest%></div>
+ <div class="td"><%=v.mac%></div>
+ <div class="td"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></div>
+ </div>
+ <%
+ style = not style
+ end
+ end
+ %>
+ </div>
+ </div>
+ </div>
+
+ <div class="cbi-section">
+ <legend><%_Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes%></legend>
+ <div class="cbi-section-node">
+ <div class="table">
+ <div class="tr table-titles">
+ <div class="th"><%:Network%></div>
+ <div class="th"><%:Target%></div>
+ <div class="th"><%_<abbr title="Internet Protocol Version 4">IPv4</abbr>-Gateway%></div>
+ <div class="th"><%:Metric%></div>
+ <div class="th"><%:Table%></div>
+ </div>
+ <% for _, v in ipairs(ip.routes({ family = 4, type = 1 })) do %>
+ <div class="tr cbi-rowstyle-<%=(style and 1 or 2)%>">
+ <div class="td"><%=luci.tools.webadmin.iface_get_network(v.dev) or v.dev%></div>
+ <div class="td"><%=v.dest%></div>
+ <div class="td"><%=v.gw or "-"%></div>
+ <div class="td"><%=v.metric or 0%></div>
+ <div class="td"><%=rtn[v.table] or v.table%></div>
+ </div>
+ <% style = not style end %>
+ </div>
+ </div>
+ </div>
+
+ <%
+ if nixio.fs.access("/proc/net/ipv6_route") then
+ style = true
+ %>
+ <div class="cbi-section">
+ <legend><%_Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes%></legend>
+ <div class="cbi-section-node">
+ <div class="table">
+ <div class="tr table-titles">
+ <div class="th"><%:Network%></div>
+ <div class="th"><%:Target%></div>
+ <div class="th"><%:Source%></div>
+ <div class="th"><%:Metric%></div>
+ <div class="th"><%:Table%></div>
+ </div>
+ <%
+ for _, v in ipairs(ip.routes({ family = 6, type = 1 })) do
+ if v.dest and not v.dest:is6linklocal() then
+ %>
+ <div class="tr cbi-rowstyle-<%=(style and 1 or 2)%>">
+ <div class="td"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></div>
+ <div class="td"><%=v.dest%></div>
+ <div class="td"><%=v.from%></div>
+ <div class="td"><%=v.metric or 0%></div>
+ <div class="td"><%=rtn[v.table] or v.table%></div>
+ </div>
+ <%
+ style = not style
+ end
+ end
+ %>
+ </div>
+ </div>
+ </div>
+
+ <div class="cbi-section">
+ <legend><%:IPv6 Neighbours%></legend>
+ <div class="cbi-section-node">
+ <div class="table">
+ <div class="tr table-titles">
+ <div class="th"><%:IPv6-Address%></div>
+ <div class="th"><%:MAC-Address%></div>
+ <div class="th"><%:Interface%></div>
+ </div>
+ <%
+ for _, v in ipairs(ip.neighbors({ family = 6 })) do
+ if v.dest and not v.dest:is6linklocal() and v.mac then
+ %>
+ <div class="tr cbi-rowstyle-<%=(style and 1 or 2)%>">
+ <div class="td"><%=v.dest%></div>
+ <div class="td"><%=v.mac%></div>
+ <div class="td"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></div>
+ </div>
+ <%
+ style = not style
+ end
+ end
+ %>
+ </div>
+ </div>
+ </div>
+ <% end %>
+</div>
+
+<%+footer%>
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/syslog.htm b/modules/luci-mod-status/luasrc/view/admin_status/syslog.htm
new file mode 100644
index 0000000000..fb734a76d9
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/syslog.htm
@@ -0,0 +1,12 @@
+<%#
+ Copyright 2008 Steven Barth <steven@midlink.org>
+ Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%+header%>
+<h2 name="content"><%:System Log%></h2>
+<div id="content_syslog">
+<textarea style="font-size: 12px;" readonly="readonly" wrap="off" rows="<%=syslog:cmatch("\n")+2%>" id="syslog"><%=syslog:pcdata()%></textarea>
+</div>
+<%+footer%>
diff --git a/modules/luci-mod-status/luasrc/view/admin_status/wireless.htm b/modules/luci-mod-status/luasrc/view/admin_status/wireless.htm
new file mode 100644
index 0000000000..8ec43cb0e6
--- /dev/null
+++ b/modules/luci-mod-status/luasrc/view/admin_status/wireless.htm
@@ -0,0 +1,371 @@
+<%#
+ Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<%-
+ local ntm = require "luci.model.network".init()
+
+ local dev
+ local devices = { }
+ for _, dev in luci.util.vspairs(luci.sys.net.devices()) do
+ if dev:match("^wlan%d") or dev:match("^ath%d") or dev:match("^wl%d") then
+ devices[#devices+1] = dev
+ end
+ end
+
+ local curdev = luci.http.formvalue("dev") or devices[1]
+-%>
+
+<%+header%>
+
+<script type="text/javascript">//<![CDATA[
+ var bwxhr = new XHR();
+
+ var G, G2;
+ var TIME = 0;
+ var RATE = 1;
+ var RSSI = 2;
+ var NOISE = 3;
+
+ var width = 760;
+ var height = 300;
+ var step = 5;
+
+ var data_wanted = Math.floor(width / step);
+ var data_fill = 0;
+ var data_stamp = 0;
+
+ var data_rssi = [ ];
+ var data_noise = [ ];
+ var data_rate = [ ];
+
+ var line_rssi;
+ var line_noise;
+ var line_rate;
+
+ var label_25, label_25_2;
+ var label_50, label_50_2;
+ var label_75, label_75_2;
+
+ var label_rssi_cur;
+ var label_rssi_avg;
+ var label_rssi_peak;
+
+ var label_noise_cur;
+ var label_noise_avg;
+ var label_noise_peak;
+
+ var label_rate_cur;
+ var label_rate_avg;
+ var label_rate_peak;
+
+ var label_scale;
+
+
+ /* wait for SVG */
+ window.setTimeout(
+ function() {
+ var svg = document.getElementById('iwsvg');
+ var svg2 = document.getElementById('iwsvg2');
+
+ try {
+ G = svg.getSVGDocument
+ ? svg.getSVGDocument() : svg.contentDocument;
+ G2 = svg2.getSVGDocument
+ ? svg2.getSVGDocument() : svg2.contentDocument;
+ }
+ catch(e) {
+ G = document.embeds['iwsvg'].getSVGDocument();
+ G2 = document.embeds['iwsvg2'].getSVGDocument();
+ }
+
+ if (!G || !G2)
+ {
+ window.setTimeout(arguments.callee, 1000);
+ }
+ else
+ {
+ /* find sizes */
+ width = svg.offsetWidth - 2;
+ height = svg.offsetHeight - 2;
+ data_wanted = Math.ceil(width / step);
+
+ /* prefill datasets */
+ for (var i = 0; i < data_wanted; i++)
+ {
+ data_rssi[i] = 0;
+ data_noise[i] = 0;
+ data_rate[i] = 0;
+ }
+
+ /* find svg elements */
+ line_rssi = G.getElementById('rssi');
+ line_noise = G.getElementById('noise');
+ line_rate = G2.getElementById('rate');
+
+ label_25 = G.getElementById('label_25');
+ label_50 = G.getElementById('label_50');
+ label_75 = G.getElementById('label_75');
+ label_25_2 = G2.getElementById('label_25');
+ label_50_2 = G2.getElementById('label_50');
+ label_75_2 = G2.getElementById('label_75');
+
+ label_rssi_cur = document.getElementById('rssi_bw_cur');
+ label_rssi_avg = document.getElementById('rssi_bw_avg');
+ label_rssi_peak = document.getElementById('rssi_bw_peak');
+
+ label_noise_cur = document.getElementById('noise_bw_cur');
+ label_noise_avg = document.getElementById('noise_bw_avg');
+ label_noise_peak = document.getElementById('noise_bw_peak');
+
+ label_rate_cur = document.getElementById('rate_bw_cur');
+ label_rate_avg = document.getElementById('rate_bw_avg');
+ label_rate_peak = document.getElementById('rate_bw_peak');
+
+ label_scale = document.getElementById('scale');
+ label_scale_2 = document.getElementById('scale2');
+
+
+ /* plot horizontal time interval lines */
+ for (var i = width % (step * 60); i < width; i += step * 60)
+ {
+ var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
+ line.setAttribute('x1', i);
+ line.setAttribute('y1', 0);
+ line.setAttribute('x2', i);
+ line.setAttribute('y2', '100%');
+ line.setAttribute('style', 'stroke:black;stroke-width:0.1');
+
+ var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
+ text.setAttribute('x', i + 5);
+ text.setAttribute('y', 15);
+ text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
+ text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
+
+ label_25.parentNode.appendChild(line);
+ label_25.parentNode.appendChild(text);
+
+
+ var line2 = G2.createElementNS('http://www.w3.org/2000/svg', 'line');
+ line2.setAttribute('x1', i);
+ line2.setAttribute('y1', 0);
+ line2.setAttribute('x2', i);
+ line2.setAttribute('y2', '100%');
+ line2.setAttribute('style', 'stroke:black;stroke-width:0.1');
+
+ var text2 = G2.createElementNS('http://www.w3.org/2000/svg', 'text');
+ text2.setAttribute('x', i + 5);
+ text2.setAttribute('y', 15);
+ text2.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
+ text2.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
+
+ label_25_2.parentNode.appendChild(line2);
+ label_25_2.parentNode.appendChild(text2);
+ }
+
+ label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
+ label_scale_2.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
+
+ /* render datasets, start update interval */
+ XHR.poll(3, '<%=build_url("admin/status/realtime/wireless_status", curdev)%>', null,
+ function(x, data)
+ {
+ var noise_floor = 255;
+ var rate_floor = 60000;
+
+ for (var i = 0; i < data.length; i++) {
+ noise_floor = Math.min(noise_floor, data[i][NOISE]);
+ rate_floor = Math.min(rate_floor, data[i][RATE]);
+ }
+
+ noise_floor -= 5;
+
+ var data_max = 0;
+ var data_scale = 0;
+ var data_max_2 = 0;
+ var data_scale_2 = 0;
+
+ var data_rssi_avg = 0;
+ var data_noise_avg = 0;
+ var data_rate_avg = 0;
+
+ var data_rssi_peak = 0;
+ var data_noise_peak = 0;
+ var data_rate_peak = 0;
+
+ for (var i = data_stamp ? 0 : 1; i < data.length; i++)
+ {
+ /* skip overlapping entries */
+ if (data[i][TIME] <= data_stamp)
+ continue;
+
+ data_rssi.push(data[i][RSSI] - noise_floor);
+ data_noise.push(data[i][NOISE] - noise_floor);
+ data_rate.push(Math.floor(data[i][RATE] / 1000));
+ }
+
+ /* cut off outdated entries */
+ data_rssi = data_rssi.slice(data_rssi.length - data_wanted, data_rssi.length);
+ data_noise = data_noise.slice(data_noise.length - data_wanted, data_noise.length);
+ data_rate = data_rate.slice(data_rate.length - data_wanted, data_rate.length);
+
+ /* find peak */
+ for (var i = 0; i < data_rssi.length; i++)
+ {
+ data_max = Math.max(data_max, data_rssi[i]);
+ data_max_2 = Math.max(data_max_2, data_rate[i]);
+
+ data_rssi_peak = Math.max(data_rssi_peak, data_rssi[i]);
+ data_noise_peak = Math.max(data_noise_peak, data_noise[i]);
+ data_rate_peak = Math.max(data_rate_peak, data_rate[i]);
+
+ if (i > 0)
+ {
+ data_rssi_avg = (data_rssi_avg + data_rssi[i]) / 2;
+ data_noise_avg = (data_noise_avg + data_noise[i]) / 2;
+ data_rate_avg = (data_rate_avg + data_rate[i]) / 2;
+ }
+ else
+ {
+ data_rssi_avg = data_rssi[i];
+ data_noise_avg = data_noise[i];
+ data_rate_avg = data_rate[i];
+ }
+ }
+
+ /* remember current timestamp, calculate horizontal scale */
+ data_stamp = data[data.length-1][TIME];
+ data_scale = (height / (data_max * 1.1)).toFixed(1);
+ data_scale_2 = (height / (data_max_2 * 1.1)).toFixed(1);
+
+ /* plot data */
+ var pt_rssi = '0,' + height;
+ var pt_noise = '0,' + height;
+ var pt_rate = '0,' + height;
+
+ var y_rssi = 0;
+ var y_noise = 0;
+ var y_rate = 0;
+
+ for (var i = 0; i < data_rssi.length; i++)
+ {
+ var x = i * step;
+
+ y_rssi = height - Math.floor(data_rssi[i] * data_scale);
+ y_noise = height - Math.floor(data_noise[i] * data_scale);
+ y_rate = height - Math.floor(data_rate[i] * data_scale_2);
+
+ y_rssi -= Math.floor(y_rssi % (1/data_scale));
+ y_noise -= Math.floor(y_noise % (1/data_scale));
+
+ pt_rssi += ' ' + x + ',' + y_rssi;
+ pt_noise += ' ' + x + ',' + y_noise;
+ pt_rate += ' ' + x + ',' + y_rate;
+ }
+
+ pt_rssi += ' ' + width + ',' + y_rssi + ' ' + width + ',' + height;
+ pt_noise += ' ' + width + ',' + y_noise + ' ' + width + ',' + height;
+ pt_rate += ' ' + width + ',' + y_rate + ' ' + width + ',' + height;
+
+ line_rssi.setAttribute('points', pt_rssi);
+ line_noise.setAttribute('points', pt_noise);
+ line_rate.setAttribute('points', pt_rate);
+
+ function wireless_label(dbm, noise)
+ {
+ if (noise)
+ return String.format("%d <%:dBm%> (SNR %d <%:dB%>)", noise_floor + dbm - 255, dbm - noise);
+ else
+ return String.format("%d <%:dBm%>", noise_floor + dbm - 255);
+ }
+
+ function rate_label(mbit)
+ {
+ return String.format("%d <%:Mbit/s%>", mbit);
+ }
+
+ label_25.firstChild.data = wireless_label(1.1 * 0.25 * data_max);
+ label_50.firstChild.data = wireless_label(1.1 * 0.50 * data_max);
+ label_75.firstChild.data = wireless_label(1.1 * 0.75 * data_max);
+
+ label_25_2.firstChild.data = rate_label(1.1 * 0.25 * data_max_2);
+ label_50_2.firstChild.data = rate_label(1.1 * 0.50 * data_max_2);
+ label_75_2.firstChild.data = rate_label(1.1 * 0.75 * data_max_2);
+
+ label_rssi_cur.innerHTML = wireless_label(data_rssi[data_rssi.length-1], data_noise[data_noise.length-1]).nobr();
+ label_noise_cur.innerHTML = wireless_label(data_noise[data_noise.length-1]).nobr();
+
+ label_rssi_avg.innerHTML = wireless_label(data_rssi_avg, data_noise_avg).nobr();
+ label_noise_avg.innerHTML = wireless_label(data_noise_avg).nobr();
+
+ label_rssi_peak.innerHTML = wireless_label(data_rssi_peak, data_noise_peak).nobr();
+ label_noise_peak.innerHTML = wireless_label(data_noise_peak).nobr();
+
+ label_rate_cur.innerHTML = rate_label(data_rate[data_rate.length-1]);
+ label_rate_avg.innerHTML = rate_label(data_rate_avg);
+ label_rate_peak.innerHTML = rate_label(data_rate_peak);
+ }
+ );
+
+ XHR.run();
+ }
+ }, 1000
+ );
+//]]></script>
+
+<h2 name="content"><%:Realtime Wireless%></h2>
+
+<ul class="cbi-tabmenu">
+ <% for _, dev in ipairs(devices) do %>
+ <li class="cbi-tab<%= dev == curdev and "" or "-disabled" %>"><a href="?dev=<%=pcdata(dev)%>"><%=pcdata(dev)%></a></li>
+ <% end %>
+</ul>
+
+<embed id="iwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/wireless.svg" />
+<div style="text-align:right"><small id="scale">-</small></div>
+<br />
+
+<div class="table" style="width:100%; table-layout:fixed" cellspacing="5">
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:Signal:%></strong></div>
+ <div class="td" id="rssi_bw_cur">0 <%:dBm%></div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="rssi_bw_avg">0 <%:dBm%></div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="rssi_bw_peak">0 <%:dBm%></div>
+ </div>
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid red"><%:Noise:%></strong></div>
+ <div class="td" id="noise_bw_cur">0 <%:dBm%></div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="noise_bw_avg">0 <%:dBm%></div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="noise_bw_peak">0 <%:dBm%></div>
+ </div>
+</div>
+
+<br />
+
+<embed id="iwsvg2" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/wifirate.svg" />
+<div style="text-align:right"><small id="scale2">-</small></div>
+<br />
+
+<div class="table" style="width:100%; table-layout:fixed" cellspacing="5">
+ <div class="tr">
+ <div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:Phy Rate:%></strong></div>
+ <div class="td" id="rate_bw_cur">0 MBit/s</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
+ <div class="td" id="rate_bw_avg">0 MBit/s</div>
+
+ <div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
+ <div class="td" id="rate_bw_peak">0 MBit/s</div>
+ </div>
+</div>
+
+<%+footer%>
diff --git a/modules/luci-mod-status/src/Makefile b/modules/luci-mod-status/src/Makefile
new file mode 100644
index 0000000000..d6ed8c6e46
--- /dev/null
+++ b/modules/luci-mod-status/src/Makefile
@@ -0,0 +1,14 @@
+%.o: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(FPIC) -c -o $@ $<
+
+clean:
+ rm -f luci-bwc *.o
+
+luci-bwc: luci-bwc.o
+ $(CC) $(LDFLAGS) -o $@ $^ -ldl
+
+compile: luci-bwc
+
+install: compile
+ mkdir -p $(DESTDIR)/usr/bin
+ cp luci-bwc $(DESTDIR)/usr/bin/luci-bwc
diff --git a/modules/luci-mod-status/src/luci-bwc.c b/modules/luci-mod-status/src/luci-bwc.c
new file mode 100644
index 0000000000..8ddd91727a
--- /dev/null
+++ b/modules/luci-mod-status/src/luci-bwc.c
@@ -0,0 +1,778 @@
+/*
+ * luci-bwc - Very simple bandwidth collector cache for LuCI realtime graphs
+ *
+ * Copyright (C) 2010 Jo-Philipp Wich <jow@openwrt.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <arpa/inet.h>
+
+#include <dlfcn.h>
+#include <iwinfo.h>
+
+#define STEP_COUNT 60
+#define STEP_TIME 1
+#define TIMEOUT 10
+
+#define PID_PATH "/var/run/luci-bwc.pid"
+
+#define DB_PATH "/var/lib/luci-bwc"
+#define DB_IF_FILE DB_PATH "/if/%s"
+#define DB_RD_FILE DB_PATH "/radio/%s"
+#define DB_CN_FILE DB_PATH "/connections"
+#define DB_LD_FILE DB_PATH "/load"
+
+#define IF_SCAN_PATTERN \
+ " %[^ :]:%u %u" \
+ " %*d %*d %*d %*d %*d %*d" \
+ " %u %u"
+
+#define LD_SCAN_PATTERN \
+ "%f %f %f"
+
+
+struct file_map {
+ int fd;
+ int size;
+ char *mmap;
+};
+
+struct traffic_entry {
+ uint32_t time;
+ uint32_t rxb;
+ uint32_t rxp;
+ uint32_t txb;
+ uint32_t txp;
+};
+
+struct conn_entry {
+ uint32_t time;
+ uint32_t udp;
+ uint32_t tcp;
+ uint32_t other;
+};
+
+struct load_entry {
+ uint32_t time;
+ uint16_t load1;
+ uint16_t load5;
+ uint16_t load15;
+};
+
+struct radio_entry {
+ uint32_t time;
+ uint16_t rate;
+ uint8_t rssi;
+ uint8_t noise;
+};
+
+static int readpid(void)
+{
+ int fd;
+ int pid = -1;
+ char buf[9] = { 0 };
+
+ if ((fd = open(PID_PATH, O_RDONLY)) > -1)
+ {
+ if (read(fd, buf, sizeof(buf)))
+ {
+ buf[8] = 0;
+ pid = atoi(buf);
+ }
+
+ close(fd);
+ }
+
+ return pid;
+}
+
+static int writepid(void)
+{
+ int fd;
+ int wlen;
+ char buf[9] = { 0 };
+
+ if ((fd = open(PID_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0600)) > -1)
+ {
+ wlen = snprintf(buf, sizeof(buf), "%i", getpid());
+ write(fd, buf, wlen);
+ close(fd);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int timeout = TIMEOUT;
+static int countdown = -1;
+
+static void reset_countdown(int sig)
+{
+ countdown = timeout;
+
+}
+
+
+static char *progname;
+static int prognamelen;
+
+static struct iwinfo_ops *backend = NULL;
+
+
+static int init_directory(char *path)
+{
+ char *p = path;
+
+ for (p = &path[1]; *p; p++)
+ {
+ if (*p == '/')
+ {
+ *p = 0;
+
+ if (mkdir(path, 0700) && (errno != EEXIST))
+ return -1;
+
+ *p = '/';
+ }
+ }
+
+ return 0;
+}
+
+static int init_file(char *path, int esize)
+{
+ int i, file;
+ char buf[sizeof(struct traffic_entry)] = { 0 };
+
+ if (init_directory(path))
+ return -1;
+
+ if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
+ {
+ for (i = 0; i < STEP_COUNT; i++)
+ {
+ if (write(file, buf, esize) < 0)
+ break;
+ }
+
+ close(file);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static inline uint32_t timeof(void *entry)
+{
+ return ntohl(((struct traffic_entry *)entry)->time);
+}
+
+static int update_file(const char *path, void *entry, int esize)
+{
+ int rv = -1;
+ int file;
+ char *map;
+
+ if ((file = open(path, O_RDWR)) >= 0)
+ {
+ map = mmap(NULL, esize * STEP_COUNT, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_LOCKED, file, 0);
+
+ if ((map != NULL) && (map != MAP_FAILED))
+ {
+ if (timeof(entry) > timeof(map + esize * (STEP_COUNT-1)))
+ {
+ memmove(map, map + esize, esize * (STEP_COUNT-1));
+ memcpy(map + esize * (STEP_COUNT-1), entry, esize);
+ }
+
+ munmap(map, esize * STEP_COUNT);
+
+ rv = 0;
+ }
+
+ close(file);
+ }
+
+ return rv;
+}
+
+static int mmap_file(const char *path, int esize, struct file_map *m)
+{
+ m->fd = -1;
+ m->size = -1;
+ m->mmap = NULL;
+
+ if ((m->fd = open(path, O_RDONLY)) >= 0)
+ {
+ m->size = STEP_COUNT * esize;
+ m->mmap = mmap(NULL, m->size, PROT_READ,
+ MAP_SHARED | MAP_LOCKED, m->fd, 0);
+
+ if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
+ return 0;
+ }
+
+ return -1;
+}
+
+static void umap_file(struct file_map *m)
+{
+ if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
+ munmap(m->mmap, m->size);
+
+ if (m->fd > -1)
+ close(m->fd);
+}
+
+static void * iw_open(void)
+{
+ return dlopen("/usr/lib/libiwinfo.so", RTLD_LAZY);
+}
+
+static int iw_update(
+ void *iw, const char *ifname, uint16_t *rate, uint8_t *rssi, uint8_t *noise
+) {
+ struct iwinfo_ops *(*probe)(const char *);
+ int val;
+
+ if (!backend)
+ {
+ probe = dlsym(iw, "iwinfo_backend");
+
+ if (!probe)
+ return 0;
+
+ backend = probe(ifname);
+
+ if (!backend)
+ return 0;
+ }
+
+ *rate = (backend->bitrate && !backend->bitrate(ifname, &val)) ? val : 0;
+ *rssi = (backend->signal && !backend->signal(ifname, &val)) ? val : 0;
+ *noise = (backend->noise && !backend->noise(ifname, &val)) ? val : 0;
+
+ return 1;
+}
+
+static void iw_close(void *iw)
+{
+ void (*finish)(void);
+
+ finish = dlsym(iw, "iwinfo_finish");
+
+ if (finish)
+ finish();
+
+ dlclose(iw);
+}
+
+
+static int update_ifstat(
+ const char *ifname, uint32_t rxb, uint32_t rxp, uint32_t txb, uint32_t txp
+) {
+ char path[1024];
+
+ struct stat s;
+ struct traffic_entry e;
+
+ snprintf(path, sizeof(path), DB_IF_FILE, ifname);
+
+ if (stat(path, &s))
+ {
+ if (init_file(path, sizeof(struct traffic_entry)))
+ {
+ fprintf(stderr, "Failed to init %s: %s\n",
+ path, strerror(errno));
+
+ return -1;
+ }
+ }
+
+ e.time = htonl(time(NULL));
+ e.rxb = htonl(rxb);
+ e.rxp = htonl(rxp);
+ e.txb = htonl(txb);
+ e.txp = htonl(txp);
+
+ return update_file(path, &e, sizeof(struct traffic_entry));
+}
+
+static int update_radiostat(
+ const char *ifname, uint16_t rate, uint8_t rssi, uint8_t noise
+) {
+ char path[1024];
+
+ struct stat s;
+ struct radio_entry e;
+
+ snprintf(path, sizeof(path), DB_RD_FILE, ifname);
+
+ if (stat(path, &s))
+ {
+ if (init_file(path, sizeof(struct radio_entry)))
+ {
+ fprintf(stderr, "Failed to init %s: %s\n",
+ path, strerror(errno));
+
+ return -1;
+ }
+ }
+
+ e.time = htonl(time(NULL));
+ e.rate = htons(rate);
+ e.rssi = rssi;
+ e.noise = noise;
+
+ return update_file(path, &e, sizeof(struct radio_entry));
+}
+
+static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
+{
+ char path[1024];
+
+ struct stat s;
+ struct conn_entry e;
+
+ snprintf(path, sizeof(path), DB_CN_FILE);
+
+ if (stat(path, &s))
+ {
+ if (init_file(path, sizeof(struct conn_entry)))
+ {
+ fprintf(stderr, "Failed to init %s: %s\n",
+ path, strerror(errno));
+
+ return -1;
+ }
+ }
+
+ e.time = htonl(time(NULL));
+ e.udp = htonl(udp);
+ e.tcp = htonl(tcp);
+ e.other = htonl(other);
+
+ return update_file(path, &e, sizeof(struct conn_entry));
+}
+
+static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
+{
+ char path[1024];
+
+ struct stat s;
+ struct load_entry e;
+
+ snprintf(path, sizeof(path), DB_LD_FILE);
+
+ if (stat(path, &s))
+ {
+ if (init_file(path, sizeof(struct load_entry)))
+ {
+ fprintf(stderr, "Failed to init %s: %s\n",
+ path, strerror(errno));
+
+ return -1;
+ }
+ }
+
+ e.time = htonl(time(NULL));
+ e.load1 = htons(load1);
+ e.load5 = htons(load5);
+ e.load15 = htons(load15);
+
+ return update_file(path, &e, sizeof(struct load_entry));
+}
+
+static int run_daemon(void)
+{
+ FILE *info;
+ uint32_t rxb, txb, rxp, txp;
+ uint32_t udp, tcp, other;
+ uint16_t rate;
+ uint8_t rssi, noise;
+ float lf1, lf5, lf15;
+ char line[1024];
+ char ifname[16];
+ int i;
+ void *iw;
+ struct sigaction sa;
+
+ struct stat s;
+ const char *ipc = stat("/proc/net/nf_conntrack", &s)
+ ? "/proc/net/ip_conntrack" : "/proc/net/nf_conntrack";
+
+ switch (fork())
+ {
+ case -1:
+ perror("fork()");
+ return -1;
+
+ case 0:
+ if (chdir("/") < 0)
+ {
+ perror("chdir()");
+ exit(1);
+ }
+
+ close(0);
+ close(1);
+ close(2);
+ break;
+
+ default:
+ return 0;
+ }
+
+ /* setup USR1 signal handler to reset timer */
+ sa.sa_handler = reset_countdown;
+ sa.sa_flags = SA_RESTART;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGUSR1, &sa, NULL);
+
+ /* write pid */
+ if (writepid())
+ {
+ fprintf(stderr, "Failed to write pid file: %s\n", strerror(errno));
+ return 1;
+ }
+
+ /* initialize iwinfo */
+ iw = iw_open();
+
+ /* go */
+ for (reset_countdown(0); countdown >= 0; countdown--)
+ {
+ /* alter progname for ps, top */
+ memset(progname, 0, prognamelen);
+ snprintf(progname, prognamelen, "luci-bwc %d", countdown);
+
+ if ((info = fopen("/proc/net/dev", "r")) != NULL)
+ {
+ while (fgets(line, sizeof(line), info))
+ {
+ if (strchr(line, '|'))
+ continue;
+
+ if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
+ {
+ if (strncmp(ifname, "lo", sizeof(ifname)))
+ update_ifstat(ifname, rxb, rxp, txb, txp);
+ }
+ }
+
+ fclose(info);
+ }
+
+ if (iw)
+ {
+ for (i = 0; i < 5; i++)
+ {
+#define iw_checkif(pattern) \
+ do { \
+ snprintf(ifname, sizeof(ifname), pattern, i); \
+ if (iw_update(iw, ifname, &rate, &rssi, &noise)) \
+ { \
+ update_radiostat(ifname, rate, rssi, noise); \
+ continue; \
+ } \
+ } while(0)
+
+ iw_checkif("wlan%d");
+ iw_checkif("ath%d");
+ iw_checkif("wl%d");
+ }
+ }
+
+ if ((info = fopen(ipc, "r")) != NULL)
+ {
+ udp = 0;
+ tcp = 0;
+ other = 0;
+
+ while (fgets(line, sizeof(line), info))
+ {
+ if (strstr(line, "TIME_WAIT"))
+ continue;
+
+ if ((strstr(line, "src=127.0.0.1 ") && strstr(line, "dst=127.0.0.1 "))
+ || (strstr(line, "src=::1 ") && strstr(line, "dst=::1 ")))
+ continue;
+
+ if (sscanf(line, "%*s %*d %s", ifname) || sscanf(line, "%s %*d", ifname))
+ {
+ if (!strcmp(ifname, "tcp"))
+ tcp++;
+ else if (!strcmp(ifname, "udp"))
+ udp++;
+ else
+ other++;
+ }
+ }
+
+ update_cnstat(udp, tcp, other);
+
+ fclose(info);
+ }
+
+ if ((info = fopen("/proc/loadavg", "r")) != NULL)
+ {
+ if (fscanf(info, LD_SCAN_PATTERN, &lf1, &lf5, &lf15))
+ {
+ update_ldstat((uint16_t)(lf1 * 100),
+ (uint16_t)(lf5 * 100),
+ (uint16_t)(lf15 * 100));
+ }
+
+ fclose(info);
+ }
+
+ sleep(STEP_TIME);
+ }
+
+ unlink(PID_PATH);
+
+ if (iw)
+ iw_close(iw);
+
+ return 0;
+}
+
+static void check_daemon(void)
+{
+ int pid;
+
+ if ((pid = readpid()) < 0 || kill(pid, 0) < 0)
+ {
+ /* daemon ping failed, try to start it up */
+ if (run_daemon())
+ {
+ fprintf(stderr,
+ "Failed to ping daemon and unable to start it up: %s\n",
+ strerror(errno));
+
+ exit(1);
+ }
+ }
+ else if (kill(pid, SIGUSR1))
+ {
+ fprintf(stderr, "Failed to send signal: %s\n", strerror(errno));
+ exit(2);
+ }
+}
+
+static int run_dump_ifname(const char *ifname)
+{
+ int i;
+ char path[1024];
+ struct file_map m;
+ struct traffic_entry *e;
+
+ check_daemon();
+ snprintf(path, sizeof(path), DB_IF_FILE, ifname);
+
+ if (mmap_file(path, sizeof(struct traffic_entry), &m))
+ {
+ fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
+ return 1;
+ }
+
+ for (i = 0; i < m.size; i += sizeof(struct traffic_entry))
+ {
+ e = (struct traffic_entry *) &m.mmap[i];
+
+ if (!e->time)
+ continue;
+
+ printf("[ %u, %u, %" PRIu32
+ ", %u, %u ]%s\n",
+ ntohl(e->time),
+ ntohl(e->rxb), ntohl(e->rxp),
+ ntohl(e->txb), ntohl(e->txp),
+ ((i + sizeof(struct traffic_entry)) < m.size) ? "," : "");
+ }
+
+ umap_file(&m);
+
+ return 0;
+}
+
+static int run_dump_radio(const char *ifname)
+{
+ int i;
+ char path[1024];
+ struct file_map m;
+ struct radio_entry *e;
+
+ check_daemon();
+ snprintf(path, sizeof(path), DB_RD_FILE, ifname);
+
+ if (mmap_file(path, sizeof(struct radio_entry), &m))
+ {
+ fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
+ return 1;
+ }
+
+ for (i = 0; i < m.size; i += sizeof(struct radio_entry))
+ {
+ e = (struct radio_entry *) &m.mmap[i];
+
+ if (!e->time)
+ continue;
+
+ printf("[ %u, %d, %d, %d ]%s\n",
+ ntohl(e->time),
+ e->rate, e->rssi, e->noise,
+ ((i + sizeof(struct radio_entry)) < m.size) ? "," : "");
+ }
+
+ umap_file(&m);
+
+ return 0;
+}
+
+static int run_dump_conns(void)
+{
+ int i;
+ char path[1024];
+ struct file_map m;
+ struct conn_entry *e;
+
+ check_daemon();
+ snprintf(path, sizeof(path), DB_CN_FILE);
+
+ if (mmap_file(path, sizeof(struct conn_entry), &m))
+ {
+ fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
+ return 1;
+ }
+
+ for (i = 0; i < m.size; i += sizeof(struct conn_entry))
+ {
+ e = (struct conn_entry *) &m.mmap[i];
+
+ if (!e->time)
+ continue;
+
+ printf("[ %u, %u, %u, %u ]%s\n",
+ ntohl(e->time), ntohl(e->udp),
+ ntohl(e->tcp), ntohl(e->other),
+ ((i + sizeof(struct conn_entry)) < m.size) ? "," : "");
+ }
+
+ umap_file(&m);
+
+ return 0;
+}
+
+static int run_dump_load(void)
+{
+ int i;
+ char path[1024];
+ struct file_map m;
+ struct load_entry *e;
+
+ check_daemon();
+ snprintf(path, sizeof(path), DB_LD_FILE);
+
+ if (mmap_file(path, sizeof(struct load_entry), &m))
+ {
+ fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
+ return 1;
+ }
+
+ for (i = 0; i < m.size; i += sizeof(struct load_entry))
+ {
+ e = (struct load_entry *) &m.mmap[i];
+
+ if (!e->time)
+ continue;
+
+ printf("[ %u, %u, %u, %u ]%s\n",
+ ntohl(e->time),
+ ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
+ ((i + sizeof(struct load_entry)) < m.size) ? "," : "");
+ }
+
+ umap_file(&m);
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int opt;
+
+ progname = argv[0];
+ prognamelen = -1;
+
+ for (opt = 0; opt < argc; opt++)
+ prognamelen += 1 + strlen(argv[opt]);
+
+ while ((opt = getopt(argc, argv, "t:i:r:cl")) > -1)
+ {
+ switch (opt)
+ {
+ case 't':
+ timeout = atoi(optarg);
+ break;
+
+ case 'i':
+ if (optarg)
+ return run_dump_ifname(optarg);
+ break;
+
+ case 'r':
+ if (optarg)
+ return run_dump_radio(optarg);
+ break;
+
+ case 'c':
+ return run_dump_conns();
+
+ case 'l':
+ return run_dump_load();
+
+ default:
+ break;
+ }
+ }
+
+ fprintf(stderr,
+ "Usage:\n"
+ " %s [-t timeout] -i ifname\n"
+ " %s [-t timeout] -r radiodev\n"
+ " %s [-t timeout] -c\n"
+ " %s [-t timeout] -l\n",
+ argv[0], argv[0], argv[0], argv[0]
+ );
+
+ return 1;
+}