summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-mod-status
diff options
context:
space:
mode:
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;
+}