From 0737e27eb829b38ad6ebbf1458dd71951006a48c Mon Sep 17 00:00:00 2001 From: "Ycarus (Yannick Chabanois)" Date: Tue, 30 Jul 2019 14:22:11 +0200 Subject: [PATCH] Disable processes list for now --- luci-mod-status/Makefile | 18 + .../luci-static/resources/bandwidth.svg | 16 + .../luci-static/resources/connections.svg | 17 + .../htdocs/luci-static/resources/load.svg | 17 + .../resources/view/status/index.js | 239 ++++++ .../resources/view/status/iptables.js | 253 ++++++ .../htdocs/luci-static/resources/wifirate.svg | 15 + .../htdocs/luci-static/resources/wireless.svg | 16 + .../luasrc/controller/admin/status.lua | 175 ++++ .../model/cbi/admin_status/processes.lua | 34 + .../luasrc/view/admin_status/bandwidth.htm | 308 +++++++ .../luasrc/view/admin_status/connections.htm | 405 +++++++++ .../luasrc/view/admin_status/dmesg.htm | 12 + .../luasrc/view/admin_status/index.htm | 148 ++++ .../view/admin_status/index/10-system.htm | 29 + .../view/admin_status/index/20-memory.htm | 31 + .../view/admin_status/index/30-network.htm | 17 + .../admin_status/index/40-dhcp-leases.htm | 14 + .../luasrc/view/admin_status/index/50-dsl.htm | 20 + .../view/admin_status/index/60-wifi.htm | 26 + .../luasrc/view/admin_status/iptables.htm | 73 ++ .../luasrc/view/admin_status/load.htm | 283 +++++++ .../luasrc/view/admin_status/routes.htm | 156 ++++ .../luasrc/view/admin_status/syslog.htm | 12 + .../luasrc/view/admin_status/wireless.htm | 370 +++++++++ luci-mod-status/src/Makefile | 14 + luci-mod-status/src/luci-bwc.c | 778 ++++++++++++++++++ 27 files changed, 3496 insertions(+) create mode 100644 luci-mod-status/Makefile create mode 100644 luci-mod-status/htdocs/luci-static/resources/bandwidth.svg create mode 100644 luci-mod-status/htdocs/luci-static/resources/connections.svg create mode 100644 luci-mod-status/htdocs/luci-static/resources/load.svg create mode 100644 luci-mod-status/htdocs/luci-static/resources/view/status/index.js create mode 100644 luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js create mode 100644 luci-mod-status/htdocs/luci-static/resources/wifirate.svg create mode 100644 luci-mod-status/htdocs/luci-static/resources/wireless.svg create mode 100644 luci-mod-status/luasrc/controller/admin/status.lua create mode 100644 luci-mod-status/luasrc/model/cbi/admin_status/processes.lua create mode 100644 luci-mod-status/luasrc/view/admin_status/bandwidth.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/connections.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/dmesg.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/index.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/index/10-system.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/index/20-memory.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/index/30-network.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/index/40-dhcp-leases.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/index/50-dsl.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/index/60-wifi.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/iptables.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/load.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/routes.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/syslog.htm create mode 100644 luci-mod-status/luasrc/view/admin_status/wireless.htm create mode 100644 luci-mod-status/src/Makefile create mode 100644 luci-mod-status/src/luci-bwc.c diff --git a/luci-mod-status/Makefile b/luci-mod-status/Makefile new file mode 100644 index 000000000..2205503c0 --- /dev/null +++ b/luci-mod-status/Makefile @@ -0,0 +1,18 @@ +# +# Copyright (C) 2008-2014 The LuCI Team +# +# 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/luci.mk + +# call BuildPackage - OpenWrt buildroot signature + diff --git a/luci-mod-status/htdocs/luci-static/resources/bandwidth.svg b/luci-mod-status/htdocs/luci-static/resources/bandwidth.svg new file mode 100644 index 000000000..5a121b85c --- /dev/null +++ b/luci-mod-status/htdocs/luci-static/resources/bandwidth.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/luci-mod-status/htdocs/luci-static/resources/connections.svg b/luci-mod-status/htdocs/luci-static/resources/connections.svg new file mode 100644 index 000000000..5794e7942 --- /dev/null +++ b/luci-mod-status/htdocs/luci-static/resources/connections.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/luci-mod-status/htdocs/luci-static/resources/load.svg b/luci-mod-status/htdocs/luci-static/resources/load.svg new file mode 100644 index 000000000..716d37617 --- /dev/null +++ b/luci-mod-status/htdocs/luci-static/resources/load.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/luci-mod-status/htdocs/luci-static/resources/view/status/index.js b/luci-mod-status/htdocs/luci-static/resources/view/status/index.js new file mode 100644 index 000000000..edf7489d4 --- /dev/null +++ b/luci-mod-status/htdocs/luci-static/resources/view/status/index.js @@ -0,0 +1,239 @@ +function progressbar(query, value, max, byte) +{ + var pg = document.querySelector(query), + vn = parseInt(value) || 0, + mn = parseInt(max) || 100, + fv = byte ? String.format('%1024.2mB', value) : value, + fm = byte ? String.format('%1024.2mB', max) : max, + pc = Math.floor((100 / mn) * vn); + + if (pg) { + pg.firstElementChild.style.width = pc + '%'; + pg.setAttribute('title', '%s / %s (%d%%)'.format(fv, fm, pc)); + } +} + +function renderBox(title, active, childs) { + childs = childs || []; + childs.unshift(L.itemlist(E('span'), [].slice.call(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 || '' }), + L.itemlist(E('span'), [].slice.call(arguments, 2)) + ]); +} + +L.poll(5, L.location(), { 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( + L.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.ipaddrs) ? ifc.ipaddrs[0] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[1] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[2] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[3] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[4] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[5] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[6] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[7] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[8] : null, + _('Address'), (ifc.ipaddrs) ? ifc.ipaddrs[9] : null, + _('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)); + } + + 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( + L.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.ip6addrs) ? ifc6.ip6addrs[0] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[1] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[2] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[3] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[4] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[5] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[6] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[7] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[8] : null, + _('Address'), (ifc6.ip6addrs) ? ifc6.ip6addrs[9] : null, + _('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)); + } + + var ds = document.getElementById('dsl_status_table'); + if (ds) { + 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)); + } + + 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 = L.resource('icons/signal-none.png'); + else if (net.quality <= 0) + icon = L.resource('icons/signal-0.png'); + else if (net.quality < 25) + icon = L.resource('icons/signal-0-25.png'); + else if (net.quality < 50) + icon = L.resource('icons/signal-25-50.png'); + else if (net.quality < 75) + icon = L.resource('icons/signal-50-75.png'); + else + icon = L.resource('icons/signal-75-100.png'); + + vifs.push(renderBadge( + icon, + '%s: %d dBm / %s: %d%%'.format(_('Signal'), net.signal, _('Quality'), 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 %s)'.format(net0.channel, net0.frequency, _('GHz')) : '-', + _('Bitrate'), net0.bitrate ? '%d %s'.format(net0.bitrate, _('Mbit/s')) : '-')); + } + + if (!ws.lastElementChild) + ws.appendChild(E('em', _('No information available'))); + } + + 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 + ); + + progressbar('#memtotal', + info.memory.free + info.memory.buffered, + info.memory.total, + true); + + progressbar('#memfree', + info.memory.free, + info.memory.total, + true); + + progressbar('#membuff', + info.memory.buffered, + info.memory.total, + true); + + progressbar('#swaptotal', + info.swap.free, + info.swap.total, + true); + + progressbar('#swapfree', + info.swap.free, + info.swap.total, + true); + + progressbar('#conns', + info.conncount, info.connmax, false); + + } +); diff --git a/luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js b/luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js new file mode 100644 index 000000000..a742243a0 --- /dev/null +++ b/luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js @@ -0,0 +1,253 @@ +var table_names = [ 'Filter', 'NAT', 'Mangle', 'Raw' ], + current_mode = document.querySelector('.cbi-tab[data-mode="6"]') ? 6 : 4; + +function create_table_section(table) +{ + var idiv = document.getElementById('iptables'), + tdiv = idiv.querySelector('[data-table="%s"]'.format(table)), + title = '%s: %s'.format(_('Table'), table); + + if (!tdiv) { + tdiv = E('div', { 'data-table': table }, [ + E('h3', {}, title), + E('div') + ]); + + if (idiv.firstElementChild.nodeName.toLowerCase() === 'p') + idiv.removeChild(idiv.firstElementChild); + + var added = false, thisIdx = table_names.indexOf(table); + + idiv.querySelectorAll('[data-table]').forEach(function(child) { + var childIdx = table_names.indexOf(child.getAttribute('data-table')); + + if (added === false && childIdx > thisIdx) { + idiv.insertBefore(tdiv, child); + added = true; + } + }); + + if (added === false) + idiv.appendChild(tdiv); + } + + return tdiv.lastElementChild; +} + +function create_chain_section(table, chain, policy, packets, bytes, references) +{ + var tdiv = create_table_section(table), + cdiv = tdiv.querySelector('[data-chain="%s"]'.format(chain)), + title; + + if (policy) + title = '%s %s (%s: %s, %d %s, %.2mB %s)' + .format(_('Chain'), chain, _('Policy'), policy, packets, _('Packets'), bytes, _('Traffic')); + else + title = '%s %s (%d %s)' + .format(_('Chain'), chain, references, _('References')); + + if (!cdiv) { + cdiv = E('div', { 'data-chain': chain }, [ + E('h4', { 'id': 'rule_%s_%s'.format(table.toLowerCase(), chain) }, title), + E('div', { 'class': 'table' }, [ + E('div', { 'class': 'tr table-titles' }, [ + E('div', { 'class': 'th center' }, _('Pkts.')), + E('div', { 'class': 'th center' }, _('Traffic')), + E('div', { 'class': 'th' }, _('Target')), + E('div', { 'class': 'th' }, _('Prot.')), + E('div', { 'class': 'th' }, _('In')), + E('div', { 'class': 'th' }, _('Out')), + E('div', { 'class': 'th' }, _('Source')), + E('div', { 'class': 'th' }, _('Destination')), + E('div', { 'class': 'th' }, _('Options')), + E('div', { 'class': 'th' }, _('Comment')) + ]) + ]) + ]); + + tdiv.appendChild(cdiv); + } + else { + cdiv.firstElementChild.innerHTML = title; + } + + return cdiv.lastElementChild; +} + +function update_chain_section(chaintable, rows) +{ + if (!chaintable) + return; + + cbi_update_table(chaintable, rows, _('No rules in this chain.')); + + if (rows.length === 0 && + document.querySelector('form > [data-hide-empty="true"]')) + chaintable.parentNode.style.display = 'none'; + else + chaintable.parentNode.style.display = ''; + + chaintable.parentNode.setAttribute('data-empty', rows.length === 0); +} + +function hide_empty(btn) +{ + var hide = (btn.getAttribute('data-hide-empty') === 'false'); + + btn.setAttribute('data-hide-empty', hide); + btn.value = hide ? _('Show empty chains') : _('Hide empty chains'); + btn.blur(); + + document.querySelectorAll('[data-chain][data-empty="true"]') + .forEach(function(chaintable) { + chaintable.style.display = hide ? 'none' : ''; + }); +} + +function jump_target(ev) +{ + var link = ev.target, + table = findParent(link, '[data-table]').getAttribute('data-table'), + chain = link.textContent, + num = +link.getAttribute('data-num'), + elem = document.getElementById('rule_%s_%s'.format(table.toLowerCase(), chain)); + + if (elem) { + (document.documentElement || document.body.parentNode || document.body).scrollTop = elem.offsetTop - 40; + elem.classList.remove('flash'); + void elem.offsetWidth; + elem.classList.add('flash'); + + if (num) { + var rule = elem.nextElementSibling.childNodes[num]; + if (rule) { + rule.classList.remove('flash'); + void rule.offsetWidth; + rule.classList.add('flash'); + } + } + } +} + +function parse_output(table, s) +{ + var current_chain = null; + var current_rules = []; + var seen_chains = {}; + var chain_refs = {}; + var re = /([^\n]*)\n/g; + var m, m2; + + while ((m = re.exec(s)) != null) { + if (m[1].match(/^Chain (.+) \(policy (\w+) (\d+) packets, (\d+) bytes\)$/)) { + var chain = RegExp.$1, + policy = RegExp.$2, + packets = +RegExp.$3, + bytes = +RegExp.$4; + + update_chain_section(current_chain, current_rules); + + seen_chains[chain] = true; + current_chain = create_chain_section(table, chain, policy, packets, bytes); + current_rules = []; + } + else if (m[1].match(/^Chain (.+) \((\d+) references\)$/)) { + var chain = RegExp.$1, + references = +RegExp.$2; + + update_chain_section(current_chain, current_rules); + + seen_chains[chain] = true; + current_chain = create_chain_section(table, chain, null, null, null, references); + current_rules = []; + } + else if (m[1].match(/^num /)) { + continue; + } + else if ((m2 = m[1].match(/^(\d+) +(\d+) +(\d+) +(.*?) +(\S+) +(\S*) +(\S+) +(\S+) +([a-f0-9:.]+(?:\/[a-f0-9:.]+)?) +([a-f0-9:.]+(?:\/[a-f0-9:.]+)?) +(.+)$/)) !== null) { + var num = +m2[1], + pkts = +m2[2], + bytes = +m2[3], + target = m2[4], + proto = m2[5], + indev = m2[7], + outdev = m2[8], + srcnet = m2[9], + dstnet = m2[10], + options = m2[11] || '-', + comment = '-'; + + options = options.trim().replace(/(?:^| )\/\* (.+) \*\//, + function(m1, m2) { + comment = m2.replace(/^!fw3(: |$)/, '').trim() || '-'; + return ''; + }) || '-'; + + current_rules.push([ + '%.2m'.format(pkts).nobr(), + '%.2mB'.format(bytes).nobr(), + target ? '%s'.format(target) : '-', + proto, + (indev !== '*') ? '%s'.format(indev) : '*', + (outdev !== '*') ? '%s'.format(outdev) : '*', + srcnet, + dstnet, + options, + comment + ]); + + if (target) { + chain_refs[target] = chain_refs[target] || []; + chain_refs[target].push([ current_chain, num ]); + } + } + } + + update_chain_section(current_chain, current_rules); + + document.querySelectorAll('[data-table="%s"] [data-chain]'.format(table)) + .forEach(function(cdiv) { + if (!seen_chains[cdiv.getAttribute('data-chain')]) { + cdiv.parentNode.removeChild(cdiv); + return; + } + + cdiv.querySelectorAll('.target').forEach(function(tspan) { + if (seen_chains[tspan.textContent]) { + tspan.classList.add('jump'); + tspan.addEventListener('click', jump_target); + } + }); + + cdiv.querySelectorAll('.references').forEach(function(rspan) { + var refs = chain_refs[cdiv.getAttribute('data-chain')]; + if (refs && refs.length) { + rspan.classList.add('cbi-tooltip-container'); + rspan.appendChild(E('small', { 'class': 'cbi-tooltip ifacebadge', 'style': 'top:1em; left:auto' }, [ E('ul') ])); + + refs.forEach(function(ref) { + var chain = ref[0].parentNode.getAttribute('data-chain'), + num = ref[1]; + + rspan.lastElementChild.lastElementChild.appendChild(E('li', {}, [ + _('Chain'), ' ', + E('span', { + 'class': 'jump', + 'data-num': num, + 'onclick': 'jump_target(event)' + }, chain), + ', %s #%d'.format(_('Rule'), num) + ])); + }); + } + }); + }); +} + +table_names.forEach(function(table) { + L.poll(5, L.url('admin/status/iptables_dump', current_mode, table.toLowerCase()), null, + function (xhr) { + parse_output(table, xhr.responseText); + }); +}); diff --git a/luci-mod-status/htdocs/luci-static/resources/wifirate.svg b/luci-mod-status/htdocs/luci-static/resources/wifirate.svg new file mode 100644 index 000000000..e75ea614c --- /dev/null +++ b/luci-mod-status/htdocs/luci-static/resources/wifirate.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/luci-mod-status/htdocs/luci-static/resources/wireless.svg b/luci-mod-status/htdocs/luci-static/resources/wireless.svg new file mode 100644 index 000000000..00cc2a12f --- /dev/null +++ b/luci-mod-status/htdocs/luci-static/resources/wireless.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/luci-mod-status/luasrc/controller/admin/status.lua b/luci-mod-status/luasrc/controller/admin/status.lua new file mode 100644 index 000000000..689e6e138 --- /dev/null +++ b/luci-mod-status/luasrc/controller/admin/status.lua @@ -0,0 +1,175 @@ +-- Copyright 2008 Steven Barth +-- Copyright 2011 Jo-Philipp Wich +-- 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_dump"}, call("dump_iptables")).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 dump_iptables(family, table) + local prefix = (family == "6") and "ip6" or "ip" + local ok, lines = pcall(io.lines, "/proc/net/%s_tables_names" % prefix) + if ok and lines then + local s + for s in lines do + if s == table then + luci.http.prepare_content("text/plain") + luci.sys.process.exec({ + "/usr/sbin/%stables" % prefix, "-w", "-t", table, + "--line-numbers", "-nxvL" + }, luci.http.write) + return + end + end + end + + luci.http.status(404, "No such table") + luci.http.prepare_content("text/plain") +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/luci-mod-status/luasrc/model/cbi/admin_status/processes.lua b/luci-mod-status/luasrc/model/cbi/admin_status/processes.lua new file mode 100644 index 000000000..0a6e48fd8 --- /dev/null +++ b/luci-mod-status/luasrc/model/cbi/admin_status/processes.lua @@ -0,0 +1,34 @@ +-- Copyright 2008 Steven Barth +-- Copyright 2008 Jo-Philipp Wich +-- 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/luci-mod-status/luasrc/view/admin_status/bandwidth.htm b/luci-mod-status/luasrc/view/admin_status/bandwidth.htm new file mode 100644 index 000000000..5cc661ad1 --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/bandwidth.htm @@ -0,0 +1,308 @@ +<%# + Copyright 2010-2018 Jo-Philipp Wich + 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%> + + + +

<%:Realtime Traffic%>

+ + + + +
-
+
+ +
+
+
<%:Inbound:%>
+
0 <%:kbit/s%>
(0 <%:kB/s%>)
+ +
<%:Average:%>
+
0 <%:kbit/s%>
(0 <%:kB/s%>)
+ +
<%:Peak:%>
+
0 <%:kbit/s%>
(0 <%:kB/s%>)
+
+
+
<%:Outbound:%>
+
0 <%:kbit/s%>
(0 <%:kB/s%>)
+ +
<%:Average:%>
+
0 <%:kbit/s%>
(0 <%:kB/s%>)
+ +
<%:Peak:%>
+
0 <%:kbit/s%>
(0 <%:kB/s%>)
+
+
+ +<%+footer%> diff --git a/luci-mod-status/luasrc/view/admin_status/connections.htm b/luci-mod-status/luasrc/view/admin_status/connections.htm new file mode 100644 index 000000000..37debcde6 --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/connections.htm @@ -0,0 +1,405 @@ +<%# + Copyright 2010-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<%+header%> + + + +

<%:Realtime Connections%>

+ +
<%:This page gives an overview over currently active network connections.%>
+ +
+ <%:Active Connections%> + + +
-
+
+ +
+
+
<%:UDP:%>
+
0
+ +
<%:Average:%>
+
0
+ +
<%:Peak:%>
+
0
+
+
+
<%:TCP:%>
+
0
+ +
<%:Average:%>
+
0
+ +
<%:Peak:%>
+
0
+
+
+
<%:Other:%>
+
0
+ +
<%:Average:%>
+
0
+ +
<%:Peak:%>
+
0
+
+
+
+ +
+
+
+
<%:Network%>
+
<%:Protocol%>
+
<%:Source%>
+
<%:Destination%>
+
<%:Transfer%>
+
+ +
+
+ <%:Collecting data...%> +
+
+
+
+
+ +<%+footer%> diff --git a/luci-mod-status/luasrc/view/admin_status/dmesg.htm b/luci-mod-status/luasrc/view/admin_status/dmesg.htm new file mode 100644 index 000000000..1a8770ef8 --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/dmesg.htm @@ -0,0 +1,12 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<%+header%> +

<%:Kernel Log%>

+
+ +
+<%+footer%> diff --git a/luci-mod-status/luasrc/view/admin_status/index.htm b/luci-mod-status/luasrc/view/admin_status/index.htm new file mode 100644 index 000000000..5b53e8ed3 --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/index.htm @@ -0,0 +1,148 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2018 Jo-Philipp Wich + 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" + + if luci.http.formvalue("status") == "1" then + + local sysinfo = luci.util.ubus("system", "info") 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") + + 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 = { + ipaddrs = v:ipaddrs(), + gwaddr = v:gwaddr(), + 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 = { + ip6addrs = v:ip6addrs(), + 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%> + +

<%:Status%>

+ +<%- + local incdir = util.libpath() .. "/view/admin_status/index/" + if fs.access(incdir) then + local _, inc + local includes = {} + for inc in fs.dir(incdir) do + if inc:match("%.htm$") then + includes[#includes + 1] = inc:gsub("%.htm$", "") + end + end + for _, inc in luci.util.vspairs(includes) do + include("admin_status/index/" .. inc) + end + end +-%> + + + +<%+footer%> diff --git a/luci-mod-status/luasrc/view/admin_status/index/10-system.htm b/luci-mod-status/luasrc/view/admin_status/index/10-system.htm new file mode 100644 index 000000000..994550ec2 --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/index/10-system.htm @@ -0,0 +1,29 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local boardinfo = luci.util.ubus("system", "board") or { } + local unameinfo = nixio.uname() or { } + local ver = require "luci.version" +%> + +
+

<%:System%>

+ +
+
<%:Hostname%>
<%=luci.sys.hostname() or "?"%>
+
<%:Model%>
<%=pcdata(boardinfo.model or "?")%>
+
<%:Architecture%>
<%=pcdata(boardinfo.system or "?")%>
+
<%:Firmware Version%>
+ <%=pcdata(ver.distname)%> <%=pcdata(ver.distversion)%> / + <%=pcdata(ver.luciname)%> (<%=pcdata(ver.luciversion)%>) +
+
<%:Kernel Version%>
<%=unameinfo.release or "?"%>
+
<%:Local Time%>
-
+
<%:Uptime%>
-
+
<%:Load Average%>
-
+
+
diff --git a/luci-mod-status/luasrc/view/admin_status/index/20-memory.htm b/luci-mod-status/luasrc/view/admin_status/index/20-memory.htm new file mode 100644 index 000000000..13774704f --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/index/20-memory.htm @@ -0,0 +1,31 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local sysinfo = luci.util.ubus("system", "info") or { } + local has_swap = sysinfo.swap and sysinfo.swap.total > 0 or false +%> + +
+

<%:Memory%>

+ +
+
<%:Total Available%>
+
<%:Free%>
+
<%:Buffered%>
+
+
+ +<% if has_swap then %> +
+

<%:Swap%>

+ +
+
<%:Total Available%>
+
<%:Free%>
+
+
+<% end %> diff --git a/luci-mod-status/luasrc/view/admin_status/index/30-network.htm b/luci-mod-status/luasrc/view/admin_status/index/30-network.htm new file mode 100644 index 000000000..945a31b2e --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/index/30-network.htm @@ -0,0 +1,17 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +
+

<%:Network%>

+ +
+

<%:Collecting data...%>

+
+ +
+
<%:Active Connections%>
+
+
diff --git a/luci-mod-status/luasrc/view/admin_status/index/40-dhcp-leases.htm b/luci-mod-status/luasrc/view/admin_status/index/40-dhcp-leases.htm new file mode 100644 index 000000000..aaf366144 --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/index/40-dhcp-leases.htm @@ -0,0 +1,14 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local fs = require "nixio.fs" + local has_dhcp = fs.access("/etc/config/dhcp") + + if has_dhcp then + include("lease_status") + end +%> diff --git a/luci-mod-status/luasrc/view/admin_status/index/50-dsl.htm b/luci-mod-status/luasrc/view/admin_status/index/50-dsl.htm new file mode 100644 index 000000000..f37bf147a --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/index/50-dsl.htm @@ -0,0 +1,20 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local fs = require "nixio.fs" + local has_dsl = fs.access("/etc/init.d/dsl_control") +%> + +<% if has_dsl then %> +
+

<%:DSL%>

+ +
+

<%:Collecting data...%>

+
+
+<% end %> diff --git a/luci-mod-status/luasrc/view/admin_status/index/60-wifi.htm b/luci-mod-status/luasrc/view/admin_status/index/60-wifi.htm new file mode 100644 index 000000000..7338bc77f --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/index/60-wifi.htm @@ -0,0 +1,26 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local fs = require "nixio.fs" + local has_wifi = ((fs.stat("/etc/config/wireless", "size") or 0) > 0) +%> + +<% if has_wifi then %> +
+

<%:Wireless%>

+ +
+

<%:Collecting data...%>

+
+
+ +
+

<%:Associated Stations%>

+ + <%+wifi_assoclist%> +
+<% end %> diff --git a/luci-mod-status/luasrc/view/admin_status/iptables.htm b/luci-mod-status/luasrc/view/admin_status/iptables.htm new file mode 100644 index 000000000..89f229f3b --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/iptables.htm @@ -0,0 +1,73 @@ +<%# + Copyright 2008-2009 Steven Barth + Copyright 2008-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<%- + local fs = require "nixio.fs" + 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 +-%> + +<%+header%> + + + +

<%:Firewall Status%>

+ +<% if has_ip6tables then %> + +<% end %> + +
+
" style="position: absolute; right: 0"> + + + + + +
+
+ +
+

<%:Collecting data...%>

+
+ + + +<%+footer%> diff --git a/luci-mod-status/luasrc/view/admin_status/load.htm b/luci-mod-status/luasrc/view/admin_status/load.htm new file mode 100644 index 000000000..d31d34062 --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/load.htm @@ -0,0 +1,283 @@ +<%# + Copyright 2010-2018 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<%+header%> + + + +

<%:Realtime Load%>

+ + +
-
+
+ +
+
+
<%:1 Minute Load:%>
+
0
+ +
<%:Average:%>
+
0
+ +
<%:Peak:%>
+
0
+
+
+
<%:5 Minute Load:%>
+
0
+ +
<%:Average:%>
+
0
+ +
<%:Peak:%>
+
0
+
+
+
<%:15 Minute Load:%>
+
0
+ +
<%:Average:%>
+
0
+ +
<%:Peak:%>
+
0
+
+
+ +<%+footer%> diff --git a/luci-mod-status/luasrc/view/admin_status/routes.htm b/luci-mod-status/luasrc/view/admin_status/routes.htm new file mode 100644 index 000000000..74779f6ad --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/routes.htm @@ -0,0 +1,156 @@ +<%# + Copyright 2008-2009 Steven Barth + Copyright 2008-2015 Jo-Philipp Wich + 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%> + + +
+

<%:Routes%>

+
<%:The following rules are currently active on this system.%>
+ +
+ ARP +
+
+
+
<%_IPv4-Address%>
+
<%_MAC-Address%>
+
<%:Interface%>
+
+ + <% + for _, v in ipairs(ip.neighbors({ family = 4 })) do + if v.mac then + %> +
+
<%=v.dest%>
+
<%=v.mac%>
+
<%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%>
+
+ <% + style = not style + end + end + %> +
+
+
+ +
+ <%_Active IPv4-Routes%> +
+
+
+
<%:Network%>
+
<%:Target%>
+
<%_IPv4-Gateway%>
+
<%:Metric%>
+
<%:Table%>
+
+ <% for _, v in ipairs(ip.routes({ family = 4, type = 1 })) do %> +
+
<%=luci.tools.webadmin.iface_get_network(v.dev) or v.dev%>
+
<%=v.dest%>
+
<%=v.gw or "-"%>
+
<%=v.metric or 0%>
+
<%=rtn[v.table] or v.table%>
+
+ <% style = not style end %> +
+
+
+ + <% + if nixio.fs.access("/proc/net/ipv6_route") then + style = true + %> +
+ <%_Active IPv6-Routes%> +
+
+
+
<%:Network%>
+
<%:Target%>
+
<%:Source%>
+
<%:Metric%>
+
<%:Table%>
+
+ <% + for _, v in ipairs(ip.routes({ family = 6, type = 1 })) do + if v.dest and not v.dest:is6linklocal() then + %> +
+
<%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%>
+
<%=v.dest%>
+
<%=v.from%>
+
<%=v.metric or 0%>
+
<%=rtn[v.table] or v.table%>
+
+ <% + style = not style + end + end + %> +
+
+
+ +
+ <%:IPv6 Neighbours%> +
+
+
+
<%:IPv6-Address%>
+
<%:MAC-Address%>
+
<%:Interface%>
+
+ <% + for _, v in ipairs(ip.neighbors({ family = 6 })) do + if v.dest and not v.dest:is6linklocal() and v.mac then + %> +
+
<%=v.dest%>
+
<%=v.mac%>
+
<%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%>
+
+ <% + style = not style + end + end + %> +
+
+
+ <% end %> +
+ +<%+footer%> diff --git a/luci-mod-status/luasrc/view/admin_status/syslog.htm b/luci-mod-status/luasrc/view/admin_status/syslog.htm new file mode 100644 index 000000000..fb734a76d --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/syslog.htm @@ -0,0 +1,12 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<%+header%> +

<%:System Log%>

+
+ +
+<%+footer%> diff --git a/luci-mod-status/luasrc/view/admin_status/wireless.htm b/luci-mod-status/luasrc/view/admin_status/wireless.htm new file mode 100644 index 000000000..5ac2eb462 --- /dev/null +++ b/luci-mod-status/luasrc/view/admin_status/wireless.htm @@ -0,0 +1,370 @@ +<%# + Copyright 2011-2018 Jo-Philipp Wich + 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%> + + + +

<%:Realtime Wireless%>

+ + + + +
-
+
+ +
+
+
<%:Signal:%>
+
0 <%:dBm%>
+ +
<%:Average:%>
+
0 <%:dBm%>
+ +
<%:Peak:%>
+
0 <%:dBm%>
+
+
+
<%:Noise:%>
+
0 <%:dBm%>
+ +
<%:Average:%>
+
0 <%:dBm%>
+ +
<%:Peak:%>
+
0 <%:dBm%>
+
+
+ +
+ + +
-
+
+ +
+
+
<%:Phy Rate:%>
+
0 MBit/s
+ +
<%:Average:%>
+
0 MBit/s
+ +
<%:Peak:%>
+
0 MBit/s
+
+
+ +<%+footer%> diff --git a/luci-mod-status/src/Makefile b/luci-mod-status/src/Makefile new file mode 100644 index 000000000..d6ed8c6e4 --- /dev/null +++ b/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/luci-mod-status/src/luci-bwc.c b/luci-mod-status/src/luci-bwc.c new file mode 100644 index 000000000..8ddd91727 --- /dev/null +++ b/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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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; +}