diff --git a/CONTRIBUTED.md b/CONTRIBUTED.md index e9dc534fc9..5bdd7df5df 100644 --- a/CONTRIBUTED.md +++ b/CONTRIBUTED.md @@ -45,9 +45,7 @@ luci-app-oled source: [NateLol/luci-app-oled](https://github.com/NateLol/luci-ap luci-app-beardropper source: [NateLol/natelol](https://github.com/NateLol/natelol).
luci-app-vssr source: [jerrykuku/luci-app-vssr](https://github.com/jerrykuku/luci-app-vssr).
luci-theme-edge source: [garypang13/luci-theme-edge](https://github.com/garypang13/luci-theme-edge).
-luci-proto-minieap source: [ysc3839/luci-proto-minieap](https://github.com/ysc3839/luci-proto-minieap).
-wrtbwmon source: [brvphoenix/wrtbwmon](https://github.com/brvphoenix/wrtbwmon).
-luci-app-wrtbwmon source: [brvphoenix/luci-app-wrtbwmon](https://github.com/brvphoenix/luci-app-wrtbwmon). +luci-proto-minieap source: [ysc3839/luci-proto-minieap](https://github.com/ysc3839/luci-proto-minieap). ## License ### Depend on their own License. diff --git a/package/ctcgfw/luci-app-wrtbwmon/Makefile b/package/ctcgfw/luci-app-wrtbwmon/Makefile deleted file mode 100644 index a20263b8cb..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# -# Copyright (C) 2021 Van Waholtz -# -# This is free software, licensed under the Apache License, Version 2.0. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=luci-app-wrtbwmon -PKG_VERSION:=2.0.8 -PKG_RELEASE:=1 - -PKG_LICENSE:=Apache-2.0 -PKG_MAINTAINER:=Van Waholtz - -LUCI_TITLE:=A Luci module that uses wrtbwmon to track bandwidth usage -LUCI_DEPENDS:=+@BUSYBOX_CONFIG_IP +iptables -LUCI_PKGARCH:=all - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/package/ctcgfw/luci-app-wrtbwmon/etc/config/wrtbwmon b/package/ctcgfw/luci-app-wrtbwmon/etc/config/wrtbwmon deleted file mode 100644 index da9470a111..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/etc/config/wrtbwmon +++ /dev/null @@ -1,5 +0,0 @@ - -config wrtbwmon 'general' - option enabled '1' - option path '/tmp/usage.db' - diff --git a/package/ctcgfw/luci-app-wrtbwmon/etc/hotplug.d/iface/99-wrtbwmon b/package/ctcgfw/luci-app-wrtbwmon/etc/hotplug.d/iface/99-wrtbwmon deleted file mode 100755 index 41b49a4a20..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/etc/hotplug.d/iface/99-wrtbwmon +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -[ "$ACTION" = ifup -o "$ACTION" = ifupdate ] || exit 0 -[ "$ACTION" = ifupdate -a -z "$IFUPDATE_ADDRESSES" -a -z "$IFUPDATE_DATA" ] && exit 0 - -/etc/init.d/wrtbwmon restart - -logger -t wrtbwmon "Restart for $ACTION of $INTERFACE ($DEVICE)" - diff --git a/package/ctcgfw/luci-app-wrtbwmon/etc/init.d/wrtbwmon b/package/ctcgfw/luci-app-wrtbwmon/etc/init.d/wrtbwmon deleted file mode 100755 index 1c3213ee17..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/etc/init.d/wrtbwmon +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh /etc/rc.common -# Copyright (C) 2019 OpenWrt.org - -START=99 -USE_PROCD=1 - -NAME=wrtbwmon -PID_FILE=/var/run/wrtbwmon.pid -args=/usr/sbin/wrtbwmon - -create_instance() { - procd_open_instance - procd_set_param command $args - procd_set_param respawn - procd_set_param user root -# procd_set_param pidfile $PID_FILE - procd_close_instance -} - -service_triggers() -{ - procd_add_reload_trigger "$NAME" -} - -start_service() { - local db enabled - config_load $NAME - - config_get db general path - [ -z "$db" ] && db="/tmp/usage.db" - append args " -46" - append args "-f $db" - append args "-p /tmp/usage.htm" - append args "-u /etc/wrtbwmon.user" - append args "-d" - - config_get enabled general enabled - [ "$enabled"0 -eq 0 ] || create_instance -} - -stop_service() { - procd_kill wrtbwmon - kill -CONT $(cat $PID_FILE) -} diff --git a/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/config.js b/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/config.js deleted file mode 100644 index 693ab5630a..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/config.js +++ /dev/null @@ -1,95 +0,0 @@ -'use strict'; -'require form'; -'require fs'; -'require uci'; - -function renameFile(str, tag) { - var dir = dirName(str); - var n = str.lastIndexOf('/'), fn = n > -1 ? str.slice(n + 1) : str; - var n = fn.lastIndexOf('.'), bn = n > -1 ? fn.slice(0, n) : fn; - var n = fn.lastIndexOf('.'), en = n > -1 ? fn.slice(n + 1) : ''; - return dir + bn + '.' + tag + (en ? '.' + en : ''); -} - -function dirName(str) { - var n = str.lastIndexOf('/'); - return n > -1 ? str.slice(0, n + 1) : ''; -} - -return L.view.extend({ - lastPath: null, - - load: function() { - return uci.load('wrtbwmon').then(L.bind(function() { - this.lastPath = uci.get_first('wrtbwmon', 'wrtbwmon', 'path') || null; - }, this)); - }, - - render: function() { - var m, s, o; - - m = new form.Map('wrtbwmon', _('Usage - Configuration')); - - s = m.section(form.NamedSection, 'general', 'wrtbwmon', _('General settings')); - s.addremove = false; - - o = s.option(form.Flag, 'enabled', _('Keep running in the background')); - o.rmempty = true; - - o = s.option(form.Value, 'path', _('Database path'), _('This box is used to select the Database path, which is /tmp/usage.db by default.')); - o.value('/tmp/usage.db'); - o.value('/etc/usage.db'); - o.default = '/tmp/usage.db'; - o.rmempty = false; - - return m.render(); - }, - - changePath: function() { - return uci.changes().then(L.bind(function(res) { - if (res.wrtbwmon && this.lastPath) { - for (var i = 0; i < res.wrtbwmon.length; i++) { - if (res.wrtbwmon[i][2] == "path") { - var newPath = res.wrtbwmon[i][3]; - return fs.stat(dirName(newPath)).then(L.bind(function(res) { - if (res.type == 'directory') { - Promise.all([ - fs.exec('/bin/cp', ['-fp', this.lastPath, newPath]), - fs.exec('/bin/cp', ['-fp', renameFile(this.lastPath, '6'), renameFile(newPath, '6')]), - fs.exec('/bin/cp', ['-fp', renameFile(this.lastPath, '46'), renameFile(newPath, '46')]) - ]); - return true; - } - else { - var err = new Error('Can\'t move files to non-directory path.'); - err.name = 'NotDirectoryError'; - throw err; - } - }, this)).catch(function(err) { - throw err; - }) - } - } - } - return false; - }, this)); - }, - - handleSaveApply: function(ev, mode) { - return this.handleSave(ev).then(L.bind(this.changePath, this)).then(L.bind(function(data) { - L.resolveDefault(L.ui.changes.apply(mode == '0')).then(L.bind(function() { - if (data) { - Promise.all([ - fs.exec('/bin/rm', ['-f', this.lastPath]), - fs.exec('/bin/rm', ['-f', renameFile(this.lastPath, '6')]), - fs.exec('/bin/rm', ['-f', renameFile(this.lastPath, '46')]) - ]); - } - }, this)); - }, this)).catch(function(err) { - if (confirm(err + '\n\n' + _('This will revert the changes. Are you sure?'))) { - L.bind(L.ui.changes.revert, L.ui.changes)(); - } - }); - } -}); diff --git a/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/custom.js b/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/custom.js deleted file mode 100644 index 74d01cbdd5..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/custom.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; -'require fs'; -'require ui'; - -return L.view.extend({ - load: function() { - return fs.trimmed('/etc/wrtbwmon.user').catch(function(err) { - ui.addNotification(null, E('p', {}, _('Unable to load the customized hostname file: ' + err.message))); - return ''; - }); - }, - - render: function(data) { - return E('div', {'class': 'cbi-map'}, [ - E('h2', {'name': 'content'}, [ _('Usage - Custom User File') ]), - E('div', {'class': 'cbi-map-descr'}, [ - _('Each line must have the following format:'), - E('em', {}, E('font', {'color': 'red'}, '00:aa:bb:cc:ee:ff,hostname')) - ]), - E('div', {'class': 'cbi-section'}, [ - E('textarea', { - 'id': 'custom_hosts', - 'style': 'width: 100%;padding: .5em;', - 'rows': 20 - }, data) - ]) - ]); - }, - - handleSave: function(ev) { - var map = document.querySelector('#custom_hosts'); - return fs.write('/etc/wrtbwmon.user', map.value.trim().replace(/\r\n/g, '\n') + '\n'); - }, - - addFooter: function() { - return E('div', { 'class': 'cbi-page-actions' }, [ - E('button', { - 'class': 'cbi-button cbi-button-save', - 'click': L.ui.createHandlerFn(this, 'handleSave') - }, [ _('Save') ]) - ]); - } -}); diff --git a/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/details.js b/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/details.js deleted file mode 100644 index 796a51b856..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/details.js +++ /dev/null @@ -1,651 +0,0 @@ -'use strict'; -'require fs'; -'require rpc'; -'require uci'; -'require ui'; -'require validation'; - -var cachedData = []; -var luciConfig = '/etc/luci-wrtbwmon.conf'; -var hostNameFile = '/etc/wrtbwmon.user'; - -var callLuciDHCPLeases = rpc.declare({ - object: 'luci-rpc', - method: 'getDHCPLeases', - expect: { '': {} } -}); - -var callLuciDSLStatus = rpc.declare({ - object: 'luci-rpc', - method: 'getDSLStatus', - expect: { '': {} } -}); - -function $(tid) { - return document.getElementById(tid); -} - -function clickToResetDatabase(settings) { - if (confirm(_('This will delete the database file. Are you sure?'))) { - getPath().then(function(res) { - var db = settings.protocol == 'ipv4' ? res : renameFile(res, '6'); - fs.exec('/bin/rm', [db]).then(function() { - updateData($('traffic'), $('updated'), $('updating'), settings, true); - }); - }) - } -} - -function clickToSaveConfig(keylist, cstrs) { - var data = {}; - - for(var i = 0; i < keylist.length; i++) { - data[keylist[i]] = cstrs[keylist[i]].getValue(); - } - - ui.showModal(_('Configuration'), [ - E('p', { 'class': 'spinning' }, _('Saving configuration data...')) - ]); - - return fs.write(luciConfig, JSON.stringify(data, undefined, '\t') + '\n') - .catch(function(err) { - ui.addNotification(null, E('p', {}, [ _('Unable to save %s: %s').format(luciConfig, err) ])); - }) - .then(ui.hideModal) - .then(function() { document.location.reload(); }); -} - -function clickToSelectInterval(settings, ev) { - if (ev.target.value > 0) { - settings.interval = parseInt(ev.target.value); - if (!L.Request.poll.active()) L.Request.poll.start(); - } - else { - L.Request.poll.stop(); - setUpdateMessage($('updating'), -1); - } -} - -function clickToSelectProtocol(settings, ev) { - settings.protocol = ev.target.value; - updateData($('traffic'), $('updated'), $('updating'), settings, true); -} - -function clickToShowMore(settings, ev) { - var table = $('traffic'); - var t = table.querySelector('.tr.table-totals'); - settings.showMore = ev.target.checked - - if (t && t.firstElementChild) - t.firstElementChild.textContent = _('TOTAL') + (settings.showMore ? '' : ': ' + (table.childElementCount - 2)); - - table.querySelectorAll('.showMore').forEach(function(e) { - e.classList.toggle('hide'); - }); - - if (!settings.showMore && table.querySelector('.th.sorted').classList.contains('hide')) { - table.querySelector('[id="thTotal"]').click(); - } -} - -function createOption(args, val) { - var cstr = args[0], title = args[1], desc = args.slice(-1), widget, frame; - val = val != null ? val : args[2]; - widget = args.length == 5 ? new cstr(val, args[3]) : new cstr(val, args[3], args[4]); - - frame = E('div', {'class': 'cbi-value'}, [ - E('label', {'class': 'cbi-value-title'}, title), - E('div', {'class': 'cbi-value-field'}, E('div', {}, widget.render())) - ]); - - if (desc && desc != '') - L.dom.append(frame.lastChild, E('div', { 'class': 'cbi-value-description' }, desc)); - - return [widget, frame]; -} - -function displayTable(tb, settings) { - var elm, elmID, col, sortedBy, flag, IPVer; - var thID = ['thClient', 'thMAC', 'thDownload', 'thUpload', 'thTotalDown', 'thTotalUp', 'thTotal', 'thFirstSeen', 'thLastSeen', '']; - - elm = tb.querySelector('.th.sorted'); - elmID = elm ? elm.id : 'thTotal'; - sortedBy = elm && elm.classList.contains('ascent') ? 'asc' : 'desc'; - - col = thID.indexOf(elmID); - IPVer = col == 0 ? settings.protocol : null; - flag = sortedBy == 'desc' ? 1 : -1; - - cachedData[0].sort(sortTable.bind(this, col, IPVer, flag)); - - //console.time('show'); - updateTable(tb, cachedData, '%s'.format(_('Collecting data...')), settings); - //console.timeEnd('show'); - progressbar('downstream', cachedData[1][0], settings.downstream, settings.useBits, settings.useMultiple); - progressbar('upstream', cachedData[1][1], settings.upstream, settings.useBits, settings.useMultiple); -} - -function formatBandWidth(bdw, useBits) { - return bdw * Math.pow(1000, 2) / (useBits ? 1 : 8); -} - -function formatSize(size, useBits, useMultiple) { - var res = String.format('%%%s.2m%s'.format(useMultiple, (useBits ? 'bit' : 'B')), useBits ? size * 8 : size); - return useMultiple == '1024' ? res.replace(/([KMGTPEZ])/, '$&i') : res; -} - -function formatSpeed(speed, useBits, useMultiple) { - return formatSize(speed, useBits, useMultiple) + '/s'; -} - -function formatDate(d) { - var Y = d.getFullYear(), M = d.getMonth() + 1, D = d.getDate(); - var hh = d.getHours(), mm = d.getMinutes(), ss = d.getSeconds(); - return '%04d/%02d/%02d %02d:%02d:%02d'.format(Y, M, D, hh, mm, ss); -} - -function getDSLBandwidth() { - return callLuciDSLStatus().then(function(res) { - return { - upstream : res.max_data_rate_up || null, - downstream : res.max_data_rate_down || null - }; - }); -} - -function getPath() { - return uci.load('wrtbwmon').then(function() { - var res = uci.get_first('wrtbwmon', 'wrtbwmon', 'path') || '/tmp/usage.db'; - uci.unload('wrtbwmon'); - return res; - }); -} - -function handleConfig(ev) { - ui.showModal(_('Configuration'), [ - E('p', { 'class': 'spinning' }, _('Loading configuration data...')) - ]); - - parseDefaultSettings(luciConfig) - .then(function(settings) { - var arglist, keylist = Object.keys(settings), res, cstrs = {}, node = [], body; - - arglist = [ - [ui.Select, _('Default Protocol'), 'ipv4', {'ipv4': _('ipv4'), 'ipv6': _('ipv6')}, {}, ''], - [ui.Select, _('Default Refresh Interval'), '5', {'-1': _('Disabled'), '2': _('2 seconds'), '5': _('5 seconds'), '10': _('10 seconds'), '30': _('30 seconds')}, {sort: ['-1', '2', '5', '10', '30']}, ''], - [ui.Checkbox, _('Default More Columns'), false, {value_enabled: true, value_disabled: false}, ''], - [ui.Checkbox, _('Show Zeros'), true, {value_enabled: true, value_disabled: false}, ''], - [ui.Checkbox, _('Transfer Speed in Bits'), false, {value_enabled: true, value_disabled: false}, ''], - [ui.Select, _('Multiple of Unit'), '1000', {'1000': _('SI - 1000'), '1024': _('IEC - 1024')}, {}, ''], - [ui.Checkbox, _('Use DSL Bandwidth'), false, {value_enabled: true, value_disabled: false}, ''], - [ui.Textfield, _('Upstream Bandwidth'), '100', {datatype: 'ufloat'}, 'Mbps'], - [ui.Textfield, _('Downstream Bandwidth'), '100', {datatype: 'ufloat'}, 'Mbps'], - [ui.DynamicList, _('Hide MAC Addresses'), [], '', {datatype: 'macaddr'}, ''] - ]; // [constructor, lable, default_value(, all_values), options, description] - - for (var i = 0; i < keylist.length; i++) { - res = createOption(arglist[i], settings[keylist[i]]); - cstrs[keylist[i]] = res[0]; - node.push(res[1]); - } - - body = [ - E('p', {}, _('Configure the default values for luci-app-wrtbwmon.')), - E('div', {}, node), - E('div', { 'class': 'right' }, [ - E('div', { - 'class': 'btn cbi-button-neutral', - 'click': ui.hideModal - }, _('Cancel')), - ' ', - E('div', { - 'class': 'btn cbi-button-positive', - 'click': clickToSaveConfig.bind(this, keylist, cstrs), - 'disabled': (L.hasViewPermission ? !L.hasViewPermission() : null) || null - }, _('Save')) - ]) - ]; - ui.showModal(_('Configuration'), body); - }) -} - -function loadCss(path) { - var head = document.head || document.getElementsByTagName('head')[0]; - var link = E('link', { - 'rel': 'stylesheet', - 'href': path, - 'type': 'text/css' - }); - - head.appendChild(link); -} - -function parseDatabase(values, hosts, showZero, hideMACs) { - var valArr = [], totals = [0, 0, 0, 0, 0], valToRows, row; - - valToRows = values.replace(/(^\s*)|(\s*$)/g, '').split(/\r?\n|\r/g); - valToRows.shift(); - - for (var i = 0; i < valToRows.length; i++) { - row = valToRows[i].split(','); - if ((!showZero && row[7] == 0) || hideMACs.indexOf(row[0]) >= 0) continue; - - for (var j = 0; j < totals.length; j++) { - totals[j] += parseInt(row[3 + j]); - } - - row = Array.prototype.concat(row.slice(0, 2).reverse(), row.slice(3), row.slice(0, 1)); - if (row[1] in hosts && hosts[row[1]] != '-') { - row[9] = hosts[row[1]]; - } - valArr.push(row); - } - - return [valArr, totals]; -} - -function parseDefaultSettings(file) { - var keylist = ['protocol', 'interval', 'showMore', 'showZero', 'useBits', 'useMultiple', 'useDSL', 'upstream', 'downstream', 'hideMACs']; - var valuelist = ['ipv4', '5', false, true, false, '1000', false, '100', '100', []]; - - return fs.read(file).then(function(json) { - var settings; - try { - settings = JSON.parse(json); - } - catch(err) { - settings = {}; - } - - for (var i = 0; i < keylist.length; i++) { - if(!(keylist[i] in settings)) - settings[keylist[i]] = valuelist[i]; - } - - if (settings.useDSL) { - return getDSLBandwidth().then(function(dsl) { - settings.upstream = dsl.upstream || settings.upstream; - settings.downstream = dsl.downstream || settings.downstream; - return settings; - }); - } - else { - return settings; - } - }); -} - -function progressbar(query, v, m, useBits, useMultiple) { - var pg = $(query), - vn = v || 0, - mn = formatBandWidth(m, useBits) || 100, - fv = formatSpeed(v, useBits, useMultiple), - pc = '%.2f'.format((100 / mn) * vn), - wt = Math.floor(pc > 100 ? 100 : pc), - bgc = (pc >= 95 ? 'red' : (pc >= 80 ? 'darkorange' : (pc >= 60 ? 'yellow' : 'lime'))), - tc = (pc >= 80 ? 'white' : '#404040'); - if (pg) { - pg.firstElementChild.style.width = wt + '%'; - pg.firstElementChild.style.background = bgc; - pg.style.color = tc; - pg.setAttribute('title', '%s (%f%%)'.format(fv, pc)); - } -} - -function registerTableEventHandlers(settings, table) { - var indicators = $('xhr_poll_status') || $('indicators').getElementsByTagName('span')[0]; - indicators.addEventListener('click', function() { - $('selectInterval').value = L.Request.poll.active() ? settings.interval : -1; - }); - - table.querySelectorAll('.th').forEach(function(e) { - if (e) { - e.addEventListener('click', function (ev) { - setSortedColumn(ev.target); - displayTable(table, settings); - }); - } - }); -} - -function renameFile(str, tag) { - var n = str.lastIndexOf('/'), fn = n > -1 ? str.slice(n + 1) : str, dir = n > -1 ? str.slice(0, n + 1) : ''; - var n = fn.lastIndexOf('.'), bn = n > -1 ? fn.slice(0, n) : fn; - var n = fn.lastIndexOf('.'), en = n > -1 ? fn.slice(n + 1) : ''; - return dir + bn + '.' + tag + (en ? '.' + en : ''); -} - -function resolveCustomizedHostName() { - return fs.stat(hostNameFile).then(function() { - return fs.read(hostNameFile).then(function(rawStr) { - var hostNames = [], arr = rawStr.split(/\r?\n|\r/g), row; - for (var i = 0; i < arr.length; i++) { - row = arr[i].split(','); - if (row.length == 2 && row[0]) - hostNames.push({ macaddr: row[0], hostname: row[1] }); - } - return hostNames; - }) - }).catch(function() { return []; }); -} - -function resolveHostNameByMACAddr() { - return Promise.all([ - resolveCustomizedHostName(), - callLuciDHCPLeases() - ]).then(function(res) { - var leaseNames, macaddr, hostNames = {}; - leaseNames = [ - res[0], - Array.isArray(res[1].dhcp_leases) ? res[1].dhcp_leases : [], - Array.isArray(res[1].dhcp6_leases) ? res[1].dhcp6_leases : [] - ]; - for (var i = 0; i < leaseNames.length; i++) { - for (var j = 0; j < leaseNames[i].length; j++) { - if (leaseNames[i][j].macaddr) { - macaddr = leaseNames[i][j].macaddr.toLowerCase(); - if (!(macaddr in hostNames) || hostNames[macaddr] == '-') { - hostNames[macaddr] = leaseNames[i][j].hostname || '-'; - } - } - } - } - return hostNames; - }); -} - -function setSortedColumn(sorting) { - var sorted = document.querySelector('.th.sorted') || $('thTotal'); - - if (sorting.isSameNode(sorted)) { - sorting.classList.toggle('ascent'); - } - else { - sorting.classList.add('sorted'); - sorted.classList.remove('sorted', 'ascent'); - } -} - -function setUpdateMessage(e, sec) { - e.innerHTML = sec < 0 ? '' : ' ' + _('Updating again in %s second(s).').format('' + sec + ''); -} - -function sortTable(col, IPVer, flag, x, y) { - var byCol = x[col] == y[col] ? 1 : col; - var a = x[byCol], b = y[byCol]; - - if (!IPVer || byCol != 0) { - if (!(a.match(/\D/g) || b.match(/\D/g))) - a = parseInt(a), b = parseInt(b); - } - else { - IPVer == 'ipv4' - ? (a = validation.parseIPv4(a) || [0, 0, 0, 0], b = validation.parseIPv4(b) || [0, 0, 0, 0]) - : (a = validation.parseIPv6(a) || [0, 0, 0, 0, 0, 0, 0, 0], b = validation.parseIPv6(b) || [0, 0, 0, 0, 0, 0, 0, 0]); - } - - if(Array.isArray(a) && Array.isArray(b)) { - for(var i = 0; i < a.length; i++) { - if (a[i] != b[i]) { - return (b[i] - a[i]) * flag; - } - } - return 0; - } - - return a == b ? 0 : (a < b ? 1 : -1) * flag; -} - -function updateData(table, updated, updating, settings, once) { - if (!(L.Poll.tick % settings.interval) || once) { - getPath().then(function(res) { - var params = settings.protocol == 'ipv4' ? '-4' : '-6'; - fs.exec('/usr/sbin/wrtbwmon', [params, '-f', res]); - return params == '-4' ? res : renameFile(res, '6'); - }).then(function(data) { - Promise.all([ - fs.exec('/bin/cat', [ data ]), - resolveHostNameByMACAddr() - ]).then(function(res) { - //console.time('start'); - cachedData = parseDatabase(res[0].stdout || '', res[1], settings.showZero, settings.hideMACs); - displayTable(table, settings); - updated.innerHTML = _('Last updated at %s.').format(formatDate(new Date(document.lastModified))); - //console.timeEnd('start'); - }); - }); - } - updatePerSec(updating, settings.interval); -} - -function updatePerSec(e, interval) { - var tick = L.Poll.tick; - var sec = tick % interval ? interval - tick % interval : 0; - - setUpdateMessage(e, sec); - if(sec == 0) { - setTimeout(setUpdateMessage.bind(this, e, interval), 100); - } -} - -function updateTable(tb, values, placeholder, settings) { - var fragment = document.createDocumentFragment(), nodeLen = tb.childElementCount - 2; - var formData = values[0], tbTitle = tb.firstElementChild, newNode, childTD; - - // Update the table data. - for (var i = 0; i < formData.length; i++) { - if (i < nodeLen) { - newNode = tbTitle.nextElementSibling; - } - else { - if (nodeLen > 0) { - newNode = fragment.firstChild.cloneNode(true); - } - else { - newNode = document.createElement('div'); - childTD = document.createElement('div'); - for (var j = 0; j < tbTitle.children.length; j++) { - childTD.className = 'td' + ('178'.indexOf(j) != -1 ? ' showMore' + (settings.showMore ? '' : ' hide') : ''); - childTD.setAttribute('data-title', tbTitle.children[j].textContent); - newNode.appendChild(childTD.cloneNode(true)); - } - } - newNode.className = 'tr cbi-rowstyle-%d'.format(i % 2 ? 2 : 1); - } - - childTD = newNode.firstElementChild; - childTD.title = formData[i].slice(-1); - for (var j = 0; j < tbTitle.childElementCount; j++, childTD = childTD.nextElementSibling) { - switch (j) { - case 2: - case 3: - childTD.textContent = formatSpeed(formData[i][j], settings.useBits, settings.useMultiple); - break; - case 4: - case 5: - case 6: - childTD.textContent = formatSize(formData[i][j], settings.useBits, settings.useMultiple); - break; - case 7: - case 8: - childTD.textContent = formatDate(new Date(formData[i][j] * 1000)); - break; - default: - childTD.textContent = formData[i][j]; - } - } - fragment.appendChild(newNode); - } - - // Remove the table data which has been deleted from the database. - while (tb.childElementCount > 1) { - tb.removeChild(tbTitle.nextElementSibling); - } - - //Append the totals or placeholder row. - if(formData.length == 0) { - newNode = document.createElement('div'); - newNode.className = 'tr placeholder'; - childTD = document.createElement('div'); - childTD.className = 'td'; - childTD.innerHTML = placeholder; - newNode.appendChild(childTD); - } - else{ - newNode = fragment.firstChild.cloneNode(true); - newNode.className = 'tr table-totals'; - - newNode.children[0].textContent = _('TOTAL') + (settings.showMore ? '' : ': ' + formData.length); - newNode.children[1].textContent = formData.length + ' ' + _('Clients'); - - for (var j = 0; j < tbTitle.childElementCount; j++) { - switch(j) { - case 0: - case 1: - newNode.children[j].removeAttribute('title'); - newNode.children[j].style.fontWeight = 'bold'; - break; - case 2: - case 3: - newNode.children[j].textContent = formatSpeed(values[1][j - 2], settings.useBits, settings.useMultiple); - break; - case 4: - case 5: - case 6: - newNode.children[j].textContent = formatSize(values[1][j - 2], settings.useBits, settings.useMultiple); - break; - default: - newNode.children[j].textContent = ''; - newNode.children[j].removeAttribute('data-title'); - } - } - } - - fragment.appendChild(newNode); - tb.appendChild(fragment); -} - -return L.view.extend({ - load: function() { - return Promise.all([ - parseDefaultSettings(luciConfig), - loadCss(L.resource('view/wrtbwmon/wrtbwmon.css')) - ]); - }, - - render: function(data) { - var settings = data[0]; - var node = E('div', { 'class': 'cbi-map' }, [ - E('h2', {}, _('Usage - Details')), - E('div', { 'class': 'cbi-section' }, [ - E('div', { 'id': 'control_panel' }, [ - E('div', {}, [ - E('label', {}, _('Protocol:')), - E('select', { - 'id': 'selectProtocol', - 'change': clickToSelectProtocol.bind(this, settings) - }, [ - E('option', { 'value': 'ipv4' }, 'ipv4'), - E('option', { 'value': 'ipv6' }, 'ipv6') - ]) - ]), - E('div', {}, [ - E('label', { 'for': 'showMore' }, _('Show More Columns:')), - E('input', { - 'id': 'showMore', - 'type': 'checkbox', - 'click': clickToShowMore.bind(this, settings) - }), - ]), - E('div', {}, [ - E('button', { - 'class': 'btn cbi-button cbi-button-reset important', - 'id': 'resetDatabase', - 'click': clickToResetDatabase.bind(this, settings) - }, _('Reset Database')), - ' ', - E('button', { - 'class': 'btn cbi-button cbi-button-neutral', - 'click': handleConfig - }, _('Configure Options')) - ]) - ]), - E('div', {}, [ - E('div', {}, [ - E('div', { 'id': 'updated' }), - E('div', { 'id': 'updating' }) - ]), - E('div', {}, [ - E('label', { 'for': 'selectInterval' }, _('Auto update every:')), - E('select', { - 'id': 'selectInterval', - 'change': clickToSelectInterval.bind(this, settings) - }, [ - E('option', { 'value': '-1' }, _('Disabled')), - E('option', { 'value': '2' }, _('2 seconds')), - E('option', { 'value': '5' }, _('5 seconds')), - E('option', { 'value': '10' }, _('10 seconds')), - E('option', { 'value': '30' }, _('30 seconds')) - ]) - ]) - ]), - E('div', { 'id': 'progressbar_panel', 'class': 'table' }, [ - E('div', { 'class': 'tr' }, [ - E('div', { 'class': 'td' }, E('div', {}, _('Downstream:'))), - E('div', { 'class': 'td' }, E('div', { - 'id': 'downstream', - 'class': 'cbi-progressbar', - 'title': '-' - }, E('div') - )) - ]), - E('div', { 'class': 'tr' }, [ - E('div', { 'class': 'td' }, E('div', {}, _('Upstream:'))), - E('div', { 'class': 'td' }, E('div', { - 'id': 'upstream', - 'class': 'cbi-progressbar', - 'title': '-' - }, E('div') - )) - ]), - ]), - E('div', { 'class': 'table', 'id': 'traffic' }, [ - E('div', { 'class': 'tr table-titles' }, [ - E('div', { 'class': 'th', 'id': 'thClient' }, _('Clients')), - E('div', { 'class': 'th showMore hide', 'id': 'thMAC' }, _('MAC')), - E('div', { 'class': 'th', 'id': 'thDownload' }, _('Download')), - E('div', { 'class': 'th', 'id': 'thUpload' }, _('Upload')), - E('div', { 'class': 'th', 'id': 'thTotalDown' }, _('Total Down')), - E('div', { 'class': 'th', 'id': 'thTotalUp' }, _('Total Up')), - E('div', { 'class': 'th sorted', 'id': 'thTotal' }, _('Total')), - E('div', { 'class': 'th showMore hide', 'id': 'thFirstSeen' }, _('First Seen')), - E('div', { 'class': 'th showMore hide', 'id': 'thLastSeen' }, _('Last Seen')) - ]), - E('div', {'class': 'tr placeholder'}, [ - E('div', { 'class': 'td' }, E('em', {}, _('Collecting data...'))) - ]) - ]) - ]) - ]); - - return Promise.all([ - node.querySelector('[id="traffic"]'), - node.querySelector('[id="updated"]'), - node.querySelector('[id="updating"]'), - node.querySelector('[id="selectInterval"]').value = settings.interval, - node.querySelector('[id="selectProtocol"]').value = settings.protocol, - node.querySelector('[id="showMore"]').checked = settings.showMore, - node.querySelectorAll('.showMore').forEach(function(e) { settings.showMore ? e.classList.remove('hide') : e.classList.add('hide'); }) - ]) - .then(function(data) { - L.Poll.add(updateData.bind(this, data[0], data[1], data[2], settings, false), 1); - return data[0]; - }) - .then(registerTableEventHandlers.bind(this, settings)) - .then(function() { return node; }); - }, - - handleSaveApply: null, - handleSave: null, - handleReset: null -}); diff --git a/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/wrtbwmon.css b/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/wrtbwmon.css deleted file mode 100644 index c4793bc4c9..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/wrtbwmon.css +++ /dev/null @@ -1,73 +0,0 @@ -.th.sorted::after { - content: '\25bc'; -} -.th.sorted.ascent::after { - content: '\25b2'; -} -.hide { - display: none !important; -} -#control_panel { - display: flex; - margin-bottom: 1rem; - padding: .5rem; - line-height: 2rem; - background-color: #fff; -} -#control_panel > :nth-child(1), #control_panel > :nth-child(2) { - display: inline-block; - flex: 1 1 25%; -} -#control_panel > :nth-child(3) { - flex: 1 1 50%; - text-align: right; -} -#control_panel > * > * { - vertical-align: middle; -} -div > label { - margin-right: .5rem; -} -div > label + select { - min-width: unset; - width: auto; -} -#control_panel + div { - display: flex; - margin-bottom: .5rem; -} -#control_panel + div > div:nth-of-type(1) { - flex: 1 1 65%; -} -#control_panel + div > div:nth-of-type(2) { - flex: 1 1 35%; - text-align: right; -} -#updated, #updating { - display: inline; -} -#thClient { - width: 17%; -} -#thMAC { - width: 10%; -} -#thDownload, #thUpload { - width: 8%; -} -#thTotalDown, #thTotalUp, #thTotal { - width: 9%; -} -#thFirstSeen, #thLastSeen { - width: 15%; -} -#progressbar_panel > .tr > .td:nth-child(1) { - width: 10%; -} -.tr.table-totals { - background: #e0e0e0 !important; -} -#traffic .tr:not(.table-totals):not(.placeholder) > .td:not(.th):first-child::before { - content: attr(title)'\a'; - white-space: pre-line; -} diff --git a/package/ctcgfw/luci-app-wrtbwmon/po/templates/wrtbwmon.pot b/package/ctcgfw/luci-app-wrtbwmon/po/templates/wrtbwmon.pot deleted file mode 100644 index 184af4b18c..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/po/templates/wrtbwmon.pot +++ /dev/null @@ -1,260 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:585 -msgid "10 seconds" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:583 -msgid "2 seconds" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:586 -msgid "30 seconds" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:584 -msgid "5 seconds" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:577 -msgid "Auto update every:" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:199 -msgid "Cancel" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:497 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:612 -msgid "Clients" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:122 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:623 -msgid "Collecting data..." -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:46 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:165 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:208 -#: luasrc/controller/wrtbwmon.lua:16 -msgid "Configuration" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:568 -msgid "Configure Options" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:193 -msgid "Configure the default values for luci-app-wrtbwmon." -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:39 -msgid "Database path" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176 -msgid "Default More Columns" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:174 -msgid "Default Protocol" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -msgid "Default Refresh Interval" -msgstr "" - -#: luasrc/controller/wrtbwmon.lua:13 -msgid "Details" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:582 -msgid "Disabled" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:614 -msgid "Download" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:182 -msgid "Downstream Bandwidth" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:592 -msgid "Downstream:" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:17 -msgid "Each line must have the following format:" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:619 -msgid "First Seen" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:33 -msgid "General settings" -msgstr "" - -#: root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json:3 -msgid "Grant access to LuCI app wrtbwmon" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:183 -msgid "Hide MAC Addresses" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179 -msgid "IEC - 1024" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:36 -msgid "Keep running in the background" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:620 -msgid "Last Seen" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:411 -msgid "Last updated at %s." -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:166 -msgid "Loading configuration data..." -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:613 -msgid "MAC" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179 -msgid "Multiple of Unit" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:541 -msgid "Protocol:" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:563 -msgid "Reset Database" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179 -msgid "SI - 1000" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:40 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:205 -msgid "Save" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:47 -msgid "Saving configuration data..." -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:551 -msgid "Show More Columns:" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177 -msgid "Show Zeros" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:80 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:496 -msgid "TOTAL" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:39 -msgid "" -"This box is used to select the Database path, which is /tmp/usage.db by " -"default." -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:29 -msgid "This will delete the database file. Are you sure?" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:90 -msgid "This will revert the changes. Are you sure?" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:618 -msgid "Total" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:616 -msgid "Total Down" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:617 -msgid "Total Up" -msgstr "" - -#: luasrc/controller/wrtbwmon.lua:10 -msgid "Traffic Status" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:178 -msgid "Transfer Speed in Bits" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:8 -msgid "Unable to load the customized hostname file:" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:52 -msgid "Unable to save %s: %s" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:368 -msgid "Updating again in %s second(s)." -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:615 -msgid "Upload" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181 -msgid "Upstream Bandwidth" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:601 -msgid "Upstream:" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:31 -msgid "Usage - Configuration" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:15 -msgid "Usage - Custom User File" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:537 -msgid "Usage - Details" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:180 -msgid "Use DSL Bandwidth" -msgstr "" - -#: luasrc/controller/wrtbwmon.lua:19 -msgid "User file" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:174 -msgid "ipv4" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:174 -msgid "ipv6" -msgstr "" diff --git a/package/ctcgfw/luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po b/package/ctcgfw/luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po deleted file mode 100644 index eafcf530fa..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po +++ /dev/null @@ -1,260 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8\n" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:585 -msgid "10 seconds" -msgstr "10秒" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:583 -msgid "2 seconds" -msgstr "2秒" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:586 -msgid "30 seconds" -msgstr "30秒" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:584 -msgid "5 seconds" -msgstr "5秒" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:577 -msgid "Auto update every:" -msgstr "自动刷新:" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:199 -msgid "Cancel" -msgstr "取消" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:497 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:612 -msgid "Clients" -msgstr "客户端" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:122 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:623 -msgid "Collecting data..." -msgstr "收集数据中..." - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:46 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:165 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:208 -#: luasrc/controller/wrtbwmon.lua:16 -msgid "Configuration" -msgstr "配置" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:568 -msgid "Configure Options" -msgstr "配置选项" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:193 -msgid "Configure the default values for luci-app-wrtbwmon." -msgstr "配置luci-app-wrtbwmon的默认值。" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:39 -msgid "Database path" -msgstr "数据路径" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176 -msgid "Default More Columns" -msgstr "默认显示更多列" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:174 -msgid "Default Protocol" -msgstr "默认协议" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -msgid "Default Refresh Interval" -msgstr "默认刷新间隔" - -#: luasrc/controller/wrtbwmon.lua:13 -msgid "Details" -msgstr "流量信息" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:175 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:582 -msgid "Disabled" -msgstr "禁用" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:614 -msgid "Download" -msgstr "下载" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:182 -msgid "Downstream Bandwidth" -msgstr "下行带宽" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:592 -msgid "Downstream:" -msgstr "下行:" - -#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:17 -msgid "Each line must have the following format:" -msgstr "每行需要满足以下格式:" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:619 -msgid "First Seen" -msgstr "初次记录" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:33 -msgid "General settings" -msgstr "通用设置" - -#: root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json:3 -msgid "Grant access to LuCI app wrtbwmon" -msgstr "授予访问LuCI应用程序wrtbwmon的权限" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:183 -msgid "Hide MAC Addresses" -msgstr "隐藏MAC地址" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179 -msgid "IEC - 1024" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:36 -msgid "Keep running in the background" -msgstr "保持后台运行" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:620 -msgid "Last Seen" -msgstr "最后记录" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:411 -msgid "Last updated at %s." -msgstr "最后更新于%s。" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:166 -msgid "Loading configuration data..." -msgstr "载入配置数据..." - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:613 -msgid "MAC" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179 -msgid "Multiple of Unit" -msgstr "单位之间倍数" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:541 -msgid "Protocol:" -msgstr "协议:" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:563 -msgid "Reset Database" -msgstr "重置" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179 -msgid "SI - 1000" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:40 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:205 -msgid "Save" -msgstr "保存" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:47 -msgid "Saving configuration data..." -msgstr "保存配置数据..." - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:551 -msgid "Show More Columns:" -msgstr "显示更多列:" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177 -msgid "Show Zeros" -msgstr "显示0流量" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:80 -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:496 -msgid "TOTAL" -msgstr "总共" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:39 -msgid "" -"This box is used to select the Database path, which is /tmp/usage.db by " -"default." -msgstr "该选项用于选择数据存储路径,默认路径为/tmp/usage.db。" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:29 -msgid "This will delete the database file. Are you sure?" -msgstr "该操作将删除数据统计文件,确定执行该操作?" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:90 -msgid "This will revert the changes. Are you sure?" -msgstr "更改将会重置,确定吗?" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:618 -msgid "Total" -msgstr "总计" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:616 -msgid "Total Down" -msgstr "总下载" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:617 -msgid "Total Up" -msgstr "总上传" - -#: luasrc/controller/wrtbwmon.lua:10 -msgid "Traffic Status" -msgstr "流量监控" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:178 -msgid "Transfer Speed in Bits" -msgstr "以bits显示传输速度" - -#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:8 -msgid "Unable to load the customized hostname file:" -msgstr "不能载入自定义用户名文件:" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:52 -msgid "Unable to save %s: %s" -msgstr "不能保存%s: %s" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:368 -msgid "Updating again in %s second(s)." -msgstr "下次更新将于%s秒之后。" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:615 -msgid "Upload" -msgstr "上传" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181 -msgid "Upstream Bandwidth" -msgstr "上传带宽" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:601 -msgid "Upstream:" -msgstr "上行" - -#: htdocs/luci-static/resources/view/wrtbwmon/config.js:31 -msgid "Usage - Configuration" -msgstr "文件设置" - -#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:15 -msgid "Usage - Custom User File" -msgstr "用户文件配置" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:537 -msgid "Usage - Details" -msgstr "流量详情" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:180 -msgid "Use DSL Bandwidth" -msgstr "使用DSL带宽" - -#: luasrc/controller/wrtbwmon.lua:19 -msgid "User file" -msgstr "用户文件" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:174 -msgid "ipv4" -msgstr "" - -#: htdocs/luci-static/resources/view/wrtbwmon/details.js:174 -msgid "ipv6" -msgstr "" diff --git a/package/ctcgfw/luci-app-wrtbwmon/root/etc/luci-wrtbwmon.conf b/package/ctcgfw/luci-app-wrtbwmon/root/etc/luci-wrtbwmon.conf deleted file mode 100644 index 7b368df4fc..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/root/etc/luci-wrtbwmon.conf +++ /dev/null @@ -1,12 +0,0 @@ -{ - "protocol": "ipv4", - "interval": "5", - "showMore": false, - "showZero": true, - "useBits": false, - "useMultiple": "1000", - "useDSL": false, - "upstream": "100", - "downstream": "100", - "hideMACs": [] -} diff --git a/package/ctcgfw/luci-app-wrtbwmon/root/usr/share/luci/menu.d/luci-app-wrtbwmon.json b/package/ctcgfw/luci-app-wrtbwmon/root/usr/share/luci/menu.d/luci-app-wrtbwmon.json deleted file mode 100644 index 904ef60068..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/root/usr/share/luci/menu.d/luci-app-wrtbwmon.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "admin/network/usage": { - "title": "Traffic Status", - "order": 60, - "action": { - "type": "alias", - "path": "admin/network/usage/details" - }, - "depends": { - "acl": [ "luci-app-wrtbwmon" ], - "uci": { "wrtbwmon": true } - } - }, - - "admin/network/usage/details": { - "title": "Details", - "order": 10, - "action": { - "type": "view", - "path": "wrtbwmon/details" - } - }, - - "admin/network/usage/config": { - "title": "Configuration", - "order": 20, - "action": { - "type": "view", - "path": "wrtbwmon/config" - } - }, - - "admin/network/usage/custom": { - "title": "User file", - "order": 30, - "action": { - "type": "view", - "path": "wrtbwmon/custom" - } - } -} diff --git a/package/ctcgfw/luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json b/package/ctcgfw/luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json deleted file mode 100644 index 8ec2e66045..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "luci-app-wrtbwmon": { - "description": "Grant access to LuCI app wrtbwmon", - "read": { - "file": { - "/tmp/usage.db": [ "read" ], - "/etc/wrtbwmon.user": [ "read" ], - "/usr/sbin/wrtbwmon": [ "exec" ], - "/bin/cp": [ "exec" ], - "/etc/luci-wrtbwmon.conf": [ "read" ] - }, - "uci": [ "wrtbwmon", "luci-app-wrtbwmon" ] - }, - "write": { - "file": { - "/bin/cat": [ "exec" ], - "/bin/rm": [ "exec" ], - "/bin/cp": [ "exec" ], - "/etc/luci-wrtbwmon.conf": [ "write" ], - "/etc/wrtbwmon.user": [ "write" ] - }, - "uci": [ "wrtbwmon", "luci-app-wrtbwmon" ] - } - } -} diff --git a/package/ctcgfw/luci-app-wrtbwmon/usr/sbin/readDB.awk b/package/ctcgfw/luci-app-wrtbwmon/usr/sbin/readDB.awk deleted file mode 100755 index 2080fe5f79..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/usr/sbin/readDB.awk +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/awk - -function inInterfaces(host) { - return(interfaces ~ "(^| )" host "($| )") -} - -function newRule(arp_ip, ipt_cmd) { - # checking for existing rules shouldn't be necessary if newRule is - # always called after db is read, arp table is read, and existing - # iptables rules are read. - ipt_cmd=iptKey " -t mangle -j RETURN -s " arp_ip - system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD") - ipt_cmd=iptKey " -t mangle -j RETURN -d " arp_ip - system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD") -} - -function delRule(arp_ip, ipt_cmd) { - ipt_cmd=iptKey " -t mangle -D RRDIPT_FORWARD -j RETURN " - system(ipt_cmd "-s " arp_ip " 2>/dev/null") - system(ipt_cmd "-d " arp_ip " 2>/dev/null") -} - -function total(i) { - return(bw[i "/in"] + bw[i "/out"]) -} - -BEGIN { - if (ipv6) { - iptNF = 8 - iptKey = "ip6tables" - } else { - iptNF = 9 - iptKey = "iptables" - } -} - -/^#/ { # get DB filename - FS = "," - dbFile = FILENAME - next -} - -# data from database; first file -ARGIND==1 { #!@todo this doesn't help if the DB file is empty. - lb=$1 - - if (lb !~ "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$") next - - if (!(lb in mac)) { - mac[lb] = $1 - ip[lb] = $2 - inter[lb] = $3 - speed[lb "/in"] = 0 - speed[lb "/out"]= 0 - bw[lb "/in"] = $6 - bw[lb "/out"] = $7 - firstDate[lb] = $9 - lastDate[lb] = $10 - ignore[lb] = 1 - } else { - if ($9 < firstDate[lb]) - firstDate[lb] = $9 - if ($10 > lastDate[lb]) { - ip[lb] = $2 - inter[lb] = $3 - lastDate[lb] = $10 - } - bw[lb "/in"] += $6 - bw[lb "/out"] += $7 - ignore[lb] = 0 - } - next -} - -# not triggered on the first file -FNR==1 { - FS=" " - if(ARGIND == 2) next -} - -# arp: ip hw flags hw_addr mask device -ARGIND==2 { - #!@todo regex match IPs and MACs for sanity - if (ipv6) { - statFlag= ($4 != "FAILED" && $4 != "INCOMPLETE") - macAddr = $5 - hwIF = $3 - } else { - statFlag= ($3 != "0x0") - macAddr = $4 - hwIF = $6 - } - - lb=$1 - if (hwIF != wanIF && statFlag && macAddr ~ "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$") { - hosts[lb] = 1 - arp_mac[lb] = macAddr - arp_ip[lb] = $1 - arp_inter[lb] = hwIF - arp_bw[lb "/in"] = 0 - arp_bw[lb "/out"] = 0 - arp_firstDate[lb] = systime() - arp_lastDate[lb] = arp_firstDate[lb] - arp_ignore[lb] = 1 - } - next -} - -#!@todo could use mangle chain totals or tailing "unnact" rules to -# account for data for new hosts from their first presence on the -# network to rule creation. The "unnact" rules would have to be -# maintained at the end of the list, and new rules would be inserted -# at the top. -ARGIND==3 && NF==iptNF && $1!="pkts" { # iptables input - if (ipv6) { - lfn = 5 - tag = "::/0" - } else { - lfn = 6 - tag = "0.0.0.0/0" - } - - if ($(lfn) != "*") { - m = $(lfn) - n = m "/in" - } else if ($(++lfn) != "*") { - m = $(lfn) - n = m "/out" - } else if ($(++lfn) != tag) { - m = $(lfn) - n = m "/out" - } else { # $(++lfn) != tag - m = $(++lfn) - n = m "/in" - } - - if (mode == "diff" || mode == "noUpdate") print n, $2 - if (mode != "noUpdate") { - if (inInterfaces(m)) { # if label is an interface - if (!(m in arp_mac)) { - cmd = "cat /sys/class/net/" m "/address" - cmd | getline arp_mac[m] - close(cmd) - - if (length(arp_mac[m]) == 0) arp_mac[m] = "00:00:00:00:00:00" - - arp_ip[m] = "NA" - arp_inter[m] = m - arp_bw[m "/in"] = 0 - arp_bw[m "/out"] = 0 - arp_firstDate[m] = systime() - arp_lastDate[m] = arp_firstDate[m] - arp_ignore[lb] = 1 - } - } else { - if (!(m in arp_mac)) hosts[m] = 0 - else delete hosts[m] - } - - if ($2 > 0) { - arp_bw[n] = $2 - arp_lastDate[m] = systime() - arp_ignore[m] = 0 - } - } -} - -END { - if (mode == "noUpdate") exit - - for (i in arp_ip) { - lb = arp_mac[i] - if (!arp_ignore[i] || !(lb in mac)) { - ignore[lb] = 0 - - if (lb in mac) { - bw[lb "/in"] += arp_bw[i "/in"] - bw[lb "/out"] += arp_bw[i "/out"] - lastDate[lb] = arp_lastDate[i] - } else { - bw[lb "/in"] = arp_bw[i "/in"] - bw[lb "/out"] = arp_bw[i "/out"] - firstDate[lb] = arp_firstDate[i] - lastDate[lb] = arp_lastDate[i] - } - mac[lb] = arp_mac[i] - ip[lb] = arp_ip[i] - inter[lb] = arp_inter[i] - - if (interval != 0) { - speed[lb "/in"] = int(arp_bw[i "/in"] / interval) - speed[lb "/out"]= int(arp_bw[i "/out"] / interval) - } - } - } - - close(dbFile) - for (i in mac) { - if (!ignore[i]) { - print "#mac,ip,iface,speed_in,speed_out,in,out,total,first_date,last_date" > dbFile - OFS="," - for (i in mac) - print mac[i], ip[i], inter[i], speed[i "/in"], speed[i "/out"], bw[i "/in"], bw[i "/out"], total(i), firstDate[i], lastDate[i] > dbFile - close(dbFile) - break - } - } - - # for hosts without rules - for (i in hosts) - if (hosts[i]) newRule(i) - else delRule(i) -} diff --git a/package/ctcgfw/luci-app-wrtbwmon/usr/sbin/wrtbwmon b/package/ctcgfw/luci-app-wrtbwmon/usr/sbin/wrtbwmon deleted file mode 100755 index 91089a1eee..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/usr/sbin/wrtbwmon +++ /dev/null @@ -1,460 +0,0 @@ -#!/bin/sh -# - -# Default input parameters for wrtbwmon. -runMode=0 -Monitor46=4 - -# Some parameters for monitor process. -for46= -updatePID= -logFile=/var/log/wrtbwmon.log -lockFile=/var/lock/wrtbwmon.lock -pidFile=/var/run/wrtbwmon.pid -tmpDir=/var/tmp/wrtbwmon -interval4=0 -interval6=0 - -# Debug parameters for readDB.awk. -mode= -DEBUG= - -# Constant parameter for wrtbwmon. -binDir=/usr/sbin -dataDir=/usr/share/wrtbwmon - -networkFuncs=/lib/functions/network.sh -uci=`which uci 2>/dev/null` -nslookup=`which nslookup 2>/dev/null` -nvram=`which nvram 2>/dev/null` - -chains='INPUT OUTPUT FORWARD' -interfaces='eth0 tun0 br-lan' # in addition to detected WAN - -# DNS server for reverse lookups provided in "DNS". -# don't perform reverse DNS lookups by default -DO_RDNS=${DNS-} - -header="#mac,ip,iface,speed_in,speed_out,in,out,total,first_date,last_date" - -createDbIfMissing() { - [ ! -f "$DB" ] && echo $header > "$DB" - [ ! -f "$DB6" ] && echo $header > "$DB6" -} - -checkDbArg() { - [ -z "$DB" ] && echo "ERROR: Missing argument 2 (database file)" && exit 1 -} - -checkDB() { - [ ! -f "$DB" ] && echo "ERROR: $DB does not exist" && exit 1 - [ ! -w "$DB" ] && echo "ERROR: $DB is not writable" && exit 1 - [ ! -f "$DB6" ] && echo "ERROR: $DB6 does not exist" && exit 1 - [ ! -w "$DB6" ] && echo "ERROR: $DB6 is not writable" && exit 1 -} - -checkWAN() { - [ -z "$1" ] && echo "Warning: failed to detect WAN interface." -} - -lookup() { - local MAC=$1 - local IP=$2 - local userDB=$3 - local USERSFILE= - local USER= - for USERSFILE in $userDB /tmp/dhcp.leases /tmp/dnsmasq.conf /etc/dnsmasq.conf /etc/hosts; do - [ -e "$USERSFILE" ] || continue - - case $USERSFILE in - /tmp/dhcp.leases ) - USER=$(grep -i "$MAC" $USERSFILE | cut -f4 -s -d' ') - ;; - /etc/hosts ) - USER=$(grep "^$IP " $USERSFILE | cut -f2 -s -d' ') - ;; - * ) - USER=$(grep -i "$MAC" "$USERSFILE" | cut -f2 -s -d,) - ;; - esac - - [ "$USER" = "*" ] && USER= - [ -n "$USER" ] && break - - done - - if [ -n "$DO_RDNS" -a -z "$USER" -a "$IP" != "NA" -a -n "$nslookup" ]; then - USER=`$nslookup $IP $DNS | awk '!/server can/{if($4){print $4; exit}}' | sed -re 's/[.]$//'` - fi - - [ -z "$USER" ] && USER=${MAC} - echo $USER -} - -detectIF() { - local IF= - if [ -f "$networkFuncs" ]; then - IF=`. $networkFuncs; network_get_device netdev $1; echo $netdev` - [ -n "$IF" ] && echo $IF && return - fi - - if [ -n "$uci" -a -x "$uci" ]; then - IF=`$uci get network.${1}.ifname 2>/dev/null` - [ $? -eq 0 -a -n "$IF" ] && echo $IF && return - fi - - if [ -n "$nvram" -a -x "$nvram" ]; then - IF=`$nvram get ${1}_ifname 2>/dev/null` - [ $? -eq 0 -a -n "$IF" ] && echo $IF && return - fi -} - -detectLAN() { - [ -e /sys/class/net/br-lan ] && echo br-lan && return - local lan=$(detectIF lan) - [ -n "$lan" ] && echo $lan && return -} - -detectWAN() { - local wan=$(detectIF wan) - [ -n "$wan" ] && echo $wan && return - wan=$(ip route show 2>/dev/null | grep default | sed -re '/^default/ s/default.*dev +([^ ]+).*/\1/') - [ -n "$wan" ] && echo $wan && return - [ -f "$networkFuncs" ] && wan=$(. $networkFuncs; network_find_wan wan; echo $wan) - [ -n "$wan" ] && echo $wan && return -} - -lockFunc() { - #Realize the lock function by busybox lock or flock command. - # if !(lock -n $lockFile) >/dev/null 2>&1; then - # exit 1 - # fi - #The following lock method is realized by other's function. - - local attempts=0 - local flag=0 - - while [ "$flag" = 0 ]; do - local tempfile=$(mktemp $tmpDir/lock.XXXXXX) - ln $tempfile $lockFile >/dev/null 2>&1 && flag=1 - rm $tempfile - - if [ "$flag" = 1 ]; then - [ -n "$DEBUG" ] && echo ${updatePID} "got lock after $attempts attempts" - flag=1 - else - sleep 1 - attempts=$(($attempts+1)) - [ -n "$DEBUG" ] && echo ${updatePID} "The $attempts attempts." - [ "$attempts" -ge 10 ] && exit - fi - done -} - -unlockFunc() { - #Realize the lock function by busybox lock or flock command. - # lock -u $lockFile - # rm -f $lockFile - # [ -n "$DEBUG" ] && echo ${updatePID} "released lock" - #The following lock method is realized by other's function. - - rm -f $lockFile - [ -n "$DEBUG" ] && echo ${updatePID} "released lock" -} - -# chain -newChain() { - local chain=$1 - local ipt=$2 - # Create the RRDIPT_$chain chain (it doesn't matter if it already exists). - - $ipt -t mangle -N RRDIPT_$chain 2> /dev/null - - # Add the RRDIPT_$chain CHAIN to the $chain chain if not present - $ipt -t mangle -C $chain -j RRDIPT_$chain 2>/dev/null - if [ $? -ne 0 ]; then - [ -n "$DEBUG" ] && echo "DEBUG: $ipt chain misplaced, recreating it..." - $ipt -t mangle -I $chain -j RRDIPT_$chain - fi -} - -# chain tun -newRuleIF() { - local chain=$1 - local IF=$2 - local ipt=$3 - local cmd= - - if [ "$chain" = "OUTPUT" ]; then - cmd="$ipt -t mangle -o $IF -j RETURN" - elif [ "$chain" = "INPUT" ]; then - cmd="$ipt -t mangle -i $IF -j RETURN" - fi - [ -n "$cmd" ] && eval $cmd " -C RRDIPT_$chain 2>/dev/null" || eval $cmd " -A RRDIPT_$chain" -} - -publish() { - # sort DB - # busybox sort truncates numbers to 32 bits - grep -v '^#' $DB | awk -F, '{OFS=","; a=sprintf("%f",$6/1e6); $6=""; print a,$0}' | tr -s ',' | sort -rn | awk -F, '{OFS=",";$1=sprintf("%f",$1*1e6);print}' > $tmpDir/sorted_${updatePID}.tmp - - # create HTML page - local htmPage="$tmpDir/${pb_html##*/}" - rm -f $htmPage - cp $dataDir/usage.htm1 $htmPage - - while IFS=, read PEAKUSAGE_IN MAC IP IFACE SPEED_IN SPEED_OUT PEAKUSAGE_OUT TOTAL FIRSTSEEN LASTSEEN - do - echo " -new Array(\"$(lookup $MAC $IP $user_def)\",\"$MAC\",\"$IP\",$SPEED_IN,$SPEED_OUT, -$PEAKUSAGE_IN,$PEAKUSAGE_OUT,$TOTAL,\"$FIRSTSEEN\",\"$LASTSEEN\")," >> $htmPage - done < $tmpDir/sorted_${updatePID}.tmp - echo "0);" >> $htmPage - - sed "s/(date)/`date`/" < $dataDir/usage.htm2 >> $htmPage - mv $htmPage "$pb_html" -} - -updatePrepare() { - checkDbArg - createDbIfMissing - checkDB - [ -e $tmpDir ] || mkdir -p $tmpDir - - for46="$Monitor46" - local timeNow=$(cat /proc/uptime | awk '{print $1}') - - if [ -e "$logFile" ]; then - local timeLast4=$(awk -F'[: ]+' '/ipv4/{print $2}' "$logFile") - local timeLast6=$(awk -F'[: ]+' '/ipv6/{print $2}' "$logFile") - interval4=$(awk -v now=$timeNow -v last=$timeLast4 'BEGIN{print (now-last)}'); - interval6=$(awk -v now=$timeNow -v last=$timeLast6 'BEGIN{print (now-last)}'); - - for ii in 4 6; do - [[ -n "$(echo $for46 | grep ${ii})" ]] && { - if [[ "$(eval echo \$interval${ii})" \> "0.9" ]]; then - sed -i "s/^ipv${ii}: [0-9\.]\{1,\}/ipv${ii}: $timeNow/ig" "$logFile" - else - for46=`echo "$for46" | sed "s/${ii}//g"` - fi - } - done - else - echo -e "ipv4: $timeNow\nipv6: $timeNow" >"$logFile" - fi - return 0 -} - -update() { - updatePID=$( sh -c 'echo $PPID' ) - - lockFunc - - local wan=$(detectWAN) - checkWAN $wan - interfaces="$interfaces $wan" - - [ "$for46" = 4 ] && IPT='iptables' - [ "$for46" = 6 ] && IPT='ip6tables' - [ "$for46" = 46 ] && IPT='iptables ip6tables' - - for ii in $IPT ; do - if [ -z "$( ${ii}-save | grep RRDIPT )" ]; then - - for chain in $chains; do - newChain $chain $ii - done - - # track local data - for chain in INPUT OUTPUT; do - for interface in $interfaces; do - [ -n "$interface" ] && [ -e "/sys/class/net/$interface" ] && newRuleIF $chain $interface $ii - done - done - fi - # this will add rules for hosts in arp table - > $tmpDir/${ii}_${updatePID}.tmp - - for chain in $chains; do - $ii -nvxL RRDIPT_$chain -t mangle -Z >> $tmpDir/${ii}_${updatePID}.tmp - done - done - - [ -f $tmpDir/iptables_${updatePID}.tmp ] && ( - awk -v mode="$mode" -v interfaces="$interfaces" -v wanIF="$wan" -v interval=$interval4 \ - -v ipv6="0" -f $binDir/readDB.awk \ - $DB \ - /proc/net/arp \ - $tmpDir/iptables_${updatePID}.tmp - ) - - [ -f $tmpDir/ip6tables_${updatePID}.tmp ] && ( - echo "This file is geneated by 'ip -6 neigh'" > $tmpDir/ip6addr_${updatePID}.tmp - `ip -6 neigh >> $tmpDir/ip6addr_${updatePID}.tmp`; - - awk -v mode="$mode" -v interfaces="$interfaces" -v wanIF="$wan" -v interval=$interval6 \ - -v ipv6="1" -f $binDir/readDB.awk \ - "$DB6" \ - $tmpDir/ip6addr_${updatePID}.tmp \ - $tmpDir/ip6tables_${updatePID}.tmp - ) - - [ "$Monitor46" = 46 ] && ( - cp $DB $DB46 - cat $DB6 >> $DB46 - awk -f $binDir/readDB.awk "$DB46" - ) - - [ -n "$pb_html" ] && publish - - rm -f $tmpDir/*_${updatePID}.tmp - unlockFunc -} - -renamefile() { - local base=$(basename -- "$1") - local ext=$([ -z "${base/*.*/}" ] && echo ".${base##*.}" || echo '') - local base="${base%.*}" - echo "$(dirname $1)/${base}$2$ext" && return -} - -ending() { - iptables-save | grep -v RRDIPT | iptables-restore - ip6tables-save | grep -v RRDIPT | ip6tables-restore - - if checkPid $pidFile; then - local pid=$( cat $pidFile ) - rm -rf $lockFile $logFile $pidFile $tmpDir/* - kill -9 $pid >> /dev/null 2>&1 - fi - echo "exit!!" -} - -checkPid() { - [ -e "$1" ] && local pid=$(cat $1) || return 1 - [ -d "/proc/$pid" ] && { - [ -n "$( cat /proc/$pid/cmdline | grep wrtbwmon )" ] && return 0 - } - return 1 -} - -sleepProcess() { - sleep 1m - kill -CONT $1 >>/dev/null 2>&1 -} - -loop() { - trap 'ending' INT TERM HUP QUIT - if checkPid $pidFile; then - echo "Another wrtbwmon is on running!!!" - else - local loopPID=$( sh -c 'echo $PPID' ) - local SPID= - echo $loopPID > $pidFile - while true ;do - [ -n "$SPID" ] && kill -9 $SPID >>/dev/null 2>&1 - sleepProcess $loopPID & - SPID=$! - updatePrepare && update - kill -STOP $loopPID >>/dev/null 2>&1 - done - fi - trap INT TERM HUP QUIT -} - -tips() { - echo \ -"Usage: $0 [options...] -Options: - -k Exit the wrtbwmon! - -f dbfile Set the DB file path - -u usrfile Set the user_def file path - -p htmlfile Set the publish htm file path - -d Enter the foreground mode. - -D Enter the daemo mode. - -4 Listen to ipv4 only. - -6 Listen to ipv6 only. - -46 Listen to ipv4 and ipv6. - -Note: [user_file] is an optional file to match users with MAC addresses. - Its format is \"00:MA:CA:DD:RE:SS,username\", with one entry per line." -} - -############################################################ - -while [ $# != 0 ];do - case $1 in - "-k" ) - /etc/init.d/wrtbwmon stop - exit 0 - ;; - "-f" ) - shift - if [ $# -gt 0 ];then - DB=$1 - DB6="$(renamefile $DB .6)" - DB46="$(renamefile $DB .46)" - else - echo "No db file path seted, exit!!" - exit 1 - fi - ;; - "-u") - shift - if [ $# -gt 0 ];then - user_def=$1 - else - echo "No user define file path seted, exit!!" - exit 1 - fi - ;; - - "-p") - shift - if [ $# -gt 0 ];then - pb_html=$1 - else - echo "No publish html file path seted, exit!!" - exit 1 - fi - ;; - - "-d") - runMode=1 - ;; - - "-D") - runMode=2 - ;; - - "-4") - Monitor46=4 - ;; - - "-6") - Monitor46=6 - ;; - - "-46") - Monitor46=46 - ;; - - "&&" | "||" | ";") - break - ;; - - "*") - tips - ;; - esac - - shift -done - -if [ "$runMode" = '1' ]; then - loop -elif [ "$runMode" = '2' ]; then - loop >>/dev/null 2>&1 & -else - updatePrepare && update -fi diff --git a/package/ctcgfw/luci-app-wrtbwmon/usr/share/wrtbwmon/usage.htm1 b/package/ctcgfw/luci-app-wrtbwmon/usr/share/wrtbwmon/usage.htm1 deleted file mode 100644 index c469cb7d91..0000000000 --- a/package/ctcgfw/luci-app-wrtbwmon/usr/share/wrtbwmon/usage.htm1 +++ /dev/null @@ -1,34 +0,0 @@ -Traffic - -

Total Usage:

- - - - - - - - - - - -
UserDown SpeedUp SpeedDownloadUploadTotalFirst seenLast seen
-
This page was generated on (date) - diff --git a/package/lean/luci-app-wrtbwmon/Makefile b/package/lean/luci-app-wrtbwmon/Makefile new file mode 100644 index 0000000000..412b35fe52 --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/Makefile @@ -0,0 +1,17 @@ +# Copyright (C) 2016 Openwrt.org +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI support for Wrtbwmon +LUCI_DEPENDS:=+luci-app-nlbwmon +LUCI_PKGARCH:=all +PKG_VERSION:=1.0 +PKG_RELEASE:=7 + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature + diff --git a/package/lean/luci-app-wrtbwmon/htdocs/luci-static/wrtbwmon.js b/package/lean/luci-app-wrtbwmon/htdocs/luci-static/wrtbwmon.js new file mode 100644 index 0000000000..36812e723b --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/htdocs/luci-static/wrtbwmon.js @@ -0,0 +1,562 @@ +var wrt = { + // variables for auto-update, interval is in seconds + scheduleTimeout: undefined, + updateTimeout: undefined, + isScheduled: true, + interval: 5, + // option on whether to show per host sub-totals + perHostTotals: false, + // variables for sorting + sortData: { + column: 7, + elId: 'thTotal', + dir: 'desc', + cache: {} + } +}; + +(function () { + var oldDate, oldValues = []; + + // find base path + var re = /(.*?admin\/nlbw\/[^/]+)/; + var basePath = window.location.pathname.match(re)[1]; + + //---------------------- + // HELPER FUNCTIONS + //---------------------- + + /** + * Human readable text for size + * @param size + * @returns {string} + */ + function getSize(size) { + var prefix = [' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']; + var precision, base = 1000, pos = 0; + while (size > base) { + size /= base; + pos++; + } + if (pos > 2) precision = 1000; else precision = 1; + return (Math.round(size * precision) / precision) + ' ' + prefix[pos] + 'B'; + } + + /** + * Human readable text for date + * @param date + * @returns {string} + */ + function dateToString(date) { + return date.toString().substring(0, 24); + } + + /** + * Gets the string representation of the date received from BE + * @param value + * @returns {*} + */ + function getDateString(value) { + var tmp = value.split('_'), + str = tmp[0].split('-').reverse().join('-') + 'T' + tmp[1]; + return dateToString(new Date(str)); + } + + /** + * Create a `tr` element with content + * @param content + * @returns {string} + */ + function createTR(content) { + var res = '' + data[2], {title: data[1]}), + createTD(getSize(dlSpeed) + '/s', {right: true}), + createTD(getSize(upSpeed) + '/s', {right: true}), + createTD(getSize(data[3]), {right: true}), + createTD(getSize(data[4]), {right: true}), + createTD(getSize(data[5]), {right: true}), + createTD(getDateString(data[6])), + createTD(getDateString(data[7])) + ]; + + // display row data + var result = ''; + for (var k = 0; k < displayData.length; k++) { + result += displayData[k]; + } + result = createTR(result); + return [result, rowData]; + } + + /** + * Creates the HTML output based on the `data` and `totals` inputs + * @param data + * @param totals + * @returns {string} HTML output + */ + function getDisplayData(data, totals) { + var result = + createTH('客户端', {id: 'thClient'}) + + createTH('下载带宽', {id: 'thDownload'}) + + createTH('上传带宽', {id: 'thUpload'}) + + createTH('总下载流量', {id: 'thTotalDown'}) + + createTH('总上传流量', {id: 'thTotalUp'}) + + createTH('流量合计', {id: 'thTotal'}) + + createTH('首次上线时间', {id: 'thFirstSeen'}) + + createTH('最后上线时间', {id: 'thLastSeen'}); + result = createTR(result); + for (var k = 0; k < data.length; k++) { + result += data[k][0]; + } + var totalsRow = createTH('总计'); + for (var m = 0; m < totals.length; m++) { + var t = totals[m]; + totalsRow += createTD(getSize(t) + (m < 2 ? '/s' : ''), {right: true}); + } + result += createTR(totalsRow); + return result; + } + + /** + * Calculates per host sub-totals and adds them in the data input + * @param data The data input + */ + function aggregateHostTotals(data) { + if (!wrt.perHostTotals) return; + + var curHost = 0, insertAt = 1; + while (curHost < data.length && insertAt < data.length) { + // grab the current hostname/mac, and walk the data looking for rows with the same host/mac + var hostName = data[curHost][1][0].toLowerCase(); + for (var k = curHost + 1; k < data.length; k++) { + if (data[k][1][0].toLowerCase() === hostName) { + // this is another row for the same host, group it with any other rows for this host + data.splice(insertAt, 0, data.splice(k, 1)[0]); + insertAt++; + } + } + + // if we found more than one row for the host, add a subtotal row + if (insertAt > curHost + 1) { + var hostTotals = [data[curHost][1][0], '', '', 0, 0, 0, 0, 0]; + for (var i = curHost; i < insertAt && i < data.length; i++) { + for (var j = 3; j < hostTotals.length; j++) { + hostTotals[j] += data[i][1][j]; + } + } + var hostTotalRow = createTH(data[curHost][1][0] + '
(host total)', {title: data[curHost][1][1]}); + for (var m = 3; m < hostTotals.length; m++) { + var t = hostTotals[m]; + hostTotalRow += createTD(getSize(t) + (m < 5 ? '/s' : ''), {right: true}); + } + hostTotalRow = createTR(hostTotalRow); + data.splice(insertAt, 0, [hostTotalRow, hostTotals]); + } + curHost = insertAt; + insertAt = curHost + 1; + } + } + + /** + * Sorting function used to sort the `data`. Uses the global sort settings + * @param x first item to compare + * @param y second item to compare + * @returns {number} 1 for desc, -1 for asc, 0 for equal + */ + function sortingFunction(x, y) { + // get data from global variable + var sortColumn = wrt.sortData.column, sortDirection = wrt.sortData.dir; + var a = x[1][sortColumn]; + var b = y[1][sortColumn]; + if (a === b) { + return 0; + } else if (sortDirection === 'desc') { + return a < b ? 1 : -1; + } else { + return a > b ? 1 : -1; + } + } + + /** + * Sets the relevant global sort variables and re-renders the table to apply the new sorting + * @param elId + * @param column + */ + function setSortColumn(elId, column) { + if (column === wrt.sortData.column) { + // same column clicked, switch direction + wrt.sortData.dir = wrt.sortData.dir === 'desc' ? 'asc' : 'desc'; + } else { + // change sort column + wrt.sortData.column = column; + // reset sort direction + wrt.sortData.dir = 'desc'; + } + wrt.sortData.elId = elId; + + // render table data from cache + renderTableData(wrt.sortData.cache.data, wrt.sortData.cache.totals); + } + + /** + * Registers the table events handlers for sorting when clicking the column headers + */ + function registerTableEventHandlers() { + // note these ordinals are into the data array, not the table output + document.getElementById('thClient').addEventListener('click', function () { + setSortColumn(this.id, 0); // hostname + }); + document.getElementById('thDownload').addEventListener('click', function () { + setSortColumn(this.id, 3); // dl speed + }); + document.getElementById('thUpload').addEventListener('click', function () { + setSortColumn(this.id, 4); // ul speed + }); + document.getElementById('thTotalDown').addEventListener('click', function () { + setSortColumn(this.id, 5); // total down + }); + document.getElementById('thTotalUp').addEventListener('click', function () { + setSortColumn(this.id, 6); // total up + }); + document.getElementById('thTotal').addEventListener('click', function () { + setSortColumn(this.id, 7); // total + }); + } + + /** + * Fetches and handles the updated `values` from the BE + * @param once If set to true, it re-schedules itself for execution based on selected interval + */ + function receiveData(once) { + var ajax = new XMLHttpRequest(); + ajax.onreadystatechange = function () { + // noinspection EqualityComparisonWithCoercionJS + if (this.readyState == 4 && this.status == 200) { + var re = /(var values = new Array[^;]*;)/, + match = ajax.responseText.match(re); + if (!match) { + handleError(); + } else { + // evaluate values + eval(match[1]); + //noinspection JSUnresolvedVariable + var v = values; + if (!v) { + handleError(); + } else { + handleValues(v); + // set old values + oldValues = v; + // set old date + oldDate = new Date(); + document.getElementById('updated').innerHTML = '数据更新时间 ' + dateToString(oldDate); + } + } + var int = wrt.interval; + if (!once && int > 0) reschedule(int); + } + }; + ajax.open('GET', basePath + '/usage_data', true); + ajax.send(); + } + + /** + * Registers DOM event listeners for user interaction + */ + function addEventListeners() { + document.getElementById('intervalSelect').addEventListener('change', function () { + var int = wrt.interval = this.value; + if (int > 0) { + // it is not scheduled, schedule it + if (!wrt.isScheduled) { + reschedule(int); + } + } else { + // stop the scheduling + stopSchedule(); + } + }); + + document.getElementById('resetDatabase').addEventListener('click', function () { + if (confirm('This will delete the database file. Are you sure?')) { + var ajax = new XMLHttpRequest(); + ajax.onreadystatechange = function () { + // noinspection EqualityComparisonWithCoercionJS + if (this.readyState == 4 && this.status == 204) { + location.reload(); + } + }; + ajax.open('GET', basePath + '/usage_reset', true); + ajax.send(); + } + }); + + document.getElementById('perHostTotals').addEventListener('change', function () { + wrt.perHostTotals = !wrt.perHostTotals; + }); + } + + //---------------------- + // AUTO-UPDATE + //---------------------- + + /** + * Stop auto-update schedule + */ + function stopSchedule() { + window.clearTimeout(wrt.scheduleTimeout); + window.clearTimeout(wrt.updateTimeout); + setUpdateMessage(''); + wrt.isScheduled = false; + } + + /** + * Start auto-update schedule + * @param seconds + */ + function reschedule(seconds) { + wrt.isScheduled = true; + seconds = seconds || 60; + updateSeconds(seconds); + wrt.scheduleTimeout = window.setTimeout(receiveData, seconds * 1000); + } + + /** + * Sets the text of the `#updating` element + * @param msg + */ + function setUpdateMessage(msg) { + document.getElementById('updating').innerHTML = msg; + } + + /** + * Updates the 'Updating in X seconds' message + * @param start + */ + function updateSeconds(start) { + setUpdateMessage('倒数 ' + start + ' 秒后刷新.'); + if (start > 0) { + wrt.updateTimeout = window.setTimeout(function () { + updateSeconds(start - 1); + }, 1000); + } + } + + //---------------------- + // END AUTO-UPDATE + //---------------------- + + /** + * Check for dependency, and if all is well, run callback + * @param cb Callback function + */ + function checkForDependency(cb) { + var ajax = new XMLHttpRequest(); + ajax.onreadystatechange = function () { + // noinspection EqualityComparisonWithCoercionJS + if (this.readyState == 4 && this.status == 200) { + // noinspection EqualityComparisonWithCoercionJS + if (ajax.responseText == "1") { + cb(); + } else { + alert("wrtbwmon is not installed!"); + } + } + }; + ajax.open('GET', basePath + '/check_dependency', true); + ajax.send(); + } + + checkForDependency(function () { + // register events + addEventListeners(); + // Main entry point + receiveData(); + }); + +})(); diff --git a/package/lean/luci-app-wrtbwmon/luasrc/controller/wrtbwmon.lua b/package/lean/luci-app-wrtbwmon/luasrc/controller/wrtbwmon.lua new file mode 100644 index 0000000000..25909c846d --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/luasrc/controller/wrtbwmon.lua @@ -0,0 +1,43 @@ +module("luci.controller.wrtbwmon", package.seeall) + +function index() + entry({"admin", "nlbw", "usage"}, alias("admin", "nlbw", "usage", "details"), _("Usage"), 60) + entry({"admin", "nlbw", "usage", "details"}, template("wrtbwmon"), _("Details"), 10).leaf=true + entry({"admin", "nlbw", "usage", "config"}, cbi("wrtbwmon/config"), _("Configuration"), 20).leaf=true + entry({"admin", "nlbw", "usage", "custom"}, form("wrtbwmon/custom"), _("User file"), 30).leaf=true + entry({"admin", "nlbw", "usage", "check_dependency"}, call("check_dependency")).dependent=true + entry({"admin", "nlbw", "usage", "usage_data"}, call("usage_data")).dependent=true + entry({"admin", "nlbw", "usage", "usage_reset"}, call("usage_reset")).dependent=true +end + +function usage_database_path() + local cursor = luci.model.uci.cursor() + if cursor:get("wrtbwmon", "general", "persist") == "1" then + return "/etc/config/usage.db" + else + return "/tmp/usage.db" + end +end + +function check_dependency() + local ret = "0" + if require("luci.model.ipkg").installed('iptables') then + ret = "1" + end + luci.http.prepare_content("text/plain") + luci.http.write(ret) +end + +function usage_data() + local db = usage_database_path() + local publish_cmd = "wrtbwmon publish " .. db .. " /tmp/usage.htm /etc/config/wrtbwmon.user" + local cmd = "wrtbwmon update " .. db .. " && " .. publish_cmd .. " && cat /tmp/usage.htm" + luci.http.prepare_content("text/html") + luci.http.write(luci.sys.exec(cmd)) +end + +function usage_reset() + local db = usage_database_path() + local ret = luci.sys.call("wrtbwmon update " .. db .. " && rm " .. db) + luci.http.status(204) +end diff --git a/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/config.lua b/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/config.lua new file mode 100644 index 0000000000..469d4f8f2c --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/config.lua @@ -0,0 +1,18 @@ +local m = Map("wrtbwmon", translate("Details")) + +local s = m:section(NamedSection, "general", "wrtbwmon", translate("General settings")) + +local o = s:option(Flag, "persist", translate("Persist database"), + translate("Check this to persist the database file")) +o.rmempty = false + +function o.write(self, section, value) + if value == '1' then + luci.sys.call("mv /tmp/usage.db /etc/config/usage.db") + elseif value == '0' then + luci.sys.call("mv /etc/config/usage.db /tmp/usage.db") + end + return Flag.write(self, section ,value) +end + +return m diff --git a/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/custom.lua b/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/custom.lua new file mode 100644 index 0000000000..671879b4cc --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/luasrc/model/cbi/wrtbwmon/custom.lua @@ -0,0 +1,23 @@ +local USER_FILE_PATH = "/etc/config/wrtbwmon.user" + +local fs = require "nixio.fs" + +local f = SimpleForm("wrtbwmon", + translate("Usage - Custom User File"), + translate("This file is used to match users with MAC addresses and it must have the following format: 00:aa:bb:cc:ee:ff,username")) + +local o = f:field(Value, "_custom") + +o.template = "cbi/tvalue" +o.rows = 20 + +function o.cfgvalue(self, section) + return fs.readfile(USER_FILE_PATH) +end + +function o.write(self, section, value) + value = value:gsub("\r\n?", "\n") + fs.writefile(USER_FILE_PATH, value) +end + +return f diff --git a/package/lean/luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm b/package/lean/luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm new file mode 100644 index 0000000000..68713b1238 --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/luasrc/view/wrtbwmon.htm @@ -0,0 +1,46 @@ +<%+header%> +

<%=translate("Usage")%>

+

+ +

+

+ + + +
+ + +
+

+ + +
<%=translate("Loading...")%>
+ + +<%+footer%> diff --git a/package/lean/luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po b/package/lean/luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po new file mode 100644 index 0000000000..3eb050e3f4 --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po @@ -0,0 +1,56 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Usage" +msgstr "实时流量监测" + +msgid "Details" +msgstr "详细信息" + +msgid "Configuration" +msgstr "配置" + +msgid "User file" +msgstr "自定义主机信息" + +msgid "Usage - Configuration" +msgstr "详细设置" + +msgid "General settings" +msgstr "通用设置" + +msgid "Persist database" +msgstr "写入数据库到硬盘" + +msgid "Check this to persist the database file" +msgstr "把统计数据写入 /etc/config 中避免重启或者升级后丢失" + +msgid "Usage - Custom User File" +msgstr "自定义MAC地址对应的主机名" + +msgid "This file is used to match users with MAC addresses and it must have the following format: 00:aa:bb:cc:ee:ff,username" +msgstr "每一行的格式为 00:aa:bb:cc:ee:ff,username" + +msgid "Reset Database" +msgstr "重置数据库" + +msgid "Auto Refresh Interval" +msgstr "自动刷新间隔" + +msgid "Disabled" +msgstr "禁用" + +msgid "Second" +msgstr "秒" + +msgid "Seconds" +msgstr "秒" + +msgid "Minutes" +msgstr "分钟" + +msgid "Per-host Totals" +msgstr "合并每一客户端数据" + +msgid "Loading..." +msgstr "加载中..." diff --git a/package/lean/luci-app-wrtbwmon/root/etc/config/wrtbwmon b/package/lean/luci-app-wrtbwmon/root/etc/config/wrtbwmon new file mode 100644 index 0000000000..419270dbab --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/root/etc/config/wrtbwmon @@ -0,0 +1,4 @@ + +config wrtbwmon 'general' + option persist '0' + diff --git a/package/lean/luci-app-wrtbwmon/root/etc/init.d/wrtbwmon b/package/lean/luci-app-wrtbwmon/root/etc/init.d/wrtbwmon new file mode 100755 index 0000000000..d57c09ae1d --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/root/etc/init.d/wrtbwmon @@ -0,0 +1,22 @@ +#!/bin/sh /etc/rc.common +# +# start/stop wrtbwmon bandwidth monitor + +### BEGIN INIT INFO +# Provides: wrtbwmon +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: iptables-based bandwidth monitor +### END INIT INFO + +START=91 + +start(){ + /usr/sbin/wrtbwmon setup /tmp/usage.db +} + +stop(){ + /usr/sbin/wrtbwmon remove +} diff --git a/package/lean/luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon b/package/lean/luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon new file mode 100755 index 0000000000..bcb5b337f3 --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/root/etc/uci-defaults/luci-wrtbwmon @@ -0,0 +1,14 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete firewall.wrtbwmon + set firewall.wrtbwmon=include + set firewall.wrtbwmon.type=script + set firewall.wrtbwmon.path='/etc/wrtbwmon.include' + set firewall.wrtbwmon.reload=1 + commit firewall +EOF + +/etc/init.d/wrtbwmon enable +/etc/init.d/wrtbwmon start +exit 0 diff --git a/package/lean/luci-app-wrtbwmon/root/etc/wrtbwmon.include b/package/lean/luci-app-wrtbwmon/root/etc/wrtbwmon.include new file mode 100755 index 0000000000..39bcf43a10 --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/root/etc/wrtbwmon.include @@ -0,0 +1 @@ +/etc/init.d/wrtbwmon restart >/dev/null 2>&1 diff --git a/package/lean/luci-app-wrtbwmon/root/usr/sbin/readDB.awk b/package/lean/luci-app-wrtbwmon/root/usr/sbin/readDB.awk new file mode 100755 index 0000000000..fe67e4ae8b --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/root/usr/sbin/readDB.awk @@ -0,0 +1,157 @@ +#!/usr/bin/awk + +function inInterfaces(host){ + return(interfaces ~ "(^| )"host"($| )") +} + +function newRule(arp_ip, + ipt_cmd){ + # checking for existing rules shouldn't be necessary if newRule is + # always called after db is read, arp table is read, and existing + # iptables rules are read. + ipt_cmd="iptables -t mangle -j RETURN -s " arp_ip + system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD") + ipt_cmd="iptables -t mangle -j RETURN -d " arp_ip + system(ipt_cmd " -C RRDIPT_FORWARD 2>/dev/null || " ipt_cmd " -A RRDIPT_FORWARD") +} + +function total(i){ + return(bw[i "/in"] + bw[i "/out"]) +} + +function date( cmd, d){ + cmd="date +%d-%m-%Y_%H:%M:%S" + cmd | getline d + close(cmd) + #!@todo could start a process with "while true; do date ...; done" + return(d) +} + +BEGIN { + od="" + fid=1 + debug=0 + rrd=0 +} + +/^#/ { # get DB filename + FS="," + dbFile=FILENAME + next +} + +# data from database; first file +FNR==NR { #!@todo this doesn't help if the DB file is empty. + if($2 == "NA") + #!@todo could get interface IP here + n=$1 + else + n=$2 + + hosts[n] = "" # add this host/interface to hosts + mac[n] = $1 + ip[n] = $2 + inter[n] = $3 + bw[n "/in"] = $4 + bw[n "/out"] = $5 + firstDate[n] = $7 + lastDate[n] = $8 + next +} + +# not triggered on the first file +FNR==1 { + FS=" " + fid++ #!@todo use fid for all files; may be problematic for empty files + next +} + +# arp: ip hw flags hw_addr mask device +fid==2 { + #!@todo regex match IPs and MACs for sanity + arp_ip = $1 + arp_flags = $3 + arp_mac = $4 + arp_dev = $6 + if(arp_flags != "0x0" && !(arp_ip in ip)){ + if(debug) + print "new host:", arp_ip, arp_flags > "/dev/stderr" + hosts[arp_ip] = "" + mac[arp_ip] = arp_mac + ip[arp_ip] = arp_ip + inter[arp_ip] = arp_dev + bw[arp_ip "/in"] = bw[arp_ip "/out"] = 0 + firstDate[arp_ip] = lastDate[arp_ip] = date() + } + next +} + +#!@todo could use mangle chain totals or tailing "unnact" rules to +# account for data for new hosts from their first presence on the +# network to rule creation. The "unnact" rules would have to be +# maintained at the end of the list, and new rules would be inserted +# at the top. + +# skip line +# read the chain name and deal with the data accordingly +fid==3 && $1 == "Chain"{ + rrd=$2 ~ /RRDIPT_.*/ + next +} + +fid==3 && rrd && (NF < 9 || $1=="pkts"){ next } + +fid==3 && rrd { # iptables input + if($6 != "*"){ + m=$6 + n=m "/out" + } else if($7 != "*"){ + m=$7 + n=m "/in" + } else if($8 != "0.0.0.0/0"){ + m=$8 + n=m "/out" + } else { # $9 != "0.0.0.0/0" + m=$9 + n=m "/in" + } + + # remove host from array; any hosts left in array at END get new + # iptables rules + + #!@todo this deletes a host if any rule exists; if only one + # directional rule is removed, this will not remedy the situation + delete hosts[m] + + if($2 > 0){ # counted some bytes + if(mode == "diff" || mode == "noUpdate") + print n, $2 + if(mode!="noUpdate"){ + if(inInterfaces(m)){ # if label is an interface + if(!(m in mac)){ # if label was not in db (also not in + # arp table, but interfaces won't be + # there anyway) + firstDate[m] = date() + mac[m] = inter[m] = m + ip[m] = "NA" + bw[m "/in"]=bw[m "/out"]= 0 + } + } + bw[n]+=$2 + lastDate[m] = date() + } + } +} + +END { + if(mode=="noUpdate") exit + close(dbFile) + system("rm -f " dbFile) + print "#mac,ip,iface,in,out,total,first_date,last_date" > dbFile + OFS="," + for(i in mac) + print mac[i], ip[i], inter[i], bw[i "/in"], bw[i "/out"], total(i), firstDate[i], lastDate[i] > dbFile + close(dbFile) + # for hosts without rules + for(host in hosts) if(!inInterfaces(host)) newRule(host) +} diff --git a/package/lean/luci-app-wrtbwmon/root/usr/sbin/wrtbwmon b/package/lean/luci-app-wrtbwmon/root/usr/sbin/wrtbwmon new file mode 100755 index 0000000000..b2c0b9a07a --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/root/usr/sbin/wrtbwmon @@ -0,0 +1,301 @@ +#!/bin/sh +# +# wrtbwmon: traffic logging tool for routers +# +# Peter Bailey (peter.eldridge.bailey+wrtbwmon AT gmail.com) +# +# Based on work by: +# Emmanuel Brucy (e.brucy AT qut.edu.au) +# Fredrik Erlandsson (erlis AT linux.nu) +# twist - http://wiki.openwrt.org/RrdTrafficWatch + +trap "rm -f /tmp/*_$$.tmp; kill $$" INT +binDir=/usr/sbin +dataDir=/usr/share/wrtbwmon +lockDir=/tmp/wrtbwmon.lock +pidFile=$lockDir/pid +networkFuncs=/lib/functions/network.sh +uci=`which uci 2>/dev/null` +nslookup=`which nslookup 2>/dev/null` +nvram=`which nvram 2>/dev/null` + +chains='INPUT OUTPUT FORWARD' +DEBUG= +interfaces='eth0 tun0' # in addition to detected WAN +DB=$2 +mode= + +# DNS server for reverse lookups provided in "DNS". +# don't perform reverse DNS lookups by default +DO_RDNS=${DNS-} + +header="#mac,ip,iface,in,out,total,first_date,last_date" + +createDbIfMissing() +{ + [ ! -f "$DB" ] && echo $header > "$DB" +} + +checkDbArg() +{ + [ -z "$DB" ] && echo "ERROR: Missing argument 2 (database file)" && exit 1 +} + +checkDB() +{ + [ ! -f "$DB" ] && echo "ERROR: $DB does not exist" && exit 1 + [ ! -w "$DB" ] && echo "ERROR: $DB is not writable" && exit 1 +} + +checkWAN() +{ + [ -z "$wan" ] && echo "Warning: failed to detect WAN interface." +} + +lookup() +{ + MAC=$1 + IP=$2 + userDB=$3 + for USERSFILE in $userDB /tmp/dhcp.leases /tmp/dnsmasq.conf /etc/dnsmasq.conf /etc/hosts; do + [ -e "$USERSFILE" ] || continue + case $USERSFILE in + /tmp/dhcp.leases ) + USER=$(grep -i "$MAC" $USERSFILE | cut -f4 -s -d' ') + ;; + /etc/hosts ) + USER=$(grep "^$IP " $USERSFILE | cut -f2 -s -d' ') + ;; + * ) + USER=$(grep -i "$MAC" "$USERSFILE" | cut -f2 -s -d,) + ;; + esac + [ "$USER" = "*" ] && USER= + [ -n "$USER" ] && break + done + if [ -n "$DO_RDNS" -a -z "$USER" -a "$IP" != "NA" -a -n "$nslookup" ]; then + USER=`$nslookup $IP $DNS | awk '!/server can/{if($4){print $4; exit}}' | sed -re 's/[.]$//'` + fi + [ -z "$USER" ] && USER=${MAC} + echo $USER +} + +detectIF() +{ + if [ -f "$networkFuncs" ]; then + IF=`. $networkFuncs; network_get_device netdev $1; echo $netdev` + [ -n "$IF" ] && echo $IF && return + fi + + if [ -n "$uci" -a -x "$uci" ]; then + IF=`$uci get network.${1}.ifname 2>/dev/null` + [ $? -eq 0 -a -n "$IF" ] && echo $IF && return + fi + + if [ -n "$nvram" -a -x "$nvram" ]; then + IF=`$nvram get ${1}_ifname 2>/dev/null` + [ $? -eq 0 -a -n "$IF" ] && echo $IF && return + fi +} + +detectLAN() +{ + [ -e /sys/class/net/br-lan ] && echo br-lan && return + lan=$(detectIF lan) + [ -n "$lan" ] && echo $lan && return +} + +detectWAN() +{ + [ -n "$WAN_IF" ] && echo $WAN_IF && return + wan=$(detectIF wan) + [ -n "$wan" ] && echo $wan && return + wan=$(ip route show 2>/dev/null | grep default | sed -re '/^default/ s/default.*dev +([^ ]+).*/\1/') + [ -n "$wan" ] && echo $wan && return + [ -f "$networkFuncs" ] && wan=$(. $networkFuncs; network_find_wan wan; echo $wan) + [ -n "$wan" ] && echo $wan && return +} + +lock() +{ + attempts=0 + while [ $attempts -lt 10 ]; do + mkdir $lockDir 2>/dev/null && break + attempts=$((attempts+1)) + pid=`cat $pidFile 2>/dev/null` + if [ -n "$pid" ]; then + if [ -d "/proc/$pid" ]; then + [ -n "$DEBUG" ] && echo "WARNING: Lockfile detected but process $(cat $pidFile) does not exist !" + rm -rf $lockDir + else + sleep 1 + fi + fi + done + mkdir $lockDir 2>/dev/null + echo $$ > $pidFile + [ -n "$DEBUG" ] && echo $$ "got lock after $attempts attempts" + trap '' INT +} + +unlock() +{ + rm -rf $lockDir + [ -n "$DEBUG" ] && echo $$ "released lock" + trap "rm -f /tmp/*_$$.tmp; kill $$" INT +} + +# chain +newChain() +{ + chain=$1 + # Create the RRDIPT_$chain chain (it doesn't matter if it already exists). + iptables -t mangle -N RRDIPT_$chain 2> /dev/null + + # Add the RRDIPT_$chain CHAIN to the $chain chain if not present + iptables -t mangle -C $chain -j RRDIPT_$chain 2>/dev/null + if [ $? -ne 0 ]; then + [ -n "$DEBUG" ] && echo "DEBUG: iptables chain misplaced, recreating it..." + iptables -t mangle -I $chain -j RRDIPT_$chain + fi +} + +# chain tun +newRuleIF() +{ + chain=$1 + IF=$2 + + #!@todo test + if [ "$chain" = "OUTPUT" ]; then + cmd="iptables -t mangle -o $IF -j RETURN" + eval $cmd " -C RRDIPT_$chain 2>/dev/null" || eval $cmd " -A RRDIPT_$chain" + elif [ "$chain" = "INPUT" ]; then + cmd="iptables -t mangle -i $IF -j RETURN" + eval $cmd " -C RRDIPT_$chain 2>/dev/null" || eval $cmd " -A RRDIPT_$chain" + fi +} + +update() +{ + #!@todo could let readDB.awk handle this; that would place header + #!info in fewer places + createDbIfMissing + + checkDB + checkWAN + + > /tmp/iptables_$$.tmp + lock + # only zero our own chains + for chain in $chains; do + iptables -nvxL RRDIPT_$chain -t mangle -Z >> /tmp/iptables_$$.tmp + done + # the iptables and readDB commands have to be separate. Otherwise, + # they will fight over iptables locks + awk -v mode="$mode" -v interfaces=\""$interfaces"\" -f $binDir/readDB.awk \ + $DB \ + /proc/net/arp \ + /tmp/iptables_$$.tmp + unlock +} + +############################################################ + +case $1 in + "dump" ) + checkDbArg + lock + tr ',' '\t' < "$DB" + unlock + ;; + + "update" ) + checkDbArg + wan=$(detectWAN) + interfaces="$interfaces $wan" + update + rm -f /tmp/*_$$.tmp + exit + ;; + + "publish" ) + checkDbArg + [ -z "$3" ] && echo "ERROR: Missing argument 3 (output html file)" && exit 1 + + # sort DB + lock + + # busybox sort truncates numbers to 32 bits + grep -v '^#' $DB | awk -F, '{OFS=","; a=sprintf("%f",$4/1e6); $4=""; print a,$0}' | tr -s ',' | sort -rn | awk -F, '{OFS=",";$1=sprintf("%f",$1*1e6);print}' > /tmp/sorted_$$.tmp + + # create HTML page + rm -f $3.tmp + cp $dataDir/usage.htm1 $3.tmp + + #!@todo fix publishing + while IFS=, read PEAKUSAGE_IN MAC IP IFACE PEAKUSAGE_OUT TOTAL FIRSTSEEN LASTSEEN + do + echo " +new Array(\"$(lookup $MAC $IP $4)\",\"$MAC\",\"$IP\", +$PEAKUSAGE_IN,$PEAKUSAGE_OUT,$TOTAL,\"$FIRSTSEEN\",\"$LASTSEEN\")," >> $3.tmp + done < /tmp/sorted_$$.tmp + echo "0);" >> $3.tmp + + sed "s/(date)/`date`/" < $dataDir/usage.htm2 >> $3.tmp + mv $3.tmp $3 + + unlock + + #Free some memory + rm -f /tmp/*_$$.tmp + ;; + + "setup" ) + checkDbArg + [ -w "$DB" ] && echo "Warning: using existing $DB" + createDbIfMissing + + for chain in $chains; do + newChain $chain + done + + #lan=$(detectLAN) + wan=$(detectWAN) + checkWAN + interfaces="$interfaces $wan" + + # track local data + for chain in INPUT OUTPUT; do + for interface in $interfaces; do + [ -n "$interface" ] && [ -e "/sys/class/net/$interface" ] && newRuleIF $chain $interface + done + done + + # this will add rules for hosts in arp table + update + + rm -f /tmp/*_$$.tmp + ;; + + "remove" ) + iptables-save | grep -v RRDIPT | iptables-restore + rm -rf "$lockDir" + ;; + + *) + echo \ +"Usage: $0 {setup|update|publish|remove} [options...] +Options: + $0 setup database_file + $0 update database_file + $0 publish database_file path_of_html_report [user_file] +Examples: + $0 setup /tmp/usage.db + $0 update /tmp/usage.db + $0 publish /tmp/usage.db /www/user/usage.htm /jffs/users.txt + $0 remove +Note: [user_file] is an optional file to match users with MAC addresses. + Its format is \"00:MA:CA:DD:RE:SS,username\", with one entry per line." + ;; +esac diff --git a/package/lean/luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json b/package/lean/luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json new file mode 100644 index 0000000000..8290672dc8 --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json @@ -0,0 +1,11 @@ +{ + "luci-app-wrtbwmon": { + "description": "Grant UCI access for luci-app-wrtbwmon", + "read": { + "uci": [ "wrtbwmon" ] + }, + "write": { + "uci": [ "wrtbwmon" ] + } + } +} diff --git a/package/lean/luci-app-wrtbwmon/root/usr/share/wrtbwmon/usage.htm1 b/package/lean/luci-app-wrtbwmon/root/usr/share/wrtbwmon/usage.htm1 new file mode 100644 index 0000000000..1f0c342a7c --- /dev/null +++ b/package/lean/luci-app-wrtbwmon/root/usr/share/wrtbwmon/usage.htm1 @@ -0,0 +1,23 @@ +Traffic + +

Total Usage:

+ + + + + + + + + +
UserDownloadUploadTotalFirst seenLast seen
+
This page was generated on (date) +