diff --git a/Makefile b/Makefile index cd4c956b..46b5a50b 100755 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-wechatpush -PKG_VERSION:=3.4.2 +PKG_VERSION:=3.5.3 PKG_RELEASE:= PKG_MAINTAINER:=tty228 diff --git a/README.md b/README.md index 5b2394d8..16fa39c9 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,6 @@ 精力有限,如需要钉钉推送、飞书推送、Bark 推送等请尝试另一个分支 https://github.com/zzsj0928/luci-app-pushbot ,或使用自定义 API 设置 -因插件一开始只支持 Server酱,故此插件命名为 luci-app-serverchan,但后续已经越来越臃肿,想改很久了,趁这次升级 js, 插件更名为 luci-app-wechatpush - -v3.0.0 安装时会自动从 luci-app-serverchan 移植配置信息,并删除原插件,如有缺漏,请在 /etc/config/serverchan.bak 查看,安装后可能需刷新浏览器页面,否则 luci 页面入口不会更换为新插件 URL(此功能择期移除) - v2.06.2 之后的版本不再支持 LuCI 18.06,如需编译,请使用 openwrt-18.06 分支,拉取源码时请使用 `git clone -b openwrt-18.06 https://github.com/tty228/luci-app-wechatpush.git` 命令 @@ -52,6 +48,7 @@ v2.06.2 之后的版本不再支持 LuCI 18.06,如需编译,请使用 openwr - 使用设备名备注 - 在高级设置处配置从光猫获取 - 开启 MAC 设备数据库 +- 安装 samba*-server 或 samba*-client,使程序可以通过 NetBIOS 查询主机名 **关于设备在线状态:** diff --git a/README_en.md b/README_en.md index 980b0fc9..6ee5e3b1 100644 --- a/README_en.md +++ b/README_en.md @@ -42,6 +42,7 @@ For devices that do not declare hostnames, devices connected via optical modem d - Use device name remarks. - Configure to obtain the hostname from the optical modem in advanced settings. - Enable MAC device database. +- Install samba*-server or samba*-client to enable the program to query hostnames via NetBIOS. **Regarding Device Online Status:** diff --git a/htdocs/luci-static/resources/view/wechatpush/advanced.js b/htdocs/luci-static/resources/view/wechatpush/advanced.js index 0d403ad3..da574d50 100644 --- a/htdocs/luci-static/resources/view/wechatpush/advanced.js +++ b/htdocs/luci-static/resources/view/wechatpush/advanced.js @@ -5,13 +5,67 @@ 'require uci'; 'require rpc'; 'require form'; -'require tools.widgets as widgets'; -'require tools.firewall as fwtool'; +'require poll'; return view.extend({ + callHostHints: rpc.declare({ + object: 'luci-rpc', + method: 'getHostHints', + expect: { '': {} } + }), + + load: function () { + return Promise.all([ + this.callHostHints(), + fs.read('/proc/net/arp') + ]); + }, + + parseArp: function (data) { + var lines = data.split('\n'), + hosts = []; + + for (var i = 1; i < lines.length; i++) { + var columns = lines[i].replace(/ +/g, ' ').split(' '); + + if (columns.length >= 6) { + hosts.push({ + ip: columns[0], + mac: columns[3] + }); + } + } + + // Sort hosts array by IP address + hosts.sort(function (a, b) { + var ipA = a.ip.split('.').map(Number); + var ipB = b.ip.split('.').map(Number); + + for (var i = 0; i < 4; i++) { + if (ipA[i] !== ipB[i]) { + return ipA[i] - ipB[i]; + } + } + + return 0; + }); + + return hosts; + }, + + formatHostIPMAC: function (host) { + return host.ip + ' (' + host.mac + ')'; + }, + + formatHostMACIP: function (host) { + return host.mac + ' (' + host.ip + ')'; + }, + render: function (data) { - var m, s, o; - var programPath = '/usr/share/wechatpush/wechatpush'; + var arpData = data[1], + hosts = this.parseArp(arpData), + m, s, o, + programPath = '/usr/share/wechatpush/wechatpush'; m = new form.Map('wechatpush', _('')) m.description = _("If you are not familiar with the meanings of these options, please do not modify them.

") @@ -39,6 +93,14 @@ return view.extend({ o.rmempty = false; o.description = _("If the device has good signal strength and no Wi-Fi sleep issues, you can reduce the above values.
Due to the mysterious nature of Wi-Fi sleep during the night, if you encounter frequent disconnections, please adjust the parameters accordingly.
..╮(╯_╰)╭..") + o = s.option(form.DynamicList, 'always_check_ip_list', _('IP address to always scan')); + o.datatype = 'ipaddr'; + o.description = _('The IPs in the list are always subjected to online detection regardless of whether they exist in the ARP list, suitable for secondary routing scenarios.'); + + hosts.forEach(function (host) { + o.value(host.ip, this.formatHostIPMAC(host)); + }, this); + o = s.option(form.Flag, "only_timeout_push", _("Offline timeout applies only to the devices that receive push notifications")) o.default = 0 o.rmempty = true @@ -55,6 +117,12 @@ return view.extend({ o.rmempty = false; o.description = _("Do not change the setting value for low-performance devices, or reduce the parameters as appropriate.") + o = s.option(form.ListValue, "defaultSortColumn", _("Client list sorting method")) + o.default = "ip" + o.value("ip", _("IP")) + o.value("uptime", _("Online time")) + o.description = _("This will change the sorting method for both the online device list page and the sorting order in the push content.") + o = s.option(form.Value, "soc_code", _('Custom temperature reading command')) o.rmempty = true o.value("", _("Default")) @@ -160,12 +228,15 @@ return view.extend({ o.description = _("Avoid redialing network during the day to prevent waiting for DDNS domain resolution. This feature does not affect disconnection detection.
Due to the issue of certain apps consuming excessive data at night, this feature may be unstable.") o.depends('unattended_enable', '1'); - o = s.option(form.DynamicList, "unattended_device_aliases", _("Followed device list")) - o.rmempty = true + o = s.option(form.DynamicList, 'unattended_device_aliases', _('Followed device list')); + o.datatype = 'macaddr'; o.description = _("Will only be executed when none of the devices in the list are online.
After an hour of Do-Not-Disturb period, if the devices in the focus list have low traffic (around 100kb/m) for five minutes, they will be considered offline.") - //nt.mac_hints(function(mac, name) o :value(mac, "%s (%s)" %{ mac, name }) end) o.depends('unattended_enable', '1'); + hosts.forEach(function (host) { + o.value(host.mac, this.formatHostMACIP(host)); + }, this); + o = s.option(form.ListValue, "network_disconnect_event", _("When the network is disconnected")) o.default = "" o.value("", _("No operation")) diff --git a/htdocs/luci-static/resources/view/wechatpush/client.js b/htdocs/luci-static/resources/view/wechatpush/client.js index 5d707492..66639b08 100644 --- a/htdocs/luci-static/resources/view/wechatpush/client.js +++ b/htdocs/luci-static/resources/view/wechatpush/client.js @@ -2,20 +2,70 @@ 'require view'; 'require fs'; 'require ui'; +'require poll'; +'require uci'; return view.extend({ load: function () { - return L.resolveDefault(fs.exec_direct('/usr/libexec/wechatpush-call', ['get_client'], 'json'), { devices: [] }); + var self = this; + // 清除 localStorage 中的排序设置 + localStorage.removeItem('sortColumn'); + localStorage.removeItem('sortDirection'); + uci.load('wechatpush') + return this.fetchAndRenderDevices().then(function () { + self.setupAutoRefresh(); + }); + }, + + fetchAndRenderDevices: function () { + var self = this; + return this.fetchDevices().then(function (data) { + var container = self.render(data); + self.switchContent(container); + }).catch(function (error) { + console.error('Error fetching or rendering devices:', error); + }); + }, + + fetchDevices: function () { + return fs.read('/tmp/wechatpush/devices.json').then(function (content) { + try { + var data = JSON.parse(content); + return { devices: data.devices }; + } catch (e) { + console.error('Error parsing JSON:', e); + return { devices: [] }; + } + }); }, - render: function (data) { - var devices = data.devices; + render: function (data) { + if (!data || !data.devices || !Array.isArray(data.devices)) { + return document.createElement('div'); + } + var devices = data.devices.filter(device => device.status === 'online' || device.status === 'unknown'); var totalDevices = devices.length; var headers = [_('Hostname'), _('IPv4 address'), _('MAC address'), _('Interfaces'), _('Online time'), _('Details')]; var columns = ['name', 'ip', 'mac', 'interface', 'uptime', 'usage']; var visibleColumns = []; var hasData = false; + // 获取配置中的默认排序列 + var defaultSortColumn = uci.get('wechatpush', 'config', 'defaultSortColumn') || 'ip'; + var defaultSortDirection = (defaultSortColumn === 'uptime') ? 'desc' : 'asc'; + + // 获取存储的排序设置,如果没有则使用默认设置 + var storedSortColumn = localStorage.getItem('sortColumn'); + var storedSortDirection = localStorage.getItem('sortDirection'); + + var currentSortColumn = storedSortColumn || defaultSortColumn; + var currentSortDirection = storedSortDirection || defaultSortDirection; + + devices.sort(function (a, b) { + return compareDevices(a, b, currentSortColumn, currentSortDirection); + }); + + // 根据数据源决定可见列 for (var i = 0; i < columns.length; i++) { var column = columns[i]; var hasColumnData = false; @@ -86,15 +136,17 @@ return view.extend({ display: none; } @media (max-width: 767px) { + .device-table th:nth-of-type(3), + .device-table td:nth-of-type(3) { + display: none; + } .device-table th:nth-of-type(4), .device-table td:nth-of-type(4) { display: none; } .device-table th, .device-table td { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + padding: 0.35rem; } .device-table td:first-child { max-width: 150px; @@ -116,6 +168,9 @@ return view.extend({ if (visibleColumns.includes(i)) { th.classList.add('sortable'); th.dataset.column = columns[i]; + if (columns[i] === currentSortColumn) { + th.classList.add(currentSortDirection === 'asc' ? 'asc' : 'desc'); + } } else { th.classList.add('hide'); } @@ -132,7 +187,17 @@ return view.extend({ for (var i = 0; i < columns.length; i++) { if (visibleColumns.includes(i)) { var cell = document.createElement('td'); - cell.textContent = device[columns[i]]; + if (columns[i] === 'uptime') { + cell.textContent = calculateUptime(device['uptime']); + } else if (columns[i] === 'ip' && device['http_access']) { + var link = document.createElement('a'); + link.href = `${device['http_access']}://${device['ip']}`; + link.textContent = device['ip']; + link.target = '_blank'; + cell.appendChild(link); + } else { + cell.textContent = device[columns[i]]; + } row.appendChild(cell); } } @@ -144,88 +209,50 @@ return view.extend({ return table; } - var container = document.createElement('div'); - container.appendChild(document.createElement('h2')).textContent = _('当前共 ') + totalDevices + _(' 台设备在线'); - container.appendChild(createTable()); - container.appendChild(document.createElement('style')).textContent = style; - - function sortTable(column) { - var table = container.querySelector('.device-table'); - var tbody = table.querySelector('tbody'); - var rows = Array.from(tbody.querySelectorAll('tr')); - - var isAscending = true; - - if (table.classList.contains('sorted') && table.dataset.sortColumn === column) { - isAscending = !table.classList.contains('asc'); - } - - rows.sort(function (row1, row2) { - var value1 = row1.querySelector('td:nth-of-type(' + (visibleColumns.indexOf(column) + 1) + ')').textContent.toLowerCase(); - var value2 = row2.querySelector('td:nth-of-type(' + (visibleColumns.indexOf(column) + 1) + ')').textContent.toLowerCase(); - - if (value1 < value2) { - return isAscending ? -1 : 1; - } else if (value1 > value2) { - return isAscending ? 1 : -1; - } - - return 0; - }); - - tbody.innerHTML = ''; - - rows.forEach(function (row) { - tbody.appendChild(row); - }); - - table.classList.remove('sorted', 'asc', 'desc'); - if (isAscending) { - table.classList.add('sorted', 'asc'); + function calculateUptime(uptime) { + // 将时间戳转换为时间格式 + var startTimeStamp = parseInt(uptime); + var currentTimeStamp = Math.floor(Date.now() / 1000); + var uptimeInSeconds = currentTimeStamp - startTimeStamp; + + var days = Math.floor(uptimeInSeconds / (3600 * 24)); + var hours = Math.floor((uptimeInSeconds % (3600 * 24)) / 3600); + var minutes = Math.floor((uptimeInSeconds % 3600) / 60); + var seconds = uptimeInSeconds % 60; + + if (days > 0) { + return days + ' 天 ' + hours + ' 小时'; + } else if (hours > 0) { + return hours + ' 小时 ' + minutes + ' 分钟'; + } else if (minutes > 0) { + return minutes + ' 分钟 ' + seconds + ' 秒'; } else { - table.classList.add('sorted', 'desc'); + return seconds + ' 秒'; } - table.dataset.sortColumn = column; } - container.addEventListener('click', function (event) { - if ( - event.target.tagName === 'TH' && - event.target.parentNode.rowIndex === 0 - ) { - var columnIndex = event.target.cellIndex; - var table = container.querySelector('.device-table'); - var tbody = table.querySelector('tbody'); - var rows = Array.from(tbody.querySelectorAll('tr')); - - rows.sort(function (row1, row2) { - var value1 = row1.cells[columnIndex].textContent.trim(); - var value2 = row2.cells[columnIndex].textContent.trim(); - - if (columnIndex === 0) { - return value1.length - value2.length; - } else if (columnIndex === 1) { - value1 = ipToNumber(value1); - value2 = ipToNumber(value2); - } else if (columnIndex === 4) { - value1 = parseOnlineTime(value1); - value2 = parseOnlineTime(value2); - } - - if (value1 < value2) { - return -1; - } else if (value1 > value2) { - return 1; - } + function compareDevices(a, b, column, direction) { + var value1 = getValueForSorting(a, column); + var value2 = getValueForSorting(b, column); - return 0; - }); + if (value1 < value2) { + return direction === 'asc' ? -1 : 1; + } else if (value1 > value2) { + return direction === 'asc' ? 1 : -1; + } + return 0; + } - rows.forEach(function (row) { - tbody.appendChild(row); - }); + function getValueForSorting(device, column) { + var value = device[column]; + if (column === 'uptime') { + // 使用时间戳排序 + return parseInt(device['uptime']); + } else if (column === 'ip') { + return ipToNumber(value); } - }); + return value; + } function ipToNumber(ipAddress) { var parts = ipAddress.split('.'); @@ -238,26 +265,66 @@ return view.extend({ return number; } - function parseOnlineTime(time) { - var regex = /(\d+)\s+(小时|分钟|秒)/g; - var matches = time.matchAll(regex); - var minutes = 0; - - for (var match of matches) { - var value = parseInt(match[1]); - var unit = match[2]; + var container = document.createElement('div'); + container.appendChild(document.createElement('h2')).textContent = _('当前共 ') + totalDevices + _(' 台设备在线'); + container.appendChild(createTable()); + container.appendChild(document.createElement('style')).textContent = style; - if (unit === '小时') { - minutes += value * 60; - } else if (unit === '分钟') { - minutes += value; + container.addEventListener('click', function (event) { + if (event.target.tagName === 'TH' && event.target.parentNode.rowIndex === 0) { + var columnIndex = event.target.cellIndex; + var column = columns[columnIndex]; + var direction = 'asc'; + + // 使在线时间第一次点击方向为倒序 + if (column === 'uptime') { + direction = currentSortDirection === 'desc' ? 'asc' : 'desc'; + } else if (column === currentSortColumn) { + direction = currentSortDirection === 'asc' ? 'desc' : 'asc'; } + + sortTable(column, direction, container); } + }); + + function sortTable(column, direction, container) { + devices.sort(function (a, b) { + return compareDevices(a, b, column, direction); + }); + + currentSortColumn = column; + currentSortDirection = direction; - return minutes; + // 存储排序设置 + localStorage.setItem('sortColumn', currentSortColumn); + localStorage.setItem('sortDirection', currentSortDirection); + + container.innerHTML = ''; + container.appendChild(document.createElement('h2')).textContent = _('当前共 ') + totalDevices + _(' 台设备在线'); + container.appendChild(createTable()); + container.appendChild(document.createElement('style')).textContent = style; } + return container; }, + + setupAutoRefresh: function () { + var self = this; + poll.add(L.bind(function () { + self.fetchAndRenderDevices(); + })); + }, + + switchContent: function (newContent) { + var existingContainer = document.querySelector('#view'); + if (!existingContainer) { + console.error('Table container not found.'); + return; + } + existingContainer.innerHTML = ''; + existingContainer.appendChild(newContent); + }, + handleSave: null, handleSaveApply: null, handleReset: null diff --git a/htdocs/luci-static/resources/view/wechatpush/config.js b/htdocs/luci-static/resources/view/wechatpush/config.js index 3c986e3a..90357358 100644 --- a/htdocs/luci-static/resources/view/wechatpush/config.js +++ b/htdocs/luci-static/resources/view/wechatpush/config.js @@ -41,7 +41,7 @@ function renderStatus(isRunning) { } var cbiRichListValue = form.ListValue.extend({ - renderWidget: function(section_id, option_index, cfgvalue) { + renderWidget: function (section_id, option_index, cfgvalue) { var choices = this.transformChoices(); var widget = new ui.Dropdown((cfgvalue != null) ? cfgvalue : this.default, choices, { id: this.cbid(section_id), @@ -56,12 +56,12 @@ var cbiRichListValue = form.ListValue.extend({ return widget.render(); }, - value: function(value, title, description) { + value: function (value, title, description) { if (description) { form.ListValue.prototype.value.call(this, value, E([], [ - E('span', { 'class': 'hide-open' }, [ title ]), + E('span', { 'class': 'hide-open' }, [title]), E('div', { 'class': 'hide-close', 'style': 'min-width:25vw' }, [ - E('strong', [ title ]), + E('strong', [title]), E('br'), E('span', { 'style': 'white-space:normal' }, description) ]) @@ -333,10 +333,10 @@ return view.extend({ _('May fail due to server stability and frequent connections.
If the interface can obtain the IP address properly, it is not recommended to use this method.')); o = s.taboption('content', widgets.DeviceSelect, 'ipv4_interface', _("Device")); - o.description = _('Typically, it should be WAN or br-lan interface. For multi-wan environments, please choose accordingly.'); + o.description = _('Generally, it should be the pppoe-wan or WAN interface. For multi-dial environments, please select the appropriate interface yourself.'); o.modalonly = true; o.multiple = false; - o.default = 'WAN'; + o.default = 'pppoe-wan'; o.depends('get_ipv4_mode', '1'); o = s.taboption('content', form.TextValue, 'ipv4_list', _('IPv4 API List')); @@ -384,7 +384,7 @@ return view.extend({ _('May fail due to server stability and frequent connections.
If the interface can obtain the IP address properly, it is not recommended to use this method.')); o = s.taboption('content', widgets.DeviceSelect, 'ipv6_interface', _("Device")); - o.description = _('Typically, it should be WAN or br-lan interface. For multi-wan environments, please choose accordingly.'); + o.description = _('Generally, it should be the pppoe-wan or WAN interface. For multi-dial environments, please select the appropriate interface yourself.'); o.modalonly = true; o.multiple = false; o.default = 'WAN'; @@ -425,17 +425,17 @@ return view.extend({ }); } o.depends('get_ipv6_mode', '2'); - + o = s.taboption('content', form.Flag, 'auto_update_ip_list', _('Automatically update API list')); o.description = _('When multiple IP retrieval attempts fail, try to automatically update the list file from GitHub'); o.depends('get_ipv4_mode', '2'); o.depends('get_ipv6_mode', '2'); - + o = s.taboption('content', form.MultiValue, 'device_notification', _('Device Online/Offline Notification')); o.value('online', _('Online Notification')); o.value('offline', _('Offline Notification')); o.modalonly = true; - + o = s.taboption('content', form.MultiValue, 'cpu_notification', _('CPU Alert')); o.value('load', _('Load Alert')); o.value('temp', _('Temperature Alert')); @@ -451,9 +451,9 @@ return view.extend({ if (!isNaN(floatValue) && floatValue.toString() === value) { return true; } - return 'Please enter a numeric value only'; + return _('Please enter a numeric value only'); }; - o.description = _('In general, when the load value is lower than the number of logical cores, you typically don\'t need to pay much attention to it'); + o.description = _('In general, when the load value is lower than the number of logical cores, you typically don\'t need to pay much attention to it.'); o = s.taboption('content', form.Value, 'temperature_threshold', _('Temperature alert threshold')); o.rmempty = false; @@ -613,7 +613,7 @@ return view.extend({ o.value('device', _('Simplify the current device list')); o.value('nowtime', _('Simplify the current time')); o.value('content', _('Push only the title')); - + o = s.taboption('disturb', cbiRichListValue, 'do_not_disturb_mode', _('Do Not Disturb time setting')); o.value('', _('Close'), _(' ')); diff --git a/po/zh_Hans/wechatpush.po b/po/zh_Hans/wechatpush.po index 8148d9ed..c26de290 100644 --- a/po/zh_Hans/wechatpush.po +++ b/po/zh_Hans/wechatpush.po @@ -325,8 +325,8 @@ msgstr "从以上列表中随机访问地址
文本框请使用「保存」 #: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:315 #: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:366 -msgid "Typically, it should be WAN or br-lan interface. For multi-wan environments, please choose accordingly." -msgstr "一般应为 WAN 或 br-lan 接口。多拨环境请自行选择。" +msgid "Generally, it should be the pppoe-wan or WAN interface. For multi-dial environments, please select the appropriate interface yourself." +msgstr "一般应为 pppoe-wan 或 WAN 接口。多拨环境请自行选择。" #: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:321 msgid "IPv4 API List" @@ -401,7 +401,7 @@ msgid "Load alert threshold" msgstr "负载报警阈值" #: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:451 -msgid "In general, when the load value is lower than the number of logical cores, you typically don\'t need to pay much attention to it" +msgid "In general, when the load value is lower than the number of logical cores, you typically don't need to pay much attention to it." msgstr "通常情况下,负载值低于逻辑核心数量时,你不需要关注它" #: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:433 @@ -796,11 +796,18 @@ msgstr "若设备信号强度良好,无息屏 WiFi 休眠问题,可以减少 msgid "Offline timeout applies only to the devices that receive push notifications" msgstr "离线超时只针对推送设备" +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:95 +msgid "IP address to always scan" +msgstr "始终扫描的 IP" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:95 +msgid "The IPs in the list are always subjected to online detection regardless of whether they exist in the ARP list, suitable for secondary routing scenarios." +msgstr "列表中的 IP 始终进行在线检测,无论它是否存在于 ARP 列表,适用于二级路由等情况" + #: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:45 msgid "When this option is selected, the offline timeout and offline detection count apply only to the devices that require push notifications. Other devices will use default values, which can significantly reduce the time required for detection. However, it may result in inaccurate online time displayed in the online devices list. It is recommended to enable this option only when there are many devices and frequent offline occurrences are observed for specific devices of interest." msgstr "选中此项后,离线超时时间和离线检测次数只针对需要推送的设备,其余设备使用默认值,可有效减少检测所需的时间,但会造成在线设备列表中的在线时间不准确,仅建议设备较多且关注的设备离线频繁时使用" - #: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:47 msgid "Disable active detection" msgstr "关闭主动探测" @@ -817,6 +824,14 @@ msgstr "最大并发进程数" msgid "Do not change the setting value for low-performance devices, or reduce the parameters as appropriate." msgstr "低性能设备请勿更改设置值,或酌情减少参数" +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:120 +msgid "Client list sorting method" +msgstr "客户端列表排序方式" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:52 +msgid "This will change the sorting method for both the online device list page and the sorting order in the push content." +msgstr "这将会同时改变在线设备列表页面和推送内容中的排序方式" + #: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:58 msgid "Custom temperature reading command" msgstr "自定义温度读取命令" diff --git a/root/etc/init.d/wechatpush b/root/etc/init.d/wechatpush index 5839d70e..5044b635 100755 --- a/root/etc/init.d/wechatpush +++ b/root/etc/init.d/wechatpush @@ -7,16 +7,16 @@ USE_PROCD=1 dir="/tmp/wechatpush/" start_service() { - procd_open_instance - enable_value=$(uci get wechatpush.config.enable 2>/dev/null || echo "0") - [ "$enable_value" -ne "0" ] && procd_set_param command /usr/share/wechatpush/wechatpush && echo "wechatpush is starting now ..." - procd_close_instance + procd_open_instance + enable_value=$(uci get wechatpush.config.enable 2>/dev/null || echo "0") + [ "$enable_value" -ne "0" ] && procd_set_param command /usr/share/wechatpush/wechatpush && echo "wechatpush is starting now ..." + procd_close_instance } reload_service() { - stop - sleep 1 - start + stop + sleep 1 + start } stop_service() { @@ -32,5 +32,5 @@ stop_service() { } service_triggers() { - procd_add_reload_trigger wechatpush + procd_add_reload_trigger wechatpush } diff --git a/root/etc/uci-defaults/luci-wechatpush b/root/etc/uci-defaults/luci-wechatpush index 2e075855..28040d7a 100755 --- a/root/etc/uci-defaults/luci-wechatpush +++ b/root/etc/uci-defaults/luci-wechatpush @@ -1,184 +1,3 @@ #!/bin/sh -function get_config_wechatpush(){ - while [[ "$*" != "" ]]; do - eval ${1}='`uci get wechatpush.config.$1`' 2>/dev/null - uci del wechatpush.config.$1 2>/dev/null - uci commit wechatpush - shift - done -} - -function get_config_serverchan(){ - while [[ "$*" != "" ]]; do - eval ${1}='`uci get serverchan.serverchan.$1`' 2>/dev/null - uci del serverchan.serverchan.$1 2>/dev/null - uci commit serverchan 2>/dev/null - shift - done -} - -function serverchan_to_wechatpush_list(){ - [ $2 ] && config_name=$2 || config_name=$1 - config_list=`uci get serverchan.serverchan.$1` 2>/dev/null - [ -z "$config_list" ] && return - uci del serverchan.serverchan.$1 2>/dev/null - uci commit serverchan 2>/dev/null - for config in $config_list ;do - uci add_list wechatpush.config.$config_name="$config" - done - uci commit wechatpush -} - -function serverchan_to_wechatpush_change(){ - config=`uci get serverchan.serverchan.$1` 2>/dev/null - [ -z "$config" ] && return - uci del serverchan.serverchan.$1 2>/dev/null - uci commit serverchan 2>/dev/null - uci set wechatpush.config.$2=$config - uci commit wechatpush -} - -function serverchan_to_wechatpush_get(){ - while [[ "$*" != "" ]]; do - config=`uci get serverchan.serverchan.$1` 2>/dev/null - [ -z "$config" ] && shift && continue - uci del serverchan.serverchan.$1 2>/dev/null - uci commit serverchan 2>/dev/null - uci set wechatpush.config.${1}=${config} - uci commit wechatpush - shift - done -} - -if [ ! -f "/etc/config/wechatpush" ]; then -cat>/etc/config/wechatpush</dev/null - sed -i 's#/root/usr/bin/serverchan/api/#/usr/share/wechatpush/api/#g' "$serverchan_config" 2>/dev/null -fi - -get_config_serverchan "serverchan_up" "serverchan_down" -[ -n "$serverchan_up" ] && [ "$serverchan_up" -eq "1" ] && uci add_list wechatpush.config.device_notification='online' -[ -n "$serverchan_down" ] && [ "$serverchan_down" -eq "1" ] && uci add_list wechatpush.config.device_notification='offline' - -get_config_serverchan "cpuload_enable" "temperature_enable" -[ -n "$cpuload_enable" ] && [ "$cpuload_enable" -eq "1" ] && uci add_list wechatpush.config.cpu_notification='load' -[ -n "$temperature_enable" ] && [ "$temperature_enable" -eq "1" ] && uci add_list wechatpush.config.cpu_notification='temp' - -get_config_serverchan "web_logged" "ssh_logged" "web_login_failed" "ssh_login_failed" -[ -n "$web_logged" ] && [ "$web_logged" -eq "1" ] && uci add_list wechatpush.config.login_notification='web_logged' -[ -n "$ssh_logged" ] && [ "$ssh_logged" -eq "1" ] && uci add_list wechatpush.config.login_notification='ssh_logged' -[ -n "$web_login_failed" ] && [ "$web_login_failed" -eq "1" ] && uci add_list wechatpush.config.login_notification='web_login_failed' -[ -n "$ssh_login_failed" ] && [ "$ssh_login_failed" -eq "1" ] && uci add_list wechatpush.config.login_notification='ssh_login_failed' - -get_config_serverchan "router_status" "router_temp" "router_wan" "client_list" -[ -n "$router_status" ] && [ "$router_status" -eq "1" ] && uci add_list wechatpush.config.send_notification='router_status' -[ -n "$router_temp" ] && [ "$router_temp" -eq "1" ] && uci add_list wechatpush.config.send_notification='router_temp' -[ -n "$router_wan" ] && [ "$router_wan" -eq "1" ] && uci add_list wechatpush.config.send_notification='wan_info' -[ -n "$client_list" ] && [ "$client_list" -eq "1" ] && uci add_list wechatpush.config.send_notification='client_list' - -get_config_serverchan "regular_time" "regular_time2" "regular_time3" -[ -n "$regular_time" ] && uci add_list wechatpush.config.crontab_regular_time="$regular_time" -[ -n "$regular_time2" ] && uci add_list wechatpush.config.crontab_regular_time="$regular_time2" -[ -n "$regular_time3" ] && uci add_list wechatpush.config.crontab_regular_time="$regular_time3" - -get_config_wechatpush "gateway_info_enable" "gateway_sleeptime" -[ -n "$gateway_info_enable" ] && [ "$" -eq "1" ] && uci add_list wechatpush.config.device_info_helper='gateway_info' -[ -n "$gateway_sleeptime" ] && uci set wechatpush.config.device_info_helper_sleeptime="$gateway_sleeptime" - -cpu_threshold_duration=$(uci get wechatpush.config.cpu_threshold_duration 2>/dev/null) -cpu_notification_delay=$(uci get wechatpush.config.cpu_notification_delay 2>/dev/null) -[ -z "$cpu_threshold_duration" ] && uci set wechatpush.config.cpu_threshold_duration='300' -[ -z "$cpu_notification_delay" ] && uci set wechatpush.config.cpu_notification_delay='3600' - -serverchan_to_wechatpush_list "device_notification" -serverchan_to_wechatpush_list "cpu_notification" -serverchan_to_wechatpush_list "login_notification" -serverchan_to_wechatpush_list "send_notification" -serverchan_to_wechatpush_list "ip_white_list" "login_ip_white_list" -serverchan_to_wechatpush_list "port_forward_list" "login_port_forward_list" -serverchan_to_wechatpush_list "client_usage_whitelist" -serverchan_to_wechatpush_list "serverchan_whitelist" "up_down_push_whitelist" -serverchan_to_wechatpush_list "serverchan_blacklist" "up_down_push_blacklist" -serverchan_to_wechatpush_list "MAC_online_list" "mac_online_list" -serverchan_to_wechatpush_list "MAC_offline_list" "mac_offline_list" -serverchan_to_wechatpush_list "err_device_aliases" "unattended_device_aliases" - -get_config_serverchan "device_aliases" -mkdir -p /usr/share/wechatpush/api/ -[ -n "$device_aliases" ] && echo "$device_aliases"|sed 's/ /\n/g'|sed 's/-/ /' > /usr/share/wechatpush/api/device_aliases.list -serverchan_device_aliases_path="/usr/share/serverchan/api/device_aliases.list" -wechatpush_device_aliases_path="/usr/share/wechatpush/api/device_aliases.list" -[ -f "$wechatpush_device_aliases_path" ] && [ `tail -n1 "${wechatpush_device_aliases_path}" | wc -l` -eq "0" ] && echo -e >> ${wechatpush_device_aliases_path} -[ -f "$serverchan_device_aliases_path" ] && grep -v '^#' "$serverchan_device_aliases_path" | grep -v '^[[:space:]]*$' | sort -u > "$wechatpush_device_aliases_path.temp" && mv "$wechatpush_device_aliases_path.temp" "$wechatpush_device_aliases_path" -[ -f "$serverchan_device_aliases_path" ] && rm -rf "$serverchan_device_aliases_path" -cp -r "/usr/share/serverchan/api/." "/usr/share/wechatpush/api/" 2>/dev/null -rm -rf "/usr/share/serverchan/api" 2>/dev/null - -serverchan_to_wechatpush_change "serverchan_enable" "enable" -serverchan_to_wechatpush_change "serverchan_ipv4" "get_ipv4_mode" -serverchan_to_wechatpush_change "serverchan_ipv6" "get_ipv6_mode" -serverchan_to_wechatpush_change "update_list" "auto_update_ip_list" -serverchan_to_wechatpush_change "cpuload" "cpu_load_threshold" -serverchan_to_wechatpush_change "temperature" "temperature_threshold" -serverchan_to_wechatpush_change "web_login_black" "login_web_black" -serverchan_to_wechatpush_change "ip_black_timeout" "login_ip_black_timeout" -serverchan_to_wechatpush_change "port_knocking" "port_knocking_enable" -serverchan_to_wechatpush_change "ip_white_timeout" "login_ip_white_timeout" -serverchan_to_wechatpush_change "ip_port_white" "login_port_white" -serverchan_to_wechatpush_change "regular_time" "crontab_regular_time" -serverchan_to_wechatpush_change "interval_time" "crontab_interval_time" -serverchan_to_wechatpush_change "serverchan_sheep" "do_not_disturb_mode" -serverchan_to_wechatpush_change "starttime" "do_not_disturb_starttime" -serverchan_to_wechatpush_change "endtime" "do_not_disturb_endtime" -serverchan_to_wechatpush_change "serverchan_interface" "up_down_push_interface" -serverchan_to_wechatpush_change "passive_option" "passive_mode" -serverchan_to_wechatpush_change "err_enable" "unattended_enable" -serverchan_to_wechatpush_change "err_sheep_enable" "unattended_only_on_disturb_time" -serverchan_to_wechatpush_change "network_err_event" "network_disconnect_event" -serverchan_to_wechatpush_change "system_time_event" "unattended_autoreboot_mode" -serverchan_to_wechatpush_change "autoreboot_time" "autoreboot_system_uptime" -serverchan_to_wechatpush_change "network_restart_time autoreboot_network_uptime" -serverchan_to_wechatpush_change "crontab" "crontab_mode" -serverchan_to_wechatpush_change "macmechanism" "mac_filtering_mode_1" -serverchan_to_wechatpush_change "macmechanism2" "mac_filtering_mode_2" - -serverchan_to_wechatpush_get \ - "enable" "lite_enable" "device_name" "sleeptime" "oui_data" "reset_regularly" "debuglevel" \ - "jsonpath" "sckey" "corpid" "userid" "agentid" "corpsecret" "mediapath" "wxpusher_apptoken" "wxpusher_uids" "wxpusher_topicIds" "pushplus_token" "tg_token" "chat_id" \ - "get_ipv4_mode" "ipv4_interface" "get_ipv6_mode" "ipv6_interface" "auto_update_ip_list" \ - "device_notification" "cpu_notification" "cpu_load_threshold" "temperature_threshold" \ - "client_usage" "client_usage_max" "client_usage_disturb" "client_usage_whitelist" \ - "login_notification" "login_max_num" "login_web_black" "login_ip_black_timeout" "login_ip_white_list" "port_knocking_enable" "login_ip_white_timeout" "login_port_white" "login_port_forward_list" \ - "crontab_regular_time" "crontab_interval_time" \ - "do_not_disturb_mode" "do_not_disturb_starttime" "do_not_disturb_endtime" "up_down_push_whitelist" "up_down_push_blacklist" "up_down_push_interface" "mac_online_list" "mac_offline_list" "login_disturb" "login_notification_delay" \ - "up_timeout" "down_timeout" "timeout_retry_count" "passive_mode" "thread_num" "soc_code" "server_host" "server_port" \ - "unattended_enable" "zerotier_helper" "unattended_only_on_disturb_time" "unattended_device_aliases" "network_disconnect_event" "unattended_autoreboot_mode" "autoreboot_system_uptime" "autoreboot_network_uptime" \ - "gateway_info_enable" "gateway_host_url" "gateway_info_url" "gateway_logout_url" "gateway_username_id" "gateway_password_id" "gateway_username" "gateway_password" "gateway_sleeptime" - -uci commit wechatpush -rm -rf "$serverchan_config" 2>/dev/null -dir="/tmp/wechatpush/" && mkdir -p ${dir} -echo "" > ${dir}web_login -echo "" > ${dir}ssh_login -echo "" > ${dir}web_failed -echo "" > ${dir}ssh_failed - -nohup sh -c 'while [ -f /var/lock/opkg.lock ]; do sleep 1; done; opkg remove luci-app-serverchan; rm -rf /tmp/luci-*' >/dev/null 2>&1 & +rm -rf /tmp/luci-* >/dev/null 2>&1 diff --git a/root/usr/libexec/wechatpush-call b/root/usr/libexec/wechatpush-call index 3b1e1b72..a85bbd3b 100755 --- a/root/usr/libexec/wechatpush-call +++ b/root/usr/libexec/wechatpush-call @@ -1,48 +1,50 @@ #!/bin/sh logfile="/tmp/wechatpush/wechatpush.log" -dir="/tmp/wechatpush/" && mkdir -p ${dir} && mkdir -p ${dir}/client -oui_base=${dir}oui_base.txt +dir="/tmp/wechatpush" && mkdir -p "${dir}" +oui_base="${dir}/oui_base.txt" oui_data=$(uci get wechatpush.config.oui_data 2>/dev/null) oui_url="https://standards-oui.ieee.org/oui/oui.txt" -function file_date(){ - file_dir="$1" - [ -f ${file_dir} ] && filerow=$(grep -c "" ${file_dir}) || filerow="0" - [ "$filerow" -ne "0" ] && datetime=$(date -r ${file_dir} +%s 2>/dev/null) || datetime=0 - expr $(date +%s) - $datetime +# 获取文件最后修改时间和行数 +file_date() { + local file_dir="$1" + local filerow=$(grep -c "" "${file_dir}" 2>/dev/null || echo "0") + local datetime=$(date -r "${file_dir}" +%s 2>/dev/null || echo "0") + expr $(date +%s) - ${datetime} } -function down_oui(){ - if [ ! -z "$oui_data" ] && [ "$oui_data" -ne "3" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】开始后台更新 MAC 信息文件" >> ${logfile} - wget --no-check-certificate -t 10 -T 15 -O ${dir}oui.txt "$oui_url" >/dev/null 2>&1 - [ $? -ne "0"] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】设备 MAC 信息文件下载失败,返回码为 $? " && return - echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】设备 MAC 信息文件下载成功,处理中" >> ${logfile} - if [ "$oui_data" -eq "1" ]; then - cat ${dir}oui.txt | grep -i -E ".*(base 16).*(apple|aruba|asus|autelan|belkin|bhu|buffalo|cctf|cisco|comba|datang|dell|dlink|dowell|ericsson|fast|feixun|fiberhome|fujitsu|grentech|h3c|hisense|hiwifi|honghai|honghao|hp|htc|huawei|intel|jinli|jse|lenovo|lg|liteon|malata|meizu|mercury|meru|moto|netcore|netgear|nokia|omron|oneplus|oppo|philips|router_unkown|samsung|shanzhai|sony|start_net|sunyuanda|tcl|tenda|texas|tianyu|tp-link|ubq|undefine|VMware|utstarcom|volans|xerox|xiaomi|zdc|zhongxing|smartisan).*" | sed -E 's/( Electronic| Technology| Intelligence| TECHNOLOGIES| Device| Systems| TELECOMMUNICATIONS| Instruments| Electronics| Corporation| Telecommunication| Communications| Electrical| Technology| Corporate| Intelligent| Interactive| MOBILE| Solutions| Mobility| Meraki| ELECTRO| VISUAL| Limited| International| Information| LLC|Co$|Co\.|Ltd\.$|Inc\.|B\.V\.$|AB$|,).*$/ /I; s/[[:space:]]*$//; s/ +$//' > ${oui_base} - elif [ "$oui_data" -eq "2" ]; then - cat ${dir}oui.txt | grep -i "(base 16)" | sed -E 's/( Electronic| Technology| Intelligence| TECHNOLOGIES| Device| Systems| TELECOMMUNICATIONS| Instruments| Electronics| Corporation| Telecommunication| Communications| Electrical| Technology| Corporate| Intelligent| Interactive| MOBILE| Solutions| Mobility| Meraki| ELECTRO| VISUAL| Limited| International| Information| LLC|Co$|Co\.|Ltd\.$|Inc\.|B\.V\.$|AB$|,).*$/ /I; s/[[:space:]]*$//; s/ +$//' > ${oui_base} +# 更新 MAC 信息文件 +down_oui() { + if [ -n "${oui_data}" ] && [ "${oui_data}" -ne "3" ]; then + echo "$(date "+%Y-%m-%d %H:%M:%S") 【info】开始后台更新 MAC 信息文件" >>"${logfile}" + curl -fsSL --connect-timeout 15 --max-time 30 -o "${dir}/oui.txt" "${oui_url}" >/dev/null 2>&1 + local RETVAL=$? + [ ${RETVAL} -ne 0 ] && echo "$(date "+%Y-%m-%d %H:%M:%S") 【!!!】设备 MAC 信息文件下载失败,返回码为 ${RETVAL}" >>"${logfile}" && return 1 + echo "$(date "+%Y-%m-%d %H:%M:%S") 【info】设备 MAC 信息文件下载成功,处理中" >>"${logfile}" + if [ "${oui_data}" -eq "1" ]; then + grep -i -E ".*(base 16).*(apple|aruba|asus|autelan|belkin|bhu|buffalo|cctf|cisco|comba|datang|dell|dlink|dowell|ericsson|fast|feixun|fiberhome|fujitsu|grentech|h3c|hisense|hiwifi|honghai|honghao|hp|htc|huawei|intel|jinli|jse|lenovo|lg|liteon|malata|meizu|mercury|meru|moto|netcore|netgear|nokia|omron|oneplus|oppo|philips|router_unkown|samsung|shanzhai|sony|start_net|sunyuanda|tcl|tenda|texas|tianyu|tp-link|ubq|undefine|VMware|utstarcom|volans|xerox|xiaomi|zdc|zhongxing|smartisan).*" "${dir}/oui.txt" | sed -E 's/( Electronic| Technology| Intelligence| TECHNOLOGIES| Device| Systems| TELECOMMUNICATIONS| Instruments| Electronics| Corporation| Telecommunication| Communications| Electrical| Technology| Corporate| Intelligent| Interactive| MOBILE| Solutions| Mobility| Meraki| ELECTRO| VISUAL| Limited| International| Information| LLC|Co$|Co\.|Ltd\.$|Inc\.|B\.V\.$|AB$|,).*$/ /I; s/[[:space:]]*$//; s/ +$//' >"${oui_base}" + elif [ "${oui_data}" -eq "2" ]; then + grep -i "(base 16)" "${dir}/oui.txt" | sed -E 's/( Electronic| Technology| Intelligence| TECHNOLOGIES| Device| Systems| TELECOMMUNICATIONS| Instruments| Electronics| Corporation| Telecommunication| Communications| Electrical| Technology| Corporate| Intelligent| Interactive| MOBILE| Solutions| Mobility| Meraki| ELECTRO| VISUAL| Limited| International| Information| LLC|Co$|Co\.|Ltd\.$|Inc\.|B\.V\.$|AB$|,).*$/ /I; s/[[:space:]]*$//; s/ +$//' >"${oui_base}" fi - rm -f ${dir}oui.txt >/dev/null 2>&1 - echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】设备 MAC 信息文件处理完成" >> ${logfile} + rm -f "${dir}/oui.txt" >/dev/null 2>&1 + echo "$(date "+%Y-%m-%d %H:%M:%S") 【info】设备 MAC 信息文件处理完成" >>"${logfile}" fi } - -if [ "$1" == "get_client" ]; then - # 生成在线设备列表,沿用 LUA 版本生成的临时文件 - devices=$(find /tmp/wechatpush/client -type f -print0 | xargs -0 cat | sed "s/'/\"/g" | tr '\n' ',') - echo "{\"devices\": [${devices%,}]}" -elif [ "$1" == "clear_log" ]; then + +if [ "$1" == "clear_log" ]; then # 清空日志 - > /tmp/wechatpush/wechatpush.log + >"${logfile}" elif [ "$1" == "update_list" ]; then # 更新 API 列表 - echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】开始更新 $2 API 列表" >> ${logfile} - wget --no-check-certificate -t 3 -T 15 -O "/tmp/wechatpush/${2}.list" "https://raw.githubusercontent.com/tty228/luci-app-wechatpush/master/root/usr/share/wechatpush/api/${2}.list" >/dev/null 2>&1 - [ "$?" -eq "0" ] && mv -f "/tmp/wechatpush/${2}.list" "/usr/share/wechatpush/api/${2}.list" echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】 $2 API 列表更新成功" >> ${logfile} + echo "$(date "+%Y-%m-%d %H:%M:%S") 【info】开始更新 ${2} API 列表" >>"${logfile}" + curl -fsSL --connect-timeout 15 --max-time 30 -o "/tmp/wechatpush/${2}.list" "https://raw.githubusercontent.com/tty228/luci-app-wechatpush/master/root/usr/share/wechatpush/api/${2}.list" >/dev/null 2>&1 + local RETVAL=$? + [ ${RETVAL} -ne 0 ] && echo "$(date "+%Y-%m-%d %H:%M:%S") 【!!!】${2} API 列表更新失败,返回码为 ${RETVAL}" >>"${logfile}" && return 1 + mv -f "/tmp/wechatpush/${2}.list" "/usr/share/wechatpush/api/${2}.list" + echo "$(date "+%Y-%m-%d %H:%M:%S") 【info】${2} API 列表更新成功" >>"${logfile}" elif [ "$1" == "down_oui" ]; then # 更新 MAC 信息列表 - [ `file_date "$oui_base"` -lt "86400" ] && return 2 - down_oui > /dev/null 2>&1 & -fi \ No newline at end of file + [ $(file_date "$oui_base") -lt 86400 ] && echo "$(date "+%Y-%m-%d %H:%M:%S") 【info】MAC 信息文件无需更新" >>"${logfile}" && exit 2 + down_oui >/dev/null 2>&1 & +fi diff --git a/root/usr/share/rpcd/acl.d/luci-app-wechatpush.json b/root/usr/share/rpcd/acl.d/luci-app-wechatpush.json index c2530187..233fc574 100644 --- a/root/usr/share/rpcd/acl.d/luci-app-wechatpush.json +++ b/root/usr/share/rpcd/acl.d/luci-app-wechatpush.json @@ -4,6 +4,7 @@ "read": { "file": { "/etc/init.d/wechatpush": [ "exec" ], + "/proc/net/arp": [ "read" ], "/usr/share/wechatpush/wechatpush": [ "exec" ], "/tmp/wechatpush/*": [ "read" ], "/usr/share/wechatpush/api/*": [ "read" ], diff --git a/root/usr/share/wechatpush/api/ipv6.list b/root/usr/share/wechatpush/api/ipv6.list index a996a67a..bfb1e93f 100644 --- a/root/usr/share/wechatpush/api/ipv6.list +++ b/root/usr/share/wechatpush/api/ipv6.list @@ -1,5 +1,3 @@ ipv6.ddnspod.com speed.neu6.edu.cn/getIP.php 6.ipw.cn -10086.cn/web-Center/commonservice/getUserIp.do --referer http://10086.cn -XPOST -https://www.taobao.com/help/getip.php -H 'authority: www.taobao.com' diff --git a/root/usr/share/wechatpush/wechatpush b/root/usr/share/wechatpush/wechatpush index fe3b0c16..c0dc2238 100755 --- a/root/usr/share/wechatpush/wechatpush +++ b/root/usr/share/wechatpush/wechatpush @@ -1,9 +1,9 @@ #!/bin/bash # 读取设置文件 -function get_config(){ +get_config() { while [[ "$*" != "" ]]; do - eval ${1}='`uci get wechatpush.config.$1`' 2>/dev/null + eval "${1}='$(uci get wechatpush.config.$1 2>/dev/null)'" shift done } @@ -13,7 +13,7 @@ read_json() { local json_key="$1" local json_path="$2" local output_file="$output_dir/${json_key}" - jq -r ."$json_key" "$json_path" > "$output_file" & + jq -r ."$json_key" "$json_path" >"$output_file" & } # 遍历输出目录,将文件内容保存到对应的变量 @@ -25,65 +25,73 @@ wait_and_cat() { local variable_value=$(cat "$file") eval "${variable_name}='${variable_value}'" done - rm "$output_dir"/* + if [ ! -f "${dir}/send.lock" ]; then + rm "$output_dir"/* + fi +} + +# 在调试模式下隐藏输出 +# 不能直接包裹 var=$(echo $ssh_command) 等命令,待完善 +silent_run() { + "$@" >/dev/null 2>&1 } # 初始化设置信息 -function read_config(){ +read_config() { get_config \ - "enable" "lite_enable" "device_name" "proxy_address" "sleeptime" "oui_data" "reset_regularly" "debuglevel" \ - "jsonpath" "sckey" "corpid" "userid" "agentid" "corpsecret" "mediapath" "wxpusher_apptoken" "wxpusher_uids" "wxpusher_topicIds" "pushplus_token" "tg_token" "chat_id" \ - "get_ipv4_mode" "ipv4_interface" "get_ipv6_mode" "ipv6_interface" "auto_update_ip_list" \ - "device_notification" "cpu_notification" "cpu_load_threshold" "temperature_threshold" \ - "client_usage" "client_usage_max" "client_usage_disturb" "client_usage_whitelist" \ - "login_notification" "login_max_num" "login_web_black" "login_ip_black_timeout" "login_ip_white_list" "port_knocking_enable" "login_ip_white_timeout" "login_port_white" "login_port_forward_list" \ - "crontab_regular_time" "crontab_interval_time" \ - "do_not_disturb_mode" "do_not_disturb_starttime" "do_not_disturb_endtime" "up_down_push_whitelist" "up_down_push_blacklist" "up_down_push_interface" "mac_online_list" "mac_offline_list" "cpu_threshold_duration" "cpu_notification_delay" "login_disturb" "login_notification_delay" "login_log_enable" \ - "up_timeout" "down_timeout" "timeout_retry_count" "only_timeout_push" "passive_mode" "thread_num" "soc_code" "server_host" "server_port" \ - "unattended_enable" "zerotier_helper" "unattended_only_on_disturb_time" "unattended_device_aliases" "network_disconnect_event" "unattended_autoreboot_mode" "autoreboot_system_uptime" "autoreboot_network_uptime" \ - "device_info_helper" "gateway_host_url" "gateway_info_url" "gateway_logout_url" "gateway_username_id" "gateway_password_id" "gateway_username" "gateway_password" "scan_ip_range" "device_info_helper_sleeptime" - - ( echo "$device_notification"|grep -q "online" ) && notification_online="true" - ( echo "$device_notification"|grep -q "offline" ) && notification_offline="true" - ( echo "$cpu_notification"|grep -q "load" ) && notification_load="true" - ( echo "$cpu_notification"|grep -q "temp" ) && notification_temp="true" - ( echo "$login_notification"|grep -q "web_logged" ) && web_logged="true" - ( echo "$login_notification"|grep -q "ssh_logged" ) && ssh_logged="true" - ( echo "$login_notification"|grep -q "web_login_failed" ) && web_login_failed="true" - ( echo "$login_notification"|grep -q "ssh_login_failed" ) && ssh_login_failed="true" - ( echo "$device_info_helper"|grep -q "gateway_info" ) && gateway_info_enable="true" + "enable" "lite_enable" "device_name" "proxy_address" "sleeptime" "oui_data" "reset_regularly" "debuglevel" \ + "jsonpath" "sckey" "corpid" "userid" "agentid" "corpsecret" "mediapath" "wxpusher_apptoken" "wxpusher_uids" "wxpusher_topicIds" "pushplus_token" "tg_token" "chat_id" \ + "get_ipv4_mode" "ipv4_interface" "get_ipv6_mode" "ipv6_interface" "auto_update_ip_list" \ + "device_notification" "cpu_notification" "cpu_load_threshold" "temperature_threshold" \ + "client_usage" "client_usage_max" "client_usage_disturb" "client_usage_whitelist" \ + "login_notification" "login_max_num" "login_web_black" "login_ip_black_timeout" "login_ip_white_list" "port_knocking_enable" "login_ip_white_timeout" "login_port_white" "login_port_forward_list" \ + "crontab_regular_time" "crontab_interval_time" \ + "do_not_disturb_mode" "do_not_disturb_starttime" "do_not_disturb_endtime" "up_down_push_whitelist" "up_down_push_blacklist" "up_down_push_interface" "mac_online_list" "mac_offline_list" "cpu_threshold_duration" "cpu_notification_delay" "login_disturb" "login_notification_delay" "login_log_enable" \ + "up_timeout" "down_timeout" "timeout_retry_count" "always_check_ip_list" "only_timeout_push" "passive_mode" "thread_num" "defaultSortColumn" "soc_code" "server_host" "server_port" \ + "unattended_enable" "zerotier_helper" "unattended_only_on_disturb_time" "unattended_device_aliases" "network_disconnect_event" "unattended_autoreboot_mode" "autoreboot_system_uptime" "autoreboot_network_uptime" \ + "device_info_helper" "gateway_host_url" "gateway_info_url" "gateway_logout_url" "gateway_username_id" "gateway_password_id" "gateway_username" "gateway_password" "scan_ip_range" "device_info_helper_sleeptime" + + (echo "$device_notification" | grep -q "online") && notification_online="true" + (echo "$device_notification" | grep -q "offline") && notification_offline="true" + (echo "$cpu_notification" | grep -q "load") && notification_load="true" + (echo "$cpu_notification" | grep -q "temp") && notification_temp="true" + (echo "$login_notification" | grep -q "web_logged") && web_logged="true" + (echo "$login_notification" | grep -q "ssh_logged") && ssh_logged="true" + (echo "$login_notification" | grep -q "web_login_failed") && web_login_failed="true" + (echo "$login_notification" | grep -q "ssh_login_failed") && ssh_login_failed="true" + (echo "$device_info_helper" | grep -q "gateway_info") && gateway_info_enable="true" for str_version in "wrtbwmon" "iputils-arping" "curl" "iw"; do - eval `echo ${str_version:0:2}"_version"`=`opkg list-installed|grep -w ^${str_version}|awk '{print $3}'` 2>/dev/null + eval $(echo ${str_version:0:2}"_version")=$(opkg list-installed | grep -w ^${str_version} | awk '{print $3}') 2>/dev/null done - ( opkg list-installed|grep -w -q ^firewall4 ) && nftables_version="true" - dir="/tmp/wechatpush/" && mkdir -p ${dir} && mkdir -p ${dir}/client - tempjsonpath="${dir}temp.json" + (opkg list-installed | grep -w -q "^firewall4") && nftables_version="true" + devices_json="${dir}/devices.json" + tmp_devices_json="${devices_json}.tmp" + tempjsonpath="${dir}/temp.json" ip_blacklist_path="/usr/share/wechatpush/api/ip_blacklist" - oui_base="${dir}oui_base.txt" - debuglevel=`echo "$debuglevel"` && [ -z "$debuglevel" ] && logfile="/dev/null" || logfile="${dir}wechatpush.log" - login_port_forward_list=`echo "$login_port_forward_list"|sed 's/ /\n/g'` 2>/dev/null - up_down_push_blacklist=`echo "$up_down_push_blacklist"|sed 's/ /\n/g'` 2>/dev/null - up_down_push_whitelist=`echo "$up_down_push_whitelist"|sed 's/ /\n/g'` 2>/dev/null - device_aliases=`cat /usr/share/wechatpush/api/device_aliases.list` 2>/dev/null - unattended_device_aliases=`echo "$unattended_device_aliases"|sed 's/ /\n/g'` 2>/dev/null - client_usage_whitelist=`echo "$client_usage_whitelist"|sed 's/ /\n/g'` 2>/dev/null - login_ip_white_list=`echo "$login_ip_white_list"|sed 's/ /\n/g'` 2>/dev/null + oui_base="${dir}/oui_base.txt" + debuglevel=$(echo "$debuglevel") && [ -z "$debuglevel" ] && logfile="/dev/null" || logfile="${dir}/wechatpush.log" + login_port_forward_list=$(echo "$login_port_forward_list" | sed 's/ /\n/g') 2>/dev/null + up_down_push_blacklist=$(echo "$up_down_push_blacklist" | sed 's/ /\n/g') 2>/dev/null + up_down_push_whitelist=$(echo "$up_down_push_whitelist" | sed 's/ /\n/g') 2>/dev/null + device_aliases_path="/usr/share/wechatpush/api/device_aliases.list" + always_check_ip_list=$(echo "$always_check_ip_list" | sed 's/ /\n/g') 2>/dev/null + unattended_device_aliases=$(echo "$unattended_device_aliases" | sed 's/ /\n/g') 2>/dev/null + client_usage_whitelist=$(echo "$client_usage_whitelist" | sed 's/ /\n/g') 2>/dev/null + login_ip_white_list=$(echo "$login_ip_white_list" | sed 's/ /\n/g') 2>/dev/null mark_mac_list="${mac_online_list} ${mac_offline_list}" - mark_mac_list=`echo "$mark_mac_list"|sed 's/ /\n/g'|sed 's/-/ /'` 2>/dev/null - ipv4_urllist=`cat /usr/share/wechatpush/api/ipv4.list` 2>/dev/null - ipv6_urllist=`cat /usr/share/wechatpush/api/ipv6.list` 2>/dev/null - User_Agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58" + mark_mac_list=$(echo "$mark_mac_list" | sed 's/ /\n/g' | sed 's/-/ /') 2>/dev/null + ipv4_urllist=$(cat /usr/share/wechatpush/api/ipv4.list) 2>/dev/null + ipv6_urllist=$(cat /usr/share/wechatpush/api/ipv6.list) 2>/dev/null [ -z "$get_ipv4_mode" ] && get_ipv4_mode=0 [ -z "$get_ipv6_mode" ] && get_ipv6_mode=0 [ -z "$sleeptime" ] && sleeptime="60" [ -z "$login_ip_black_timeout" ] && login_ip_black_timeout="86400" [ -z "$login_ip_white_timeout" ] && login_ip_white_timeout="600" - [ "$iw_version" ] && wlan_interface=`iw dev 2>/dev/null|grep Interface|awk '{print $2}'` >/dev/null 2>&1 + [ "$iw_version" ] && wlan_interface=$(iw dev 2>/dev/null | grep Interface | awk '{print $2}') >/dev/null 2>&1 [ -z "$server_port" ] && server_port="22" - cpu_notification_delay=$((${cpu_notification_delay} - ${cpu_threshold_duration})) - output_dir="${dir}json_output" + output_dir="${dir}/json_output" mkdir -p "$output_dir" - if ( echo "$lite_enable"|grep -q "content" ); then + if (echo "$lite_enable" | grep -q "content"); then str_title_start="" && str_title_end="" && str_splitline="" && str_linefeed="" && str_tab="" else read_json "str_title_start" "$jsonpath" @@ -100,298 +108,410 @@ function read_config(){ cron } +# 日志输出函数,待设置调试等级 +log_change() { + local message="$1" + echo "$(date "+%Y-%m-%d %H:%M:%S")" "${message}" >>"$logfile" +} + # 初始化 -function init(){ +init() { enable_detection - echo "---------------------------------------------------------------------------------------" >> ${logfile} - echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】start running..." >> ${logfile} + [ -f "$logfile" ] && local logrow=$(grep -c "" "$logfile") || local logrow="0" + [ "$logrow" -ne 0 ] && echo "---------------------------------------------------------------------------------------" >>${logfile} + log_change "【初始化】start running..." if [ -f "/usr/share/wechatpush/errlog" ]; then - cat /usr/share/wechatpush/errlog > ${logfile} - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】载入上次重启前日志" >> ${logfile} + cat /usr/share/wechatpush/errlog >${logfile} + log_change "【!!!】载入上次重启前日志" fi /usr/libexec/wechatpush-call "down_oui" - rm -f ${dir}fd1 ${dir}sheep_usage ${dir}old_sheep_usage ${dir}client_usage_aliases ${dir}old_client_usage_aliases /usr/share/wechatpush/errlog >/dev/null 2>&1 - [ ! -f "/usr/sbin/wrtbwmon" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】未安装 wrtbwmon ,流量统计不可用" >> ${logfile} - [ -z "$ip_version" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取依赖项 iputils-arping 版本号,请确认插件是否正常运行" >> ${logfile} - [ -z "$cu_version" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取依赖项 curl 版本号,请确认插件是否正常运行" >> ${logfile} - [ -z "${sckey}${tg_token}${pushplus_token}${corpid}${wxpusher_apptoken}${wxpusher_uids}${wxpusher_topicIds}" -a "${jsonpath}" != "/usr/share/wechatpush/api/diy.json" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】请填写正确的 key " >> ${logfile} && return 1 - local interfacelist=`getinterfacelist` && [ -z "$interfacelist" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】存在多个接口或配置错误,可能无法获取接口在线时间等信息,请确认插件是否正常运行" >> ${logfile} - [ -n "$notification_temp" ] && [ -n "$temperature_threshold" ] && local cpu_temp=`soc_temp` || local cpu_temp="null" - [ -z "$cpu_temp" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法读取设备温度,请检查命令" >> ${logfile} - [ -n "$notification_load" ] && [ -n "$cpu_load_threshold" ] && local cpu_fuzai=`cat /proc/loadavg|awk '{print $1}'` 2>/dev/null || local cpu_fuzai="null" - [ -z "$cpu_fuzai" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法读取设备负载,请检查命令" >> ${logfile} + rm -f ${dir}/fd1 ${dir}/sheep_usage ${dir}/old_sheep_usage ${dir}/client_usage_aliases ${dir}/old_client_usage_aliases /usr/share/wechatpush/errlog >/dev/null 2>&1 + [ ! -f "/usr/sbin/wrtbwmon" ] && log_change "【info】未安装 wrtbwmon ,流量统计不可用" + [ -z "$ip_version" ] && log_change "【!!!】无法获取依赖项 iputils-arping 版本号,请确认插件是否正常运行" + [ -z "$cu_version" ] && log_change "【!!!】无法获取依赖项 curl 版本号,请确认插件是否正常运行" + [ -z "${sckey}${tg_token}${pushplus_token}${corpid}${wxpusher_apptoken}${wxpusher_uids}${wxpusher_topicIds}" -a "${jsonpath}" != "/usr/share/wechatpush/api/diy.json" ] && log_change "【!!!】请填写正确的 key" && return 1 + local interfacelist=$(getinterfacelist) && [ -z "$interfacelist" ] && log_change "【!!!】存在多个接口或配置错误,可能无法获取接口在线时间等信息,请确认插件是否正常运行" + [ -n "$notification_temp" ] && [ -n "$temperature_threshold" ] && local cpu_temp=$(soc_temp) || local cpu_temp="null" + [ -z "$cpu_temp" ] && log_change "【!!!】无法读取设备温度,请检查命令" + [ -n "$notification_load" ] && [ -n "$cpu_load_threshold" ] && local cpu_fuzai=$(cat /proc/loadavg | awk '{print $1}') 2>/dev/null || local cpu_fuzai="null" + [ -z "$cpu_fuzai" ] && log_change "【!!!】无法读取设备负载,请检查命令" [ -n "$login_web_black" ] && [ "$login_web_black" -eq "1" ] && init_ip_black "ipv4" [ -n "$login_web_black" ] && [ "$login_web_black" -eq "1" ] && init_ip_black "ipv6" [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] && init_ip_white "ipv4" [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] && init_ip_white "ipv6" - tmp_ip_list=`echo "$login_ip_white_list"|grep -v "^$"|sort -u` + tmp_ip_list=$(echo "$login_ip_white_list" | grep -v "^$" | sort -u) while IFS= read -r tmp_ip; do [ -n "$tmp_ip" ] && add_ip_white "$tmp_ip" "0" - done <<< "$tmp_ip_list" + done <<<"$tmp_ip_list" set_ip_black return 0 } -# 推送 -function diy_send(){ - ( ! echo "$lite_enable"|grep -q "content" ) && ( ! echo "$lite_enable"|grep -q "nowtime" ) && local nowtime=`date "+%Y-%m-%d %H:%M:%S"` - ! jq -r '.' ${3} >/dev/null 2>&1 && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】json 文件格式错误,这不是一个标准的 json 文件,请检查 ${3} 文件是否有特殊符号未转义或语法错误" >> ${logfile} && return 1 - local diyurl=`jq -r .url ${3}` && local diyurl=`eval echo ${diyurl}` - local type=`jq -r '.type' ${3}` && local type=`eval echo ${type}` - local data=`jq -r '.data' ${3}` && local data=`eval echo ${data}` - local content_type=`jq -r '.content_type' ${3}` - ! jq "$type" ${3} > ${tempjsonpath} && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】type:{ } 字段转义变量后格式错误,请检查 type:{ } 字段内是否有特殊符号未转义或语法错误" >> ${logfile} && return 1 - - [ -n "$proxy_address" ] && local proxy_cmd="-x $proxy_address" - curl $proxy_cmd --connect-timeout 30 -m 60 --retry 1 -X POST -H "$content_type" -d "$data" "${diyurl}" - local RETVAL=$? - if [ -n "$4" ] || [ $RETVAL -ne 0 ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】网络错误或 URL 错误,推送失败,curl 返回值为 ${RETVAL},请参考如下调试信息进行排查" >> ${logfile} - echo '{"url":"'${diyurl}'","content_type":"'${content_type}'","type":'`jq "$type" ${3}`'}' > ${dir}debug_send_json - echo -e "${send_title}" "${send_content}" > ${dir}debug_send_content - cat ${tempjsonpath} > ${dir}debug_send_data - ! jq -r '.' ${dir}debug_send_json && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】转义变量后格式错误,请检查 ${dir}debug_send_json 字段内是否有特殊符号未转义或语法错误" >> ${logfile} - echo "`date "+%Y-%m-%d %H:%M:%S"` 【debug】json 文件已保存至:${dir}debug_send_json" >> ${logfile} - echo "`date "+%Y-%m-%d %H:%M:%S"` 【debug】推送内容预览文件保存至:${dir}debug_send_content" >> ${logfile} - echo "`date "+%Y-%m-%d %H:%M:%S"` 【debug】如果收不到信息,请检查 ${dir}debug_send_data 文件,或使用下列命令手动测试返回值 (可能需要关闭日志自动刷新方便选中)" >> ${logfile} - echo "`date "+%Y-%m-%d %H:%M:%S"` 【debug】"'curl -X POST -H "'$content_type'" -d "@'${dir}debug_send_data'" "'${diyurl}'" ' >> ${logfile} - [ -n "$4" ] && [ $RETVAL -eq 0 ] && return 0 - return 1 - else - return 0 - fi -} - # 清理临时文件 -function deltemp(){ - unset title content ipAddress_logrow online_list online_mac mac_online_status gateway_iplist - rm -f ${dir}title ${dir}content ${dir}tmp_downlist ${dir}send_enable.lock ${tempjsonpath} ${dir}cookies.txt ${dir}tmp_sort_file >/dev/null 2>&1 - [ ! -f ${dir}ipAddress ] && rm -f ${dir}client/* >/dev/null 2>&1 - LockFile unlock - [ -f ${logfile} ] && local logrow=$(grep -c "" ${logfile}) || local logrow="0" - [ $logrow -gt 500 ] && sed -i '1,100d' ${logfile} && echo "`date "+%Y-%m-%d %H:%M:%S"` 【清理】日志超出上限,删除前 100 条" >> ${logfile} +deltemp() { + unset title content gateway_iplist + rm -f "${dir}/title" "${dir}/content" "${dir}/send_enable.lock" "${dir}/send.lock" "${tempjsonpath}" "${dir}/cookies.txt" >/dev/null 2>&1 + silent_run LockFile unlock + [ -f "$logfile" ] && local logrow=$(grep -c "" "$logfile") || local logrow="0" + [ "$logrow" -gt 500 ] && sed -i '1,100d' "$logfile" && log_change "【清理】日志超出上限,删除前 100 条" + # 检查 JSON 文件格式是否有效 + if ! jq empty "$devices_json" >/dev/null 2>&1 || ! jq -e 'has("address") and has("devices") and has("disks")' "$devices_json" >/dev/null 2>&1; then + [ -f "$devices_json" ] && log_change "【!!!】设备列表文件格式错误,需重新初始化,原文件已保存至 ${devices_json}.err" && mv "$devices_json" "${devices_json}.err" + fi + # 如果设备列表文件为空,初始化 + [ ! -s "$devices_json" ] && echo '{"address": [], "devices": [], "disks": []}' >"$devices_json" } # 检测程序开关 -function enable_detection(){ - [ ! "$1" ] && local time_n=1 - for i in `seq 1 $time_n`; do - get_config enable;[ -z "$enable" ] || [ "$enable" -eq "0" ] && exit || sleep 1 +enable_detection() { + [ -z "$1" ] && local time_n=1 + for i in $(seq 1 $time_n); do + get_config enable + [ -z "$enable" ] || [ "$enable" -eq "0" ] && exit || sleep 1 done + unset i } # 获取 ip -function getip(){ - [ ! "$1" ] && return +getip() { + [ -z "$1" ] && return # 从接口获取 IPv4 - if [ $1 == "wanipv4" ] ;then - [ ! -z "$ipv4_interface" ] && local wanIP=$(/sbin/ifconfig ${ipv4_interface}|awk '/inet addr/ {print $2}'|awk -F: '{print $2}'|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') - [ -z "$ipv4_interface" ] && local wanIP=$(getinterfacelist|grep '\"address\"'|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') + if [ $1 == "wanipv4" ]; then + [ -n "$ipv4_interface" ] && local wanIP=$(/sbin/ifconfig ${ipv4_interface} | awk '/inet addr/ {print $2}' | awk -F: '{print $2}' | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') + [ -z "$ipv4_interface" ] && local wanIP=$(getinterfacelist | grep '\"address\"' | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') echo "$wanIP" # 从 URL 获取 IPv4 - elif [ $1 == "hostipv4" ] ;then - local url_number=`echo "$ipv4_urllist"|wc -l` - local rand_number=`rand 1 $url_number` - function get_hostipv4() - { - local ipv4_URL=`echo "$ipv4_urllist"| sed -n "${1}p"|sed -e 's/\r//g'` - [ ! -z "$ipv4_interface" ] && local tmp_hostIP=$(eval "curl --connect-timeout 2 -m 2 -k -s -4 --interface ${ipv4_interface} -m 5 ${ipv4_URL}") || local tmp_hostIP=$(eval "curl --connect-timeout 2 -m 2 -k -s -4 -m 5 ${ipv4_URL}") - [ -z "$tmp_hostIP" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】IP 获取失败,当前使用的 API 为 $ipv4_URL & ${ipv4_interface}" >> ${logfile} - local tmp_hostIP=`echo $tmp_hostIP|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'|head -n1` + elif [ $1 == "hostipv4" ]; then + local url_number=$(echo "$ipv4_urllist" | wc -l) + local rand_number=$(rand 1 $url_number) + get_hostipv4() { + local ipv4_URL=$(echo "$ipv4_urllist" | sed -n "${1}p" | sed -e 's/\r//g') + [ -n "$ipv4_interface" ] && local tmp_hostIP=$(eval "curl --connect-timeout 2 -m 2 -k -s -4 --interface ${ipv4_interface} -m 5 ${ipv4_URL}") || local tmp_hostIP=$(eval "curl --connect-timeout 2 -m 2 -k -s -4 -m 5 ${ipv4_URL}") + [ -z "$tmp_hostIP" ] && log_change "【info】IP 获取失败,当前使用的 API 为 $ipv4_URL & ${ipv4_interface}" + local tmp_hostIP=$(echo $tmp_hostIP | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | head -n1) echo "{\"IP\":\"${tmp_hostIP}\", \"URL\":\"${ipv4_URL}\"}" } - local hostIP=`get_hostipv4 ${rand_number}` - [ -z `echo $hostIP|jq -r '.IP'` ] && local rand_number=`expr $rand_number + 1` && [ $rand_number -gt $url_number ] && local rand_number=1;[ -z `echo $hostIP|jq -r '.IP'` ] && local hostIP=`get_hostipv4 ${rand_number}` - [ -z `echo $hostIP|jq -r '.IP'` ] && local rand_number=`expr $rand_number + 1` && [ $rand_number -gt $url_number ] && local rand_number=1;[ -z `echo $hostIP|jq -r '.IP'` ] && local hostIP=`get_hostipv4 ${rand_number}` - [ -n "$list_auto_up" ] && [ "$list_auto_up" -eq "1" ] && [ -z `echo $hostIP|jq -r '.IP'` ] && /usr/libexec/wechatpush-call "auto_update_ip_list" "ipv4" - [ -z `echo $hostIP|jq -r '.IP'` ] && ipv4_urllist=`cat /usr/share/wechatpush/api/ipv4.list` 2>/dev/null + local hostIP=$(get_hostipv4 ${rand_number}) + [ -z $(echo $hostIP | jq -r '.IP') ] && local rand_number=$(expr $rand_number + 1) && [ $rand_number -gt $url_number ] && local rand_number=1 + [ -z $(echo $hostIP | jq -r '.IP') ] && local hostIP=$(get_hostipv4 ${rand_number}) + [ -z $(echo $hostIP | jq -r '.IP') ] && local rand_number=$(expr $rand_number + 1) && [ $rand_number -gt $url_number ] && local rand_number=1 + [ -z $(echo $hostIP | jq -r '.IP') ] && local hostIP=$(get_hostipv4 ${rand_number}) + [ -n "$list_auto_up" ] && [ "$list_auto_up" -eq "1" ] && [ -z $(echo $hostIP | jq -r '.IP') ] && /usr/libexec/wechatpush-call "auto_update_ip_list" "ipv4" + [ -z $(echo $hostIP | jq -r '.IP') ] && ipv4_urllist=$(cat /usr/share/wechatpush/api/ipv4.list) 2>/dev/null echo $hostIP # 从接口获取 IPv6 - elif [ $1 == "wanipv6" ] ;then - [ ! -z "$ipv6_interface" ] && local wanIPv6=$(ip -6 addr show dev ${ipv6_interface}|grep "inet6"|grep "global dynamic noprefixroute"|awk '{print $2, $4}'|sort -k3 -n|head -n 1|awk '{print $1}'|grep -oE "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}") - [ -z "$ipv6_interface" ] && local wanIPv6=$(ip -6 addr show|grep "inet6"|grep "global dynamic noprefixroute"|awk '{print $2, $4}'|sort -k3 -n|head -n 1|awk '{print $1}'|grep -oE "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}") + elif [ $1 == "wanipv6" ]; then + [ -n "$ipv6_interface" ] && local wanIPv6=$(ip -6 addr show dev ${ipv6_interface} | grep "inet6" | grep "global dynamic noprefixroute" | awk '{print $2}' | grep -Ev "^(fc|fd)" | tail -n 1 | grep -oE "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}") + [ -z "$ipv6_interface" ] && local wanIPv6=$(ip -6 addr show | grep "inet6" | grep "global dynamic noprefixroute" | awk '{print $2}' | grep -Ev "^(fc|fd)" | tail -n 1 | grep -oE "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}") echo "$wanIPv6" # 从 URL 获取 IPv6 - elif [ $1 == "hostipv6" ] ;then - local urlv6_number=`echo "$ipv6_urllist"|wc -l` - local rand_numberv6=`rand 1 $urlv6_number` - function get_hostipv6() - { - local ipv6_URL=`echo "$ipv6_urllist"| sed -n "${1}p"|sed -e 's/\r//g'` - [ ! -z "$ipv6_interface" ] && local tmp_hostIPv6=$(eval "curl --connect-timeout 2 -m 2 -k -s -6 --interface ${ipv6_interface} -m 5 ${ipv6_URL}") || local tmp_hostIPv6=$(eval "curl --connect-timeout 2 -m 2 -k -s -6 -m 5 ${ipv6_URL}") - [ -z "$tmp_hostIPv6" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】IP 获取失败,当前使用的 API 为 $ipv6_URL & ${ipv6_interface}" >> ${logfile} - local tmp_hostIPv6=`echo $tmp_hostIPv6|grep -oE "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}"|head -n1` + elif [ $1 == "hostipv6" ]; then + local urlv6_number=$(echo "$ipv6_urllist" | wc -l) + local rand_numberv6=$(rand 1 $urlv6_number) + get_hostipv6() { + local ipv6_URL=$(echo "$ipv6_urllist" | sed -n "${1}p" | sed -e 's/\r//g') + [ -n "$ipv6_interface" ] && local tmp_hostIPv6=$(eval "curl --connect-timeout 2 -m 2 -k -s -6 --interface ${ipv6_interface} -m 5 ${ipv6_URL}") || local tmp_hostIPv6=$(eval "curl --connect-timeout 2 -m 2 -k -s -6 -m 5 ${ipv6_URL}") + [ -z "$tmp_hostIPv6" ] && log_change "【info】IP 获取失败,当前使用的 API 为 $ipv6_URL & ${ipv6_interface}" + local tmp_hostIPv6=$(echo $tmp_hostIPv6 | grep -oE "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}" | head -n1) echo "{\"IP\":\"${tmp_hostIPv6}\", \"URL\":\"${ipv6_URL}\"}" } - local hostIPv6=`get_hostipv6 ${rand_numberv6}` - [ -z `echo $hostIPv6|jq -r '.IP'` ] && local rand_numberv6=`expr $rand_numberv6 + 1` && [ $rand_numberv6 -gt $urlv6_number ] && local rand_numberv6=1;[ -z `echo $hostIPv6|jq -r '.IP'` ] && local hostIPv6=`get_hostipv6 ${rand_numberv6}` - [ -z `echo $hostIPv6|jq -r '.IP'` ] && local rand_numberv6=`expr $rand_numberv6 + 1` && [ $rand_numberv6 -gt $urlv6_number ] && local rand_numberv6=1;[ -z `echo $hostIPv6|jq -r '.IP'` ] && local hostIPv6=`get_hostipv6 ${rand_numberv6}` - [ -n "$list_auto_up" ] && [ "$list_auto_up" -eq "1" ] && [ -z `echo $hostIP|jq -r '.IP'` ] && /usr/libexec/wechatpush-call "auto_update_ip_list" "ipv6" - [ -z `echo $hostIPv6|jq -r '.IP'` ] && ipv4_urllist=`cat /usr/share/wechatpush/api/ipv6.list` 2>/dev/null + local hostIPv6=$(get_hostipv6 ${rand_numberv6}) + [ -z $(echo $hostIPv6 | jq -r '.IP') ] && local rand_numberv6=$(expr $rand_numberv6 + 1) && [ $rand_numberv6 -gt $urlv6_number ] && local rand_numberv6=1 + [ -z $(echo $hostIPv6 | jq -r '.IP') ] && local hostIPv6=$(get_hostipv6 ${rand_numberv6}) + [ -z $(echo $hostIPv6 | jq -r '.IP') ] && local rand_numberv6=$(expr $rand_numberv6 + 1) && [ $rand_numberv6 -gt $urlv6_number ] && local rand_numberv6=1 + [ -z $(echo $hostIPv6 | jq -r '.IP') ] && local hostIPv6=$(get_hostipv6 ${rand_numberv6}) + [ -n "$list_auto_up" ] && [ "$list_auto_up" -eq "1" ] && [ -z $(echo $hostIP | jq -r '.IP') ] && /usr/libexec/wechatpush-call "auto_update_ip_list" "ipv6" + [ -z $(echo $hostIPv6 | jq -r '.IP') ] && ipv4_urllist=$(cat /usr/share/wechatpush/api/ipv6.list) 2>/dev/null echo $hostIPv6 fi } # 获取接口信息 -function getinterfacelist(){ - [ `ubus list|grep -w -i "network.interface.wan"|wc -l` -ge "1" ] && ubus call network.interface.wan status && return - [ ! -z "$ipv4_interface" ] && local device_name=$ipv4_interface || [ ! -z "$ipv6_interface" ] && local device_name=$ipv6_interface - [ ! -z "$device_name" ] && local interface_name=`ubus call network.interface dump | jq -r --arg intf "$device_name" '.interface[] | select(.device == $intf and (.interface | endswith("6") | not)) | .interface'` - [ -z "$interface_name" ] && local interface_name=`ubus list | grep -i "network.interface." | grep -v "loopback" | grep -v -i "wan6" | grep -v -i "lan6" | grep -v -i "ipsec.*" | grep -v -i "VPN.*" | grep -v -i "DOCKER.*" | awk -F '.' '{print $3}'` - [ `echo "${interface_name}" |wc -l` -eq "1" ] && ubus call network.interface.${interface_name} status && return +getinterfacelist() { + [ $(ubus list | grep -w -i "network.interface.wan" | wc -l) -ge "1" ] && ubus call network.interface.wan status || ubus call network.interface.lan status && return + [ -n "$ipv4_interface" ] && local device_name=$ipv4_interface || [ -n "$ipv6_interface" ] && local device_name=$ipv6_interface + [ -n "$device_name" ] && local interface_name=$(ubus call network.interface dump | jq -r --arg intf "$device_name" '.interface[] | select(.device == $intf and (.interface | endswith("6") | not)) | .interface') + [ -z "$interface_name" ] && local interface_name=$(ubus list | grep -i "network.interface." | grep -v "loopback" | grep -v -i "wan6" | grep -v -i "wan_6" | grep -v -i "lan6" | grep -v -i "ipsec.*" | grep -v -i "VPN.*" | grep -v -i "DOCKER.*" | awk -F '.' '{print $3}' | head -n1) + ubus call network.interface.${interface_name} status && return } # 获取接口在线时间 -function getinterfaceuptime(){ - getinterfacelist|awk -F'\"uptime\": ' '/uptime/ { gsub(/,/, "", $2); print $2 }' +getinterfaceuptime() { + getinterfacelist | awk -F'\"uptime\": ' '/uptime/ { gsub(/,/, "", $2); print $2 }' +} + +# 处理并检查 tmp_name 的通用函数 +process_and_check() { + [ -z "$1" ] && return 1 + local value=$(echo "$1" | tr -d '\n\r' | awk '$1=$1' | sed 's/_/ /g' | grep -v "^$" | sort -u | head -n1) + [ "$value" == "unknown" ] && value="" + [ -n "$value" ] && echo "$value" && return 0 + return 1 +} + +# 查询 NetBIOS +nmblookup_timeout() { + local ip="$1" + local query_type="$2" # "name" or "mac" + local max_wait_time="$3" + local result="" + [ -z "$ip" ] && return 1 + [ -z "$query_type" ] && return 1 + [ -z "$max_wait_time" ] && return 1 + + { + if [ "$query_type" == "name" ]; then + result=$(nmblookup -A ${ip} 2>/dev/null | awk '/<00>/ && !// {print $1}') + elif [ "$query_type" == "mac" ]; then + result=$(nmblookup -A ${ip} 2>/dev/null | grep -oE 'MAC\s+Address\s+=\s+([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})' | awk '{print $NF}') + fi + echo "$result" + } & + local nmblookup_pid=$! + + # 等待最多5秒,检查结果是否已经获取 + local wait_time=0 + while [ $wait_time -lt $max_wait_time ]; do + # 检查子进程是否已完成 + if ! kill -0 $nmblookup_pid >/dev/null 2>&1; then + result=$(wait $nmblookup_pid) # 获取子进程的输出 + break + fi + sleep 1 + wait_time=$((wait_time + 1)) + done + + if kill -0 $nmblookup_pid >/dev/null 2>&1; then + kill $nmblookup_pid + fi + + echo "$result" } # 查询 MAC 地址 -function getmac(){ +getmac() { + local ip="$1" + local tmp_mac + [ -z "$ip" ] && return 1 + + # 某些路由器中继模式,会进行 MAC 克隆,如果是重复值,全都重置为 unknown + local mac_count=$(jq -r --arg mac "$tmp_mac" '.devices[] | select(.mac == $mac) | .mac' "$devices_json" | wc -l) + [ "$mac_count" -gt 1 ] && jq --arg mac "$tmp_mac" --arg new_mac "unknown" '.devices |= map(if .mac == $mac then .mac = $new_mac else . end)' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" + # 已保存的 MAC - [ -f "${dir}ipAddress" ] && local tmp_mac=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $2}'|grep -v "^$"` - ( echo "$tmp_mac"|grep -q "unknown" ) && unset tmp_mac # 为 unknown 时重新读取 - [ -n "$tmp_mac" ] && [ "$(grep -w -i "${tmp_mac}" "${dir}ipAddress" | wc -l)" -gt "1" ] && unset tmp_mac # 某些路由器中继模式,会进行 MAC 克隆,重复值重新读取 - [ -f "${dir}tmp_downlist" ] && [ -z "$tmp_mac" ] && local tmp_mac=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` + tmp_mac=$(jq -r --arg ip "$ip" '.devices[] | select(.ip == $ip) | .mac' "$devices_json") + [ -z "$force" ] && process_and_check "$tmp_mac" && return + # DHCP - ( echo "$tmp_mac"|grep -q "unknown" ) && unset tmp_mac # 为 unknown 时重新读取 - [ -f "/tmp/dhcp.leases" ] && [ -z "$tmp_mac" ] && local tmp_mac=`cat /tmp/dhcp.leases|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` - # arp - [ -z "$tmp_mac" ] && local tmp_mac=`cat /proc/net/arp|grep "0x2\|0x6"|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` + [ -f "/tmp/dhcp.leases" ] && tmp_mac=$(grep -w "${ip}" "/tmp/dhcp.leases" | awk '{print $2}') + process_and_check "$tmp_mac" && return + + # ARP + tmp_mac=$(cat /proc/net/arp | grep "0x2\|0x6" | grep -w "${ip}" | awk '{print $4}') + process_and_check "$tmp_mac" && return + + # 因 NetBIOS 查询时间过长,跳过离线设备 + jq -e --arg ip "$ip" '.devices[] | select(.ip == $ip and .status == "online") | .ip' "$devices_json" >/dev/null && tmp_mac=$(nmblookup_timeout "${ip}" "mac" "5") + process_and_check "$tmp_mac" && return - [ -z "$tmp_mac" ] && local tmp_mac="unknown" - echo $tmp_mac |tr -d '\n\r' + # 默认返回 unknown + echo "unknown" } # 查询主机名 -function getname(){ +getname() { + local ip="$1" + local mac="$2" + local tmp_name + local oui_name + [ -z "$ip" ] && return 1 + [ -z "$mac" ] && return 1 + # 自定义备注 - local tmp_name=`echo "$device_aliases"|grep -i -w "^${1}\|^${2}"|awk '{ for(i=2; i<=NF; i++) printf $i " "; print "" }'|grep -v "^$"|sort -u|head -n1` - [ -n "$tmp_name" ] && echo "$tmp_name" | tr -d '\n\r' | awk '$1=$1' | sed 's/_/ /g' | sort -u | head -n1 && return - echo "$2" | grep -q -w "unknown\|*" && echo "unknown" && return # 当 MAC 获取失败时先不要尝试获取主机名 - # 已保存的 主机名 - [ -f "${dir}ipAddress" ] && [ -z "$tmp_name" ] && local tmp_name=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` - echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 - [ -z "$tmp_name" ] && [ -f "${dir}tmp_downlist" ] && local tmp_name=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` + tmp_name=$(cat "$device_aliases_path" 2>/dev/null | grep -i -Ew "^${ip}|^${mac}" | awk '{for(i=2; i<=NF; i++) printf $i " "; print ""}') + process_and_check "$tmp_name" && return + + # MAC 获取失败时直接返回 unknown,优先级不能最高,自定义备注可以被 IP 匹配 + echo "$mac" | grep -q -w "unknown\|*" && echo "unknown" && return + + # 已保存的主机名 + tmp_name=$(jq -r --arg ip "$ip" --arg mac "$mac" '.devices[] | select(.ip == $ip and .mac == $mac) | .name' "$devices_json") + [ -n "$oui_name" ] && [ -n "$tmp_name" ] && [ "$oui_name" == "$tmp_name" ] && tmp_name="" + process_and_check "$tmp_name" && return + # 静态地址备注名 - echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 - [ -z "$tmp_name" ] && dhcp_config=`uci show dhcp|grep "ip\|mac\|name"` - if [ -n "$dhcp_config" ]; then - dhcp_ip_n=$(echo "$dhcp_config" | grep -w ^dhcp.@${dhcp_config_str}.*ip=.${1} | sed -nr 's#^dhcp.(.*).ip.*#\1#gp' 2>/dev/null) - [ ! -z "$dhcp_ip_n" ] && [ -z "$tmp_name" ] && tmp_name=$(uci get dhcp.${dhcp_ip_n}.name 2>/dev/null) - [ -z "$tmp_name" ] && dhcp_mac_n=$(echo "$dhcp_config" | grep -i ^dhcp.@${dhcp_config_str}.*mac=.${2} | sed -nr 's#^dhcp.(.*).mac.*#\1#gp' 2>/dev/null) - [ ! -z "$dhcp_mac_n" ] && [ -z "$tmp_name" ] && tmp_name=$(uci get dhcp.${dhcp_mac_n}.name 2>/dev/null) - fi + local dhcp_config=$(uci show dhcp | grep -i -w "${ip}\|${mac}" | sed -n 's/\(dhcp\..*\)\..*/\1/p') + [ -n "$dhcp_config" ] && tmp_name=$(uci get ${dhcp_config}.name) + process_and_check "$tmp_name" && return + # DHCP - echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 - [ -z "$tmp_name" ] && [ -f "/tmp/dhcp.leases" ] && local tmp_name=`cat /tmp/dhcp.leases|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` + [ -f "/tmp/dhcp.leases" ] && tmp_name=$(grep -w "${ip}" /tmp/dhcp.leases | awk '{print $4}') + process_and_check "$tmp_name" && return + # 光猫 - echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 - [ -z "$tmp_name" ] && [ -f "${dir}gateway_info" ] && gatewayinfo=`cat ${dir}gateway_info` && local tmp_name=$(cut_str "$(echo "$gatewayinfo"|grep -w ${1}|awk '{print $2}'|sort -u|head -n1)" "30") - # MAC设备信息数据库 - echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 - [ -z "$tmp_name" ] && [ -f "$oui_base" ] && local tmp_name=$(cat $oui_base|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp') - [ -z "$tmp_name" ] && [ ! -z "$oui_data" ] && [ "$oui_data" -eq "4" ] && local tmp_name=$(curl -sS "https://standards-oui.ieee.org/oui/oui.txt"|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp') - - [ -z "$tmp_name" ] && local tmp_name="unknown" - echo "$tmp_name" | tr -d '\n\r' | awk '$1=$1' | sed 's/_/ /g' | sort -u | head -n1 + [ -f "${dir}/gateway_info" ] && tmp_name=$(cut_str "$(grep -w "${ip}" "${dir}/gateway_info" | awk '{print $2}')" "30") + process_and_check "$tmp_name" && return + + # 因 NetBIOS 查询时间过长,跳过离线设备 + # 设备刚上线时设备信息未保存,json 中无法检查在线状态,应避免使用 oui 数据库,否则无法使用 NetBIOS 查询 + if jq -e --arg ip "$ip" '.devices[] | select(.ip == $ip and .status == "online") | .ip' "$devices_json" >/dev/null; then + tmp_name=$(nmblookup_timeout "${ip}" "name" "5") + process_and_check "$tmp_name" && return + + # MAC 设备信息数据库 + [ -f "$oui_base" ] && oui_name=$(grep -i "$(echo "$mac" | cut -c 1,2,4,5,7,8)" "$oui_base" | sed -nr 's#^.*16)..(.*)#\1#gp') + [ -n "$oui_data" ] && [ "$oui_data" -eq "4" ] && oui_name=$(curl -sS "https://standards-oui.ieee.org/oui/oui.txt" | grep -i "$(echo "$mac" | cut -c 1,2,4,5,7,8)" | sed -nr 's#^.*16)..(.*)#\1#gp') + tmp_name="$oui_name" + process_and_check "$tmp_name" && return + fi + + echo "unknown" +} + +# 查询 http/https 端口 +check_http_access() { + local ip="$1" + [ -z "$ip" ] && return 1 + + curl --head --silent --fail --connect-timeout 3 "http://$ip" >/dev/null + [ $? -eq 0 ] && echo "http" && return + + curl --head --silent --fail --connect-timeout 3 "https://$ip" >/dev/null + [ $? -eq 0 ] && echo "https" && return + + return 1 +} + +# 获取文件最后修改时间(距离现在过去了多少秒) +file_date() { + local file_dir="$1" + local datetime=$(date -r "${file_dir}" +%s 2>/dev/null || echo "0") + expr $(date +%s) - ${datetime} } # 从光猫处获取设备信息 -function getgateway(){ +getgateway() { [ -z "$gateway_info_enable" ] && return - last_getgateway_time=$(date -r ${dir}gateway_info +%s 2>/dev/null) || last_getgateway_time=0 - [ "$1" ] && [ "$1" == "reboot" ] && last_getgateway_time=0 - if [ `expr $(date +%s) - $last_getgateway_time` -gt "$device_info_helper_sleeptime" ]; then + [ "$1" ] && [ "$1" == "reboot" ] && last_getgateway_time="$device_info_helper_sleeptime" || last_getgateway_time=$(file_date "${dir}/gateway_info") + + if [ "$last_getgateway_time" -ge "$device_info_helper_sleeptime" ]; then # 登录 - local loginfo=`curl -s -L "${gateway_host_url}" -c ${dir}cookies.txt -d "${gateway_username_id}=${gateway_username}&${gateway_password_id}=${gateway_password}"` 2>/dev/null - [ ! -z "$loginfo" ] && local mytoken=$(echo $loginfo |sed 's/{/\n/g' | grep token |awk '/realRestart/{print $2}'| sed $'s/\'//g') + local loginfo=$(curl -s -L "${gateway_host_url}" -c ${dir}/cookies.txt -d "${gateway_username_id}=${gateway_username}&${gateway_password_id}=${gateway_password}") 2>/dev/null + [ -n "$loginfo" ] && local mytoken=$(echo $loginfo | sed 's/{/\n/g' | grep token | awk '/realRestart/{print $2}' | sed $'s/\'//g') # 获取 - [ ! -z "$mytoken" ] && local get_gateway=`curl -s -b ${dir}cookies.txt "${gateway_info_url}" -d 'token='$mytoken | jq '.[] | iterables| "\(.ip) \(.devName) \(.model)"'|sed 's/unknown//g'|sed 's/ / /g'|sed 's/ /_/g'|sed 's/_/ /'|sed 's/\"//g'` + [ -n "$mytoken" ] && local get_gateway=$(curl -s -b ${dir}/cookies.txt "${gateway_info_url}" -d 'token='$mytoken | jq '.[] | iterables| "\(.ip) \(.devName) \(.model)"' | sed 's/unknown//g' | sed 's/ / /g' | sed 's/ /_/g' | sed 's/_/ /' | sed 's/\"//g') # 重启 [ "$1" ] && [ "$1" == "reboot" ] && curl -s -b ${dir}/cookies.txt "${gateway_host_url}/admin/reboot" -d "token=$mytoken" >/dev/null 2>&1 # 注销 - [ ! -z "$get_gateway" ] && [ ! -z "$gateway_logout_url" ] && curl -s -b ${dir}cookies.txt "${gateway_logout_url}" -d 'token='$mytoken 2>/dev/null - [ -z "$get_gateway" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】获取光猫信息失败,可能当前用户未注销或设置错误" >> ${logfile} + [ -n "$get_gateway" ] && [ -n "$gateway_logout_url" ] && curl -s -b ${dir}/cookies.txt "${gateway_logout_url}" -d 'token='$mytoken 2>/dev/null + [ -z "$get_gateway" ] && log_change "【info】获取光猫信息失败,可能当前用户未注销或设置错误" # 保存信息 - [ ! -z "$get_gateway" ] && echo "$get_gateway" > ${dir}gateway_info - gateway_iplist=`echo "${get_gateway}"|awk '{print $1}'` + [ -n "$get_gateway" ] && echo "$get_gateway" >"${dir}/gateway_info" + gateway_iplist=$(echo "${get_gateway}" | awk '{print $1}') else unset gateway_iplist fi } # 扫描范围内 IP -function scanlocalip(){ +scanlocalip() { [ -z "$scan_ip_range" ] && return [ -z "$last_scan_ip_time" ] && last_scan_ip_time=0 - if [ `expr $(date +%s) - $last_scan_ip_time` -gt "$device_info_helper_sleeptime" ]; then - start_ip=$(echo "$scan_ip_range" | cut -d "-" -f 1) - end_ip=$(echo "$scan_ip_range" | cut -d "-" -f 2) - - i=$(echo "$start_ip" | awk -F '.' '{print $NF}') - end_i=$(echo "$end_ip" | awk -F '.' '{print $NF}') - while [ "$i" -le "$end_i" ]; do - if ping -c 1 "${start_ip%.*}.$i" > /dev/null 2>&1 & then - echo "${start_ip%.*}.$i" >> "${dir}scan_info" - fi - i=$((i + 1)) + + local current_time=$(date +%s) + local elapsed_time=$((current_time - last_scan_ip_time)) + + # 判断是否需要重新扫描 + if [ "$elapsed_time" -ge "$device_info_helper_sleeptime" ]; then + local start_ip=$(echo "$scan_ip_range" | cut -d "-" -f 1) + local end_ip=$(echo "$scan_ip_range" | cut -d "-" -f 2) + + local start_num=$(echo "$start_ip" | awk -F '.' '{print $NF}') + local end_num=$(echo "$end_ip" | awk -F '.' '{print $NF}') + local base_ip=$(echo "$start_ip" | awk -F '.' '{print $1"."$2"."$3}') + + for i in $(seq "$start_num" "$end_num"); do + # 获取一个令牌 + read -u 5 + { + ping -c 1 "${base_ip}.${i}" >/dev/null 2>&1 + # 释放令牌 + echo >&5 + } & done - scan_iplist=`cat ${dir}scan_info` - else - unset scan_iplist + wait + + last_scan_ip_time="$current_time" fi + unset i } # 查询设备接口 -function getinterface(){ - [ -z "${1}" ] && return - [ "${1}" == "unknown" ] && return - - [ -f "${dir}ipAddress" ] && local ip_interface=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $5}'|grep -v "^$"|sort -u|head -n1` - [ -f "${dir}tmp_downlist" ] && [ -z "$ip_interface" ] && local ip_interface=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $5}'|grep -v "^$"|sort -u|head -n1` - if [ -z "$ip_interface" ] && [ ! -z "$wlan_interface" ]; then - for interface in $wlan_interface; do - local ip_interface=`iw dev $interface station dump 2>/dev/null|grep Station|grep -i -w ${1}|sed -nr 's#^.*on (.*))#\1#gp'` >/dev/null 2>&1 - [ ! -z "$ip_interface" ] && echo "$ip_interface" && return +getinterface() { + local ip=${1} + local mac=${2} + local interface + + [ -z "$mac" ] && return + [ "$mac" == "unknown" ] && return + + # 从已保存的地址中获取接口 + interface=$(jq -r --arg ip "$ip" --arg mac "$mac" '.devices[] | select(.ip == $ip and .mac == $mac) | .interface' "$devices_json") + process_and_check "$interface" && return + + # 如果定义了 WLAN 接口列表,则查询每个接口 + if [ -n "$wlan_interface" ]; then + for interface_wlan in $wlan_interface; do + interface=$(iw dev "$interface_wlan" station dump 2>/dev/null | grep -i -w "$mac" | sed -nr 's#^.*on (.*))#\1#gp') + process_and_check "$interface" && return done fi - [ -z "$ip_interface" ] && local ip_interface=`cat /proc/net/arp|grep "0x2\|0x6"|grep -i -w ${1}|awk '{print $6}'|grep -v "^$"|sort -u|head -n1` - echo $ip_interface |tr -d '\n\r' + + # 查询 ARP 表获取接口 + interface=$(cat /proc/net/arp | grep "0x2\|0x6" | grep -i -w "$mac" | awk '{print $6}') + process_and_check "$interface" && return } # ping -function getping(){ - ip_interface=`getinterface ${2}` - [ "$iw_version" ] && [ "$ip_interface" ] && wlan_online=`iw dev ${ip_interface} station dump 2>/dev/null|grep -i -w ${2}|grep Station` >/dev/null 2>&1 +getping() { + local ip="$1" + local mac="$2" + local timeout="$3" + local retry_count="$4" + + local interface=$(getinterface "ip" "$mac") + [ "$iw_version" ] && [ "$interface" ] && wlan_online=$(iw dev ${interface} station dump 2>/dev/null | grep -i -w "$mac" | grep Station) >/dev/null 2>&1 [ "$wlan_online" ] && return 0 - arplist=$(cat /proc/net/arp) - for i in `seq 1 ${4}`; do - [ "$(echo "$arplist"|grep -i -w "${2}"|wc -l)" -eq 1 ] && interface=$(echo "$arplist"|grep -w ${1}|awk '{print $6}'|grep -v "^$"|sort -u|head -n1) && [ ! -z "$interface" ] && ip_ms=`arping -I ${interface} -c 20 -f -w ${3} ${1}` 2>/dev/null - ( ! echo "$ip_ms"|grep -q "ms" ) && ip_ms=`ping -c 5 -w ${3} ${1}|grep -v '100% packet loss'` 2>/dev/null - ( ! echo "$ip_ms"|grep -q "ms" ) && sleep 1 + + interface=$(cat /proc/net/arp | grep -w "$ip" | awk '{print $6}' | grep -v "^$" | sort -u | head -n1) + for i in $(seq 1 "$retry_count"); do + # arping 应使用 br-lan 等接口,有 WiFi 的设备接口为 wlan*,重新获取 + [ -n "$interface" ] && ip_ms=$(arping -I ${interface} -c 20 -f -w "$timeout" "$ip") 2>/dev/null + (! echo "$ip_ms" | grep -q "ms") && ip_ms=$(ping -c 5 -w "$timeout" "$ip" | grep -v '100% packet loss') 2>/dev/null + (! echo "$ip_ms" | grep -q "ms") && sleep 1 done - echo "$ip_ms"|grep -q "ms" + unset i + echo "$ip_ms" | grep -q "ms" } # CPU 占用率 -function getcpu(){ - local AT=$(cat /proc/stat|grep "^cpu "|awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}') +getcpu() { + local AT=$(cat /proc/stat | grep "^cpu " | awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}') sleep 1 - local BT=$(cat /proc/stat|grep "^cpu "|awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}') - printf "%.01f%%" $(echo ${AT} ${BT}|awk '{print (($4-$2)/($3-$1))*100}') + local BT=$(cat /proc/stat | grep "^cpu " | awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}') + printf "%.01f%%" $(echo ${AT} ${BT} | awk '{print (($4-$2)/($3-$1))*100}') } # 获取SOC温度 (取所有传感器温度最大值) -function soc_temp(){ - [ ! -z "$soc_code" ] && eval `echo "$soc_code"` 2>/dev/null && return 0 +soc_temp() { + [ -n "$soc_code" ] && eval $(echo "$soc_code") 2>/dev/null && return 0 getsensors() { # Intel local sensor_field1='["coretemp-isa-0000"]["Package id 0"]["temp1_input"]' @@ -411,151 +531,164 @@ function soc_temp(){ " } - [ ! -z "$server_host" ] && ssh_command="ssh -o StrictHostKeyChecking=yes -o BatchMode=yes -i /root/.ssh/id_rsa root@${server_host} -p ${server_port}" + [ -n "$server_host" ] && ssh_command="ssh -o StrictHostKeyChecking=yes -o BatchMode=yes -i /root/.ssh/id_rsa root@${server_host} -p ${server_port}" temperature=$(getsensors "$ssh_command" 2>/dev/null) # 通用(只能取最高温度,不一定是 CPU,特殊设备自行修改) # 将 grep °C 改为温度所在行的特别字符串,如 grep Core 0 等,就可以指定设备了 - [ -z "$temperature" ] && temperature=$(sensors 2>/dev/null|grep °C|sed -nr 's#^.*:.*\+(.*)°C .*#\1#gp'|sort -nr|head -n1) + [ -z "$temperature" ] && temperature=$(sensors 2>/dev/null | grep °C | sed -nr 's#^.*:.*\+(.*)°C .*#\1#gp' | sort -nr | head -n1) # 将 thermal_zone* 改为 thermal_zone0 thermal_zone1 等,就可以指定设备了 - [ -z "$temperature" ] && temperature=$(cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null|sort -nr|head -n1|cut -c-2) + [ -z "$temperature" ] && temperature=$(cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null | sort -nr | head -n1 | cut -c-2) printf "%.1f" "$temperature" } # 流量数据 -function usage(){ - [ ! -f "/usr/sbin/wrtbwmon" ] || [ ! "$1" ] && return +usage() { + [ ! -f "/usr/sbin/wrtbwmon" ] || [ -z "$1" ] && return # 更新 - if [ $1 == "update" ] ;then - function version_le() { test "$(echo "$@"|tr " " "\n"|sort -n|head -n 1)" == "$1"; } - function version_ge() { test "$(echo "$@"|tr " " "\n"|sort -r|head -n 1)" == "$1"; } - [ ! -z "$wr_version" ] && ( version_ge "${wr_version}" "1.2.0" ) && wrtbwmon -f ${dir}usage.db 2>/dev/null && return - [ ! -z "$wr_version" ] && ( version_le "${wr_version}" "1.0.0" ) || [ -z "$wr_version" ] && wrtbwmon update ${dir}usage.db 2>/dev/null && return + if [ $1 == "update" ]; then + version_le() { test "$(echo "$@" | tr " " "\n" | sort -n | head -n 1)" == "$1"; } + version_ge() { test "$(echo "$@" | tr " " "\n" | sort -r | head -n 1)" == "$1"; } + [ -n "$wr_version" ] && (version_ge "${wr_version}" "1.2.0") && wrtbwmon -f ${dir}/usage.db 2>/dev/null && return + [ -n "$wr_version" ] && (version_le "${wr_version}" "1.0.0") || [ -z "$wr_version" ] && wrtbwmon update ${dir}/usage.db 2>/dev/null && return # 获取 - elif [ $1 == "get" ] ;then - [ ! -f "${dir}usage.db" ] && [ ! "$3" ] && echo `bytes_for_humans 0` && return - [ ! -f "${dir}usage.db" ] && [ "$3" ] && echo 0 && return - [ -z "$total_n" ] && total_n=`cat ${dir}usage.db|head -n1|grep "total"|sed 's/,/\n/g'|awk '/total/{print NR}'` 2>/dev/null + elif [ $1 == "get" ]; then + [ ! -f "${dir}/usage.db" ] && [ -z "$3" ] && echo $(bytes_for_humans "0") && return + [ ! -f "${dir}/usage.db" ] && [ -n "$3" ] && echo "0" && return + [ -z "$total_n" ] && total_n=$(cat ${dir}/usage.db | head -n1 | grep "total" | sed 's/,/\n/g' | awk '/total/{print NR}') 2>/dev/null [ -z "$total_n" ] && total_n="6" - [ "$2" ] && local tmptotal=`cat ${dir}usage.db|sed 's/,,,/,0,0,/g'|sed 's/,,/,0,/g'|sed 's/,/ /g'|grep -i -w ${2}|awk "{print "'$'$total_n"}"|grep -v "^$"|sort -u|head -n1` 2>/dev/null + [ -n "$2" ] && local tmptotal=$(cat ${dir}/usage.db | sed 's/,,,/,0,0,/g' | sed 's/,,/,0,/g' | sed 's/,/ /g' | grep -i -w ${2} | awk "{print "'$'$total_n"}" | grep -v "^$" | sort -u | head -n1) 2>/dev/null [ -z "$tmptotal" ] && local tmptotal="0" - [ ! "$3" ] && echo `bytes_for_humans ${tmptotal}` || echo $tmptotal + [ -z "$3" ] && echo $(bytes_for_humans "${tmptotal}") || echo "$tmptotal" # 剔除 - elif [ $1 == "down" ] ;then - [ "$2" ] && sed -i "/,${2},/d" ${dir}usage.db 2>/dev/null + elif [ $1 == "down" ]; then + [ "$2" ] && sed -i "/,${2},/d" ${dir}/usage.db 2>/dev/null fi } # 流量数据单位换算 -function bytes_for_humans { - [ ! "$1" ] && return - [ "$1" -gt 1073741824 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1073741824'}'` GB" && return - [ "$1" -gt 1048576 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1048576'}'` MB" && return - [ "$1" -gt 1024 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1024'}'` KB" && return +bytes_for_humans() { + [ -z "$1" ] && return + [ "$1" -gt 1073741824 ] && echo "$(awk 'BEGIN{printf "%.2f\n",'$1'/'1073741824'}') GB" && return + [ "$1" -gt 1048576 ] && echo "$(awk 'BEGIN{printf "%.2f\n",'$1'/'1048576'}') MB" && return + [ "$1" -gt 1024 ] && echo "$(awk 'BEGIN{printf "%.2f\n",'$1'/'1024'}') KB" && return echo "${1} bytes" } # 设备异常流量检测 -function get_client_usage(){ +get_client_usage() { [ -z "$client_usage" ] && return [ "$client_usage" -ne "1" ] && return [ -z "$client_usage_max" ] && return - [ -z "$get_client_usage_time" ] && get_client_usage_time=`date +%s` - ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "K\|k" ) && client_usage_max=`expr ${client_usage_max%?} \* 1024` - ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "M\|m" ) && client_usage_max=`expr ${client_usage_max%?} \* 1048576` - ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "G\|g" ) && client_usage_max=`expr ${client_usage_max%?} \* 1073741824` + [ -z "$get_client_usage_time" ] && get_client_usage_time=$(date +%s) + (echo $client_usage_max | sed -r 's/.*(.)$/\1/' | grep -q "K\|k") && client_usage_max=$(expr ${client_usage_max%?} \* 1024) + (echo $client_usage_max | sed -r 's/.*(.)$/\1/' | grep -q "M\|m") && client_usage_max=$(expr ${client_usage_max%?} \* 1048576) + (echo $client_usage_max | sed -r 's/.*(.)$/\1/' | grep -q "G\|g") && client_usage_max=$(expr ${client_usage_max%?} \* 1073741824) [ -z "$client_usage_disturb" ] && client_usage_disturb="0" - [ "$client_usage_disturb" -eq "0" ] && [ -f "${dir}ipAddress" ] && local MACLIST=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` - [ "$client_usage_disturb" -eq "1" ] && [ ! -z "$client_usage_whitelist" ] && local MACLIST=`echo "$client_usage_whitelist"` + [ "$client_usage_disturb" -eq "0" ] && local MACLIST=$(jq -r '.devices[] | select(.status == "online") | .mac' "$devices_json" | sort -u) + [ "$client_usage_disturb" -eq "1" ] && [ -n "$client_usage_whitelist" ] && local MACLIST=$(echo "$client_usage_whitelist") [ -z "$MACLIST" ] && return - if [ "$((`date +%s`-$get_client_usage_time))" -ge "60" ]; then - > ${dir}client_usage_aliases + if [ "$(($(date +%s) - $get_client_usage_time))" -ge "60" ]; then + >${dir}/client_usage_aliases for mac in $MACLIST; do - ( ! cat ${dir}ipAddress|grep -q -i -w $mac|grep -v "^$"|sort -u|head -n1 ) && continue - echo "$mac" `usage get ${mac} bytes` >> ${dir}client_usage_aliases - [ -f "${dir}old_client_usage_aliases" ] && get_client_usage_bytes=`cat ${dir}old_client_usage_aliases|grep -i -w $mac|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` || continue + # 查找在线设备中是否存在这个mac,不存在则退出 + ! jq -e --arg mac "$mac" '.devices[] | select(.mac == $mac and .status == "online")' "$devices_json" >/dev/null && continue + + echo "$mac" $(usage get ${mac} bytes) >>${dir}/client_usage_aliases + [ -f "${dir}/old_client_usage_aliases" ] && get_client_usage_bytes=$(cat ${dir}/old_client_usage_aliases | grep -i -w $mac | awk '{print $2}' | grep -v "^$" | sort -u | head -n1) || continue + [ -z "$get_client_usage_bytes" ] && get_client_usage_bytes="0" - if [ "$((`usage get ${mac} bytes`-$get_client_usage_bytes))" -ge "$client_usage_max" ]; then - local ip=`cat ${dir}ipAddress|grep -i -w $mac|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` - local ip_name=`getname ${ip} ${mac}` - local tmp_usage=$(bytes_for_humans $(expr `usage get ${mac} bytes` - ${get_client_usage_bytes})) - local time_up=`cat ${dir}ipAddress|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - local ip_total=`usage get $mac` && [ ! -z "$ip_total" ] && local ip_total="${str_linefeed}${str_tab}总计流量: ${str_space}${str_space}${str_space}${str_space}${ip_total}" - local time1=`date +%s` - local time1=$(time_for_humans `expr ${time1} - ${time_up}`) + if [ "$(($(usage get ${mac} bytes) - $get_client_usage_bytes))" -ge "$client_usage_max" ]; then + local ip=$(jq -r --arg mac "$mac" '.devices[] | select(.mac == $mac and .status == "online") | .ip' "$devices_json") + local ip_name=$(getname ${ip} ${mac}) + local tmp_usage=$(bytes_for_humans $(expr $(usage get ${mac} bytes) - ${get_client_usage_bytes})) + local time_up=$(jq -r --arg mac "$mac" '.devices[] | select(.mac == $mac and .status == "online") | .uptime' "$devices_json") + local ip_total=$(usage get $mac) && [ -n "$ip_total" ] && local ip_total="${str_linefeed}${str_tab}总计流量: ${str_space}${str_space}${str_space}${str_space}${ip_total}" + local time1=$(date +%s) + local time1=$(time_for_humans $(expr ${time1} - ${time_up})) if [ -z "$title" ]; then title="${ip_name} 流量异常" - content="${content}${str_splitline}${str_title_start} 设备流量异常${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" - elif ( echo "$title"|grep -q "流量异常" ); then + local content_title="${str_splitline}${str_title_start} 设备流量异常${str_title_end}" + elif echo "$title" | grep -q "流量异常"; then title="${ip_name} ${title}" - content="${content}${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" else title="设备状态变化" - content="${content}${str_splitline}${str_title_start} 设备流量异常${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + local content_title="${str_splitline}${str_title_start} 设备流量异常${str_title_end}" fi + + local content_name="${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}" + local content_ip="${str_linefeed}${str_tab}客户端IP:${str_space}${str_space}${str_space}${str_space}${ip}" + local content_mac="${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}${ip_total}" + local content_usage="${str_linefeed}${str_tab}一分钟内流量:${str_space}${str_space}${str_space}${tmp_usage}" + local content_time="${str_linefeed}${str_tab}在线时间:${str_space}${str_space}${str_space}${str_space}${time1}" + + content="${content}${content_title}${content_name}${content_ip}${content_mac}${content_usage}${content_time}" fi done - cat ${dir}client_usage_aliases > ${dir}old_client_usage_aliases - get_client_usage_time=`date +%s` + cat ${dir}/client_usage_aliases >${dir}/old_client_usage_aliases + get_client_usage_time=$(date +%s) fi } # 时间单位换算 -function time_for_humans { - [ ! "$1" ] && return +time_for_humans() { + [ -z "$1" ] && return if [ "$1" -lt 60 ]; then echo "${1} 秒" elif [ "$1" -lt 3600 ]; then - local usetime_min=`expr $1 / 60` - local usetime_sec=`expr $usetime_min \* 60` - local usetime_sec=`expr $1 - $usetime_sec` + local usetime_min=$(expr $1 / 60) + local usetime_sec=$(expr $usetime_min \* 60) + local usetime_sec=$(expr $1 - $usetime_sec) echo "${usetime_min} 分 ${usetime_sec} 秒" elif [ "$1" -lt 86400 ]; then - local usetime_hour=`expr $1 / 3600` - local usetime_min=`expr $usetime_hour \* 3600` - local usetime_min=`expr $1 - $usetime_min` - local usetime_min=`expr $usetime_min / 60` + local usetime_hour=$(expr $1 / 3600) + local usetime_min=$(expr $usetime_hour \* 3600) + local usetime_min=$(expr $1 - $usetime_min) + local usetime_min=$(expr $usetime_min / 60) echo "${usetime_hour} 小时 ${usetime_min} 分" else - local usetime_day=`expr $1 / 86400` - local usetime_hour=`expr $usetime_day \* 86400` - local usetime_hour=`expr $1 - $usetime_hour` - local usetime_hour=`expr $usetime_hour / 3600` + local usetime_day=$(expr $1 / 86400) + local usetime_hour=$(expr $usetime_day \* 86400) + local usetime_hour=$(expr $1 - $usetime_hour) + local usetime_hour=$(expr $usetime_hour / 3600) echo "${usetime_day} 天 ${usetime_hour} 小时" fi } -# 计算字符显示宽度 -function length_str { - [ ! "$1" ] && return - - local length_zh=`echo "$1"|awk '{print gensub(/[\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}'|awk -F "" '{print NF}'` - local length_en=`echo "$1"|awk '{print gensub(/[^\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}'|awk -F "" '{print NF}'` - +# 计算字符串显示宽度 +length_str() { + [ -z "$1" ] && return + + local length_zh=$(echo "$1" | awk '{print gensub(/[\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}' | awk -F "" '{print NF}') + local length_en=$(echo "$1" | awk '{print gensub(/[^\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}' | awk -F "" '{print NF}') + echo $((length_zh / 3 * 2 + length_en)) } -function cut_str { - [ ! "$1" ] && return - [ ! "$2" ] && return +# 字符串显示宽度处理 +cut_str() { + [ -z "$1" ] && return + [ -z "$2" ] && return if [ $(length_str "$1") -le "$2" ]; then echo -n "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' return fi - + local columns=$(echo "$1" | awk '{print NF}') if [ "$columns" -gt 1 ]; then local max_length=0 local max_column=1 for i in $(seq 1 $columns); do - local length=$(echo "$1"|awk '{print gensub(/[\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}'|awk -F "" '{print NF}') + local length=$(echo "$1" | awk '{print gensub(/[\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}' | awk -F "" '{print NF}') if [ "$length" -gt "$max_length" ]; then max_length=$length max_column=$i fi done + unset i + local text=$(echo -n "$1" | cut -d ' ' -f $max_column) if [ $(length_str "$text") -le "$2" ]; then echo "$text" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' @@ -568,84 +701,62 @@ function cut_str { temp_length=$(expr $temp_length + 1) done while [ $(printf "%d" \'$(echo -n "$1" | cut -c $temp_length)) -ge "128" ] && [ $(printf "%d" \'$(echo -n "$1" | cut -c $temp_length)) -lt "224" ]; do - temp_length=$(expr $temp_length + 1) + temp_length=$(expr "$temp_length" + 1) done temp_length=$(expr $temp_length - 1) echo "$(echo -n "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | cut -c -$temp_length).." } - - # 随机数 -function rand(){ +rand() { local min=$1 - local max=$(($2- $min + 1)) + local max=$(($2 - $min + 1)) local num=$(date +%s%N) echo $(($num % $max + $min)) } # 在线设备列表 -function first(){ - [ -f "${dir}ipAddress" ] && local IPLIST_ipAddress=`cat ${dir}ipAddress|awk '{print $1}'|grep -v "^$"|sort -u` +first() { + local IPLIST=$(jq -r '.devices[] | select(.status == "online" or .status == "unknown") | .ip' "$devices_json" | sort -u) getgateway - scanlocalip;last_scan_ip_time=$(date +%s) - for ip in $IPLIST_ipAddress; do - [ ! -z "$passive_mode" ] && [ "$passive_mode" -eq "1" ] && break + silent_run scanlocalip + for ip in $IPLIST; do + [ -n "$passive_mode" ] && [ "$passive_mode" -eq "1" ] && break + # 获取一个令牌 read -u 5 { - down $ip - echo "" >&5 - }& + down "$ip" + # 释放令牌 + echo >&5 + } & done wait - local IPLIST=`cat /proc/net/arp|grep "0x2\|0x6"|awk '{print $1}'|grep -v "^169.254."|grep -v "^$"|sort -u|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'` - local IPLIST=`echo -e "${IPLIST}\n${gateway_iplist}\n${scan_iplist}"|grep -v "^$"|sort -u` + local IP_INFO=$(ip addr show br-lan | grep 'inet ' | awk '{print $2}') + [ -n "$IP_INFO" ] && local SUBNET=$(echo $IP_INFO | cut -d'/' -f1 | cut -d'.' -f1-3) + [ -n "$SUBNET" ] && local IPLIST=$(cat /proc/net/arp | grep "0x2\|0x6" | awk '{print $1}' | grep -v "^169.254." | grep -v "^$" | sort -u | grep -oE "${SUBNET}\.[0-9]{1,3}") || local IPLIST=$(cat /proc/net/arp | grep "0x2\|0x6" | awk '{print $1}' | grep -v "^169.254." | grep -v "^$" | sort -u | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') + + local IPLIST=$(echo -e "${IPLIST}\n${gateway_iplist}\n${always_check_ip_list}" | grep -v "^$" | sort -u) for ip in $IPLIST; do - [ ! -z "$passive_mode" ] && [ "$passive_mode" -eq "1" ] && break + [ -n "$passive_mode" ] && [ "$passive_mode" -eq "1" ] && break + # 获取一个令牌 read -u 5 { - up $ip - echo "" >&5 - }& + up "$ip" + # 释放令牌 + echo >&5 + } & done wait - - [ -z "$passive_mode" ] && return - [ "$passive_mode" -ne "1" ] && return - for ip in $IPLIST; do - if ( ! echo "$IPLIST_ipAddress"|grep -q -i -w $ip ); then - ip_mac=`getmac $ip` - ip_name=`getname ${ip} ${ip_mac}` - ip_interface=`getinterface ${ip_mac}` - echo "{'ip': '${ip}','mac': '${ip_mac}','name': '${ip_name}','uptime': '0秒','interface': '${ip_interface}','usage': ''}" > ${dir}client/${ip} - usage down ${ip} - echo "${ip} ${ip_mac} ${ip_name// /_} `date +%s` ${ip_interface}" >> ${dir}ipAddress - fi - done - for ip in $IPLIST_ipAddress; do - if ( echo "$IPLIST"|grep -q -i -w $ip ); then - ip_mac=`getmac $ip` - ip_name=`getname ${ip} ${ip_mac}` - ip_interface=`getinterface ${ip_mac}` - time_up=`cat ${dir}ipAddress|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - time1=`date +%s` - time1=$(time_for_humans `expr ${time1} - ${time_up}`) - echo "{'ip': '${ip}','mac': '${ip_mac}','name': '${ip_name}','uptime': '${time1}','interface': '${ip_interface}','usage': '`usage get $ip_mac`'}" > ${dir}client/${ip} - else - sed -i "/^${ip} /d" ${dir}ipAddress - rm -f ${dir}client/${ip} >/dev/null 2>&1 - fi - done } # 创建计划任务 -function cron(){ - function del_cron(){ - ( echo `crontab -l 2>/dev/null`|grep -q "wechatpush" ) && crontab -l > conf && sed -i "/wechatpush/d" conf && crontab conf && rm -f conf >/dev/null 2>&1 +cron() { + del_cron() { + crontab -l | grep -v "wechatpush" | crontab - } - function re_cron(){ + re_cron() { /etc/init.d/cron stop /etc/init.d/cron start } @@ -656,44 +767,56 @@ function cron(){ fi # 重置流量 - if [ ! -z "$reset_regularly" ] && [ "$reset_regularly" -eq "1" ]; then - crontab -l 2>/dev/null > conf && echo -e "0 0 * * * rm ${dir}usage.db >/dev/null 2>&1" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 - crontab -l 2>/dev/null > conf && echo -e "0 0 * * * rm ${dir}usage6.db >/dev/null 2>&1" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 + if [ -n "$reset_regularly" ] && [ "$reset_regularly" -eq "1" ]; then + ( + crontab -l 2>/dev/null + echo "0 0 * * * rm ${dir}/usage.db >/dev/null 2>&1" + ) | crontab - + ( + crontab -l 2>/dev/null + echo "0 0 * * * rm ${dir}/usage6.db >/dev/null 2>&1" + ) | crontab - fi # 定时发送 if [ -n "$crontab_regular_time" ]; then - crontab_regular_time=`echo $crontab_regular_time|sed 's/ /,/g'` 2>/dev/null - crontab -l 2>/dev/null > conf && echo -e "0 $crontab_regular_time * * * /usr/share/wechatpush/wechatpush send &" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 + crontab_regular_time=$(echo "$crontab_regular_time" | sed 's/ /,/g') + ( + crontab -l 2>/dev/null + echo "0 $crontab_regular_time * * * /usr/share/wechatpush/wechatpush send &" + ) | crontab - # 间隔发送 elif [ -n "$crontab_interval_time" ]; then - crontab -l 2>/dev/null > conf && echo -e "0 */$crontab_interval_time * * * /usr/share/wechatpush/wechatpush send &" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 + ( + crontab -l 2>/dev/null + echo "0 */$crontab_interval_time * * * /usr/share/wechatpush/wechatpush send &" + ) | crontab - fi re_cron } # 免打扰检测 -function disturb(){ +disturb() { [ -z "$do_not_disturb_mode" ] || [ -z "$do_not_disturb_starttime" ] || [ -z "$do_not_disturb_endtime" ] && return 0 # 非免打扰时间 - if [ `date +%H` -ge $do_not_disturb_endtime -a $do_not_disturb_starttime -lt $do_not_disturb_endtime ] || [ `date +%H` -lt $do_not_disturb_starttime -a $do_not_disturb_starttime -lt $do_not_disturb_endtime ] || [ `date +%H` -lt $do_not_disturb_starttime -a `date +%H` -ge $do_not_disturb_endtime -a $do_not_disturb_starttime -gt $do_not_disturb_endtime ]; then + if [ $(date +%H) -ge $do_not_disturb_endtime -a $do_not_disturb_starttime -lt $do_not_disturb_endtime ] || [ $(date +%H) -lt $do_not_disturb_starttime -a $do_not_disturb_starttime -lt $do_not_disturb_endtime ] || [ $(date +%H) -lt $do_not_disturb_starttime -a $(date +%H) -ge $do_not_disturb_endtime -a $do_not_disturb_starttime -gt $do_not_disturb_endtime ]; then unset sheep_starttime - rm -f ${dir}sheep_usage ${dir}old_sheep_usage 2>/dev/null - disturb_text=`jq -r '._api' ${jsonpath}` + rm -f ${dir}/sheep_usage ${dir}/old_sheep_usage 2>/dev/null + disturb_text=$(jq -r '._api' ${jsonpath}) return 0 # 免打扰 else - [ -z "$sheep_starttime" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【免打扰】夜深了,该休息了" >> ${logfile} && sheep_starttime=`date +%s` + [ -z "$sheep_starttime" ] && log_change "【免打扰】夜深了,该休息了" && sheep_starttime=$(date +%s) # 挂起 - if [ "$do_not_disturb_mode" -eq "1" ] ;then - while [ `date +%H` -lt "$do_not_disturb_endtime" ]; do + if [ "$do_not_disturb_mode" -eq "1" ]; then + while [ $(date +%H) -lt "$do_not_disturb_endtime" ]; do enable_detection sleep $sleeptime done # 静默 - elif [ "$do_not_disturb_mode" -eq "2" ] ;then + elif [ "$do_not_disturb_mode" -eq "2" ]; then disturb_text="【免打扰】" return 1 fi @@ -701,85 +824,105 @@ function disturb(){ } # 文件锁 -function LockFile(){ - if [ $1 = "lock" ] ;then - [ ! -f "${dir}wechatpush.lock" ] && > ${dir}wechatpush.lock && return - while [ -f "${dir}wechatpush.lock" ]; do - enable_detection 1 - done - LockFile lock +LockFile() { + local fd=200 + + if [ "$1" = "lock" ]; then + eval "exec $fd>$lock_file" + flock -n $fd + if [ $? -ne 0 ]; then + while ! flock -n $fd; do + sleep 1 + done + fi + elif [ "$1" = "unlock" ]; then + eval "exec $fd>&-" fi - [ $1 = "unlock" ] && rm -f ${dir}wechatpush.lock >/dev/null 2>&1 - return 0 } # 检测黑白名单 -function blackwhitelist(){ - [ ! "$1" ] && return 1 +blackwhitelist() { + local mac="$1" + [ -z "$mac" ] && return 1 + + # return 1 免打扰 + # return 0 正常推送 + # 没有打开免打扰功能 [ -z "$up_down_push_whitelist" ] && [ -z "$up_down_push_blacklist" ] && [ -z "$up_down_push_interface" ] && [ -z "$mac_online_list" ] && [ -z "$mac_offline_list" ] && return 0 - [ ! -z "$up_down_push_whitelist" ] && ( echo "$up_down_push_whitelist"|grep -q -i -w $1 ) && return 1 - [ ! -z "$up_down_push_blacklist" ] && ( ! echo "$up_down_push_blacklist"|grep -q -i -w $1 ) && return 1 - [ ! -z "$up_down_push_interface" ] && ( ! echo `getinterface ${1}`|grep -q -i -w $up_down_push_interface ) && return 1 - [ ! -z "$mac_online_list" ] && [ ! -z "$mac_online_status" ] && return 1 - [ ! -z "$mac_online_list" ] && ( echo "$mac_online_list"|grep -q -i -w $1 ) && return 1 - [ ! -z "$mac_offline_list" ] && [ -z "$mac_online_status" ] && return 1 + # 忽略列表内设备 + [ -n "$up_down_push_whitelist" ] && (echo "$up_down_push_whitelist" | grep -q -i -w "$mac") && return 1 + # 仅通知列表内设备 + [ -n "$up_down_push_blacklist" ] && (! echo "$up_down_push_blacklist" | grep -q -i -w "$mac") && return 1 + # 仅通知接口 + [ -n "$up_down_push_interface" ] && (! echo $(getinterface "" "$mac") | grep -q -i -w $up_down_push_interface) && return 1 + + for check_mac in $mark_mac_list; do + # 设备在线时免打扰 + [ -n "$mac_online_list" ] && jq -e --arg mac "$check_mac" '.devices[] | select(.mac == $mac and .status == "online") | .mac' "$devices_json" >/dev/null && return 1 + # 设备离线时免打扰 + [ -n "$mac_offline_list" ] && jq -e --arg mac "$check_mac" '.devices[] | select(.mac == $mac and .status == "offline") | .mac' "$devices_json" >/dev/null && return 1 + done + + unset check_mac return 0 } # 重启网络服务 -function network_restart(){ -cat>${dir}network_restart<${dir}/network_restart </dev/null 2>&1 & /etc/init.d/firewall restart >/dev/null 2>&1 & /etc/init.d/dnsmasq restart >/dev/null 2>&1 & EOF - chmod 0755 ${dir}network_restart && ${dir}network_restart - rm -f ${dir}network_restart >/dev/null 2>&1 + chmod 0755 ${dir}/network_restart && ${dir}/network_restart + rm -f ${dir}/network_restart >/dev/null 2>&1 } # 查看无人值守任务设备是否在线 -function geterrdevicealiases(){ +geterrdevicealiases() { [ -z "$unattended_device_aliases" ] && return - [ -f ${dir}ipAddress ] && local logrow=$(grep -c "" ${dir}ipAddress) || local logrow="0";[ $logrow -eq "0" ] && return + local logrow=$(jq '.devices | map(select(.status == "online")) | length' "$devices_json") + [ $logrow -eq "0" ] && return - local MACLIST=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` - for mac in $MACLIST; do - [ -z "$unattended_mac" ] && [ ! -z "$mac" ] && local unattended_mac=`echo "$unattended_device_aliases"|grep -i $mac|grep -v "^$"|sort -u|head -n1` + for mac in $unattended_device_aliases; do + [ -n "$mac" ] && local unattended_mac=$(jq -r --arg mac "$mac" '.devices[] | select(.mac == $mac and .status == "online") | .mac' "$devices_json") && break done + # 进入免打扰时间已经超过一小时 - if [ ! -z "$sheep_starttime" ] && [ "$((`date +%s`-$sheep_starttime))" -ge "3600" ]; then - > ${dir}sheep_usage - local MACLIST=`echo "$unattended_device_aliases"|grep -v "^$"|sort -u` + if [ -n "$sheep_starttime" ] && [ "$(($(date +%s) - $sheep_starttime))" -ge "3600" ]; then + >${dir}/sheep_usage + local MACLIST=$(echo "$unattended_device_aliases" | grep -v "^$" | sort -u) while IFS= read -r mac; do - [ ! -z "$mac" ] && local tmptotal=`usage get ${mac} bytes` - [ ! -z "$tmptotal" ] && awk 'BEGIN{printf "%.0f\n",'$tmptotal'/'204800'}' 2>/dev/null >> ${dir}sheep_usage - done <<< "$MACLIST" - old_sheep_usage=`cat ${dir}old_sheep_usage` 2>/dev/null - sheep_usage=`cat ${dir}sheep_usage` 2>/dev/null - [ "$old_sheep_usage" == "$sheep_usage" ] && [ -z "$sheep_nousage_starttime" ] && sheep_nousage_starttime=`date +%s` - [ "$old_sheep_usage" != "$sheep_usage" ] && unset sheep_nousage_starttime && cat ${dir}sheep_usage 2>/dev/null > ${dir}old_sheep_usage - [ ! -z "$sheep_nousage_starttime" ] && [ "$((`date +%s`-$sheep_nousage_starttime))" -ge "300" ] && unset unattended_mac + [ -n "$mac" ] && local tmptotal=$(usage get ${mac} bytes) + [ -n "$tmptotal" ] && awk 'BEGIN{printf "%.0f\n",'$tmptotal'/'204800'}' 2>/dev/null >>${dir}/sheep_usage + done <<<"$MACLIST" + [ -f ${dir}/old_sheep_usage ] && local old_sheep_usage=$(cat ${dir}/old_sheep_usage) 2>/dev/null || local old_sheep_usage="" + [ -f ${dir}/sheep_usage ] && local sheep_usage=$(cat ${dir}/sheep_usage) 2>/dev/null || local sheep_usage="" + [ "$old_sheep_usage" == "$sheep_usage" ] && [ -z "$sheep_nousage_starttime" ] && sheep_nousage_starttime=$(date +%s) + [ "$old_sheep_usage" != "$sheep_usage" ] && unset sheep_nousage_starttime && cat ${dir}/sheep_usage 2>/dev/null >${dir}/old_sheep_usage + [ -n "$sheep_nousage_starttime" ] && [ "$(($(date +%s) - $sheep_nousage_starttime))" -ge "300" ] && unset unattended_mac fi [ -z "$unattended_mac" ] } # 无人值守任务 -function unattended(){ +unattended() { [ -z "$unattended_enable" ] || [ "$unattended_enable" -ne "1" ] && return - [ ! -z "$unattended_only_on_disturb_time" ] && [ "$unattended_only_on_disturb_time" -eq "1" ] && [ -z "$sheep_starttime" ] && return - geterrdevicealiases;[ $? -eq "1" ] && return - - if [ ! -z "$unattended_autoreboot_mode" ]; then - local interfaceuptime=`getinterfaceuptime` - if [ ! -z "$autoreboot_system_uptime" ] && [ `cat /proc/uptime|awk -F. '{run_hour=$1/3600;printf("%d",run_hour)}'` -ge "$autoreboot_system_uptime" ] && [ "$unattended_autoreboot_mode" -eq "1" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【无人值守任务】重启路由器咯" >> ${logfile} - cat ${logfile} > /usr/share/wechatpush/errlog + [ -n "$unattended_only_on_disturb_time" ] && [ "$unattended_only_on_disturb_time" -eq "1" ] && [ -z "$sheep_starttime" ] && return + geterrdevicealiases + [ $? -eq "1" ] && return + + if [ -n "$unattended_autoreboot_mode" ]; then + local interfaceuptime=$(getinterfaceuptime) + if [ -n "$autoreboot_system_uptime" ] && [ $(cat /proc/uptime | awk -F. '{run_hour=$1/3600;printf("%d",run_hour)}') -ge "$autoreboot_system_uptime" ] && [ "$unattended_autoreboot_mode" -eq "1" ]; then + log_change "【无人值守任务】重启路由器咯" + cat ${logfile} >/usr/share/wechatpush/errlog sleep 2 && reboot && exit - elif [ ! -z "$autoreboot_network_uptime" ] && [ ! -z "$interfaceuptime" ] && [ `echo "$interfaceuptime"|awk -F. '{run_hour=$1/3600;printf("%d",run_hour)}'` -ge "$autoreboot_network_uptime" ] && [ "$unattended_autoreboot_mode" -eq "2" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【无人值守任务】重新拨号咯" >> ${logfile} + elif [ -n "$autoreboot_network_uptime" ] && [ -n "$interfaceuptime" ] && [ $(echo "$interfaceuptime" | awk -F. '{run_hour=$1/3600;printf("%d",run_hour)}') -ge "$autoreboot_network_uptime" ] && [ "$unattended_autoreboot_mode" -eq "2" ]; then + log_change "【无人值守任务】重新拨号咯" ifup wan >/dev/null 2>&1 sleep 60 fi @@ -787,44 +930,49 @@ function unattended(){ } # 检测网络状态 -function rand_geturl(){ +check_connect() { # 获取网络状态 - function getcheck(){ - local urllist="https://www.163.com https://www.qq.com https://www.baidu.com https://www.qidian.com https://www.douban.com" - local url_number=`expr $(echo "$urllist"|grep -o ' '|wc -l) + 1` - local url_str=`echo "$urllist"|awk -v i=$(rand 1 $url_number) '{print $i}'` - echo `curl -k -s -w "%{http_code}" -m 5 ${url_str} -A "${User_Agent}" -o /dev/null` + is_online() { + local urls=(http://connect.rom.miui.com/generate_204 http://wifi.vivo.com.cn/generate_204 http://connectivitycheck.platform.hicloud.com/generate_204 http://www.apple.com/library/test/success.html) + local shuffled_urls=($(for i in "${urls[@]}"; do echo "$i"; done | awk 'BEGIN {srand()} {print rand(), $0}' | sort -n | cut -d' ' -f2-)) + for url in "${shuffled_urls[@]}"; do + local status_code + status_code=$(curl -o /dev/null -sI -w "%{http_code}" "$url") + [[ "$status_code" -eq 204 || "$status_code" -eq 200 ]] && return 0 + done + unset i url + return 1 } - local check=`getcheck` - while [ -z "$check" ] || [[ $check -ne 200 && $check -ne 301 && $check -ne 302 ]]; do - local check=`getcheck` - if [ ! -z "$check" ] && [[ $check -eq 200 || $check -eq 301 || $check -eq 302 ]]; then - [ ! -z "$network_enable" ] && [ "$network_enable" -eq "404" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【网络状态】网络恢复正常.." >> ${logfile} - local network_enable="200" + + local network_state="unknown" + while true; do + if is_online; then + [ "$network_state" == "down" ] && log_change "【网络状态】网络恢复正常.." + break else - [ -z "$network_enable" ] || [ "$network_enable" -eq "200" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】当前网络不通!停止检测! " >> ${logfile} - local network_enable="404" + [ "$network_state" == "unknown" ] && log_change "【!!!】当前网络不通!停止检测! " + local network_state="down" # 无人值守、待弃用或改进 - [ -z "$network_unattended_time" ] && network_unattended_time=`date +%s` - if [ ! -z "$network_disconnect_event" ] && [ "$((`date +%s`-$network_unattended_time))" -ge "300" ]; then - > ${dir}send_enable.lock && first && deltemp + [ -z "$network_unattended_time" ] && network_unattended_time=$(date +%s) + if [ -n "$network_disconnect_event" ] && [ "$(($(date +%s) - $network_unattended_time))" -ge "300" ]; then + >"${dir}/send_enable.lock" && first && deltemp geterrdevicealiases if [ "$?" -eq "0" ]; then - [ -f /usr/share/wechatpush/autoreboot_count ] && retry_count=`cat /usr/share/wechatpush/autoreboot_count` && rm -f /usr/share/wechatpush/autoreboot_count >/dev/null 2>&1 - [ ! -z "${retry_count}" ] && retry_count=0 + [ -f /usr/share/wechatpush/autoreboot_count ] && retry_count=$(cat /usr/share/wechatpush/autoreboot_count) && rm -f /usr/share/wechatpush/autoreboot_count >/dev/null 2>&1 + [ -z "${retry_count}" ] && retry_count=0 retry_count=$((retry_count + 1)) - if [ "$network_disconnect_event" -eq "1" ] ;then - if [ "$retry_count" -lt "3" ] ;then - echo "$retry_count" > /usr/share/wechatpush/autoreboot_count - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试重启路由,当前第 $retry_count 次 " >> ${logfile} - cat ${logfile} > /usr/share/wechatpush/errlog + if [ "$network_disconnect_event" -eq "1" ]; then + if [ "$retry_count" -lt "3" ]; then + echo "$retry_count" >/usr/share/wechatpush/autoreboot_count + log_change "【!!!】正在尝试重启路由,当前第 $retry_count 次 " + cat ${logfile} >/usr/share/wechatpush/errlog getgateway "reboot" sleep 2 && reboot && exit fi - [ "$retry_count" -eq "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】已经重启路由2次,修复失败,请主人自行修复哦" >> ${logfile} - elif [ "$network_disconnect_event" -eq "2" ] ;then - [ "$retry_count" -lt "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试重启网络,当前第 $retry_count 次 " >> ${logfile} && network_restart - [ "$retry_count" -eq "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】已经重启网络2次,修复失败,请主人自行修复哦 " >> ${logfile} + [ "$retry_count" -eq "3" ] && log_change "【!!!】已经重启路由2次,修复失败,请主人自行修复哦" + elif [ "$network_disconnect_event" -eq "2" ]; then + [ "$retry_count" -lt "3" ] && log_change "【!!!!】正在尝试重启网络,当前第 $retry_count 次 " && network_restart + [ "$retry_count" -eq "3" ] && log_change "【!!!】已经重启网络2次,修复失败,请主人自行修复哦 " fi fi elif [ -f /usr/share/wechatpush/autoreboot_count ]; then @@ -833,315 +981,396 @@ function rand_geturl(){ enable_detection sleep $sleeptime fi - continue done rm -f /usr/share/wechatpush/autoreboot_count >/dev/null 2>&1 } # 检测 ip 状况 -function ip_changes(){ +ip_changes() { local IPv4_URL="网络接口" local IPv6_URL="网络接口" - [ "$get_ipv4_mode" -eq "1" ] && getip wanipv4 > "$output_dir/IPv4" & - [ "$get_ipv6_mode" -eq "1" ] && getip wanipv6 > "$output_dir/IPv6" & + [ "$get_ipv4_mode" -eq "1" ] && getip wanipv4 >"$output_dir/IPv4" & + [ "$get_ipv6_mode" -eq "1" ] && getip wanipv6 >"$output_dir/IPv6" & + [ "$get_ipv4_mode" -eq "2" ] && local IPv4=$(getip hostipv4) && local IPv4_URL=$(echo ${IPv4} | jq -r '.URL') && local IPv4=$(echo ${IPv4} | jq -r '.IP') + [ "$get_ipv6_mode" -eq "2" ] && local IPv6=$(getip hostipv6) && local IPv6_URL=$(echo ${IPv6} | jq -r '.URL') && local IPv6=$(echo ${IPv6} | jq -r '.IP') wait_and_cat - [ "$get_ipv4_mode" -eq "2" ] && local IPv4=`getip hostipv4` && local IPv4_URL=`echo ${IPv4}|jq -r '.URL'` && local IPv4=`echo ${IPv4}|jq -r '.IP'` - [ "$get_ipv6_mode" -eq "2" ] && local IPv6=`getip hostipv6` && local IPv6_URL=`echo ${IPv6}|jq -r '.URL'` && local IPv6=`echo ${IPv6}|jq -r '.IP'` - if [ "$1" ] && [ $1 == "getip" ]; then + if [ "$1" ] && [ "$1" == "getip" ]; then echo "IPv4:$IPv4
地址:$(get_ip_attribution $IPv4)
接口:$IPv4_URL
IPv6:$IPv6
地址:$(get_ip_attribution $IPv6)
接口:$IPv6_URL" return fi - # 存在临时文件 - if [ -f ${dir}ip ]; then - local last_IPv4=$(cat "${dir}ip"|grep IPv4|awk '{print $2}'|grep -v "^$"|sort -u|head -n1) - local last_IPv6=$(cat "${dir}ip"|grep IPv6|awk '{print $2}'|grep -v "^$"|sort -u|head -n1) - if [ "$get_ipv4_mode" -ne "0" ] && [ ! -z "$IPv4" ] && ( ! echo ${IPv4}|grep -w -q ${last_IPv4} ); then - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前 IP:${IPv4} from:${IPv4_URL}" >> ${logfile} - echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $last_IPv6 >> ${dir}ip - title="IP 地址变化" - content="${content}${str_splitline}${str_title_start} IP 地址变化${str_title_end}${str_linefeed}${str_tab}当前 IP:${IPv4}" - fi - - if [ "$get_ipv6_mode" -ne "0" ] && [ ! -z "$IPv6" ] && ( ! echo "$IPv6"|grep -w -q ${last_IPv6} ); then - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前 IPv6:${IPv6} from:${IPv6_URL}" >> ${logfile} - echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $IPv6 >> ${dir}ip - [ -z "$title" ] && title="IPv6 地址变化" - [ ! -z "$title" ] && title="IP 地址变化" - content="${content}${str_splitline}${str_title_start} IPv6 地址变化${str_title_end}${str_linefeed}${str_tab}当前 IPv6:${IPv6}" - fi + local last_IPv4=$(jq -r '.address[] | select(.IPv4 != null) | .IPv4' "$devices_json") + local last_IPv6=$(jq -r '.address[] | select(.IPv6 != null) | .IPv6' "$devices_json") + silent_run LockFile lock - # 临时文件目录为空 - else - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}路由器已经重启!" >> ${logfile} - [ "$get_ipv4_mode" -ne "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前 IP: ${IPv4} from:${IPv4_URL}" >> ${logfile} - [ "$get_ipv6_mode" -ne "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前 IPv6: ${IPv6} from:${IPv6_URL}" >> ${logfile} - echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $IPv6 >> ${dir}ip + if [ -z "$last_IPv4" ] && [ -z "$last_IPv6" ]; then + log_change "${disturb_text}路由器已经重启!" title="路由器重新启动" content="${content}${str_splitline}${str_title_start} 路由器重新启动${str_title_end}" - [ "$get_ipv4_mode" -ne "0" ] && content="${content}${str_linefeed}${str_tab}当前IP:${IPv4}" - [ "$get_ipv6_mode" -ne "0" ] && content="${content}${str_linefeed}${str_tab}当前IPv6:${IPv6}" + fi + if [ "$get_ipv4_mode" -ne "0" ] && [ -n "$IPv4" ] && [ "$IPv4" != "$last_IPv4" ]; then + log_change "${disturb_text}当前 IP:${IPv4} from:${IPv4_URL}" + [ -z "$title" ] && title="IP 地址变化" + [ -z "$content" ] && content="${content}${str_splitline}${str_title_start} IP 地址变化${str_title_end}" + content="${content}${str_linefeed}${str_tab}当前 IP: ${IPv4}" + jq --arg IPv4 "$IPv4" '.address[0].IPv4 = $IPv4' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" + + fi + if [ "$get_ipv6_mode" -ne "0" ] && [ -n "$IPv6" ] && [ "$IPv6" != "$last_IPv6" ]; then + log_change "${disturb_text}当前 IPv6:${IPv6} from:${IPv6_URL}" + [ -z "$title" ] && title="IPv6 地址变化" + [ -z "$content" ] && content="${content}${str_splitline}${str_title_start} IPv6 地址变化${str_title_end}" + content="${content}${str_linefeed}${str_tab}当前 IPv6:${IPv6}" + jq --arg IPv6 "$IPv6" '.address[0].IPv6 = $IPv6' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" fi # IP 变化,悄咪咪的重启 zerotier - if [ ! -z "$content" ] && [ -n "$zerotier_helper" ] && [ "$zerotier_helper" -eq "1" ];then + if [ -n "$content" ] && [ -n "$zerotier_helper" ] && [ "$zerotier_helper" -eq "1" ]; then [ -z "$zerotier_enabled" ] && zerotier_enabled=$(uci get zerotier.sample_config.enabled) - if [ ! -z "$zerotier_enabled" ] && [ $zerotier_enabled -eq "1" ] ; then + if [ -n "$zerotier_enabled" ] && [ $zerotier_enabled -eq "1" ]; then /etc/init.d/zerotier restart >/dev/null 2>&1 fi fi + silent_run LockFile unlock } # 检测设备上线 -function up(){ - [ -f ${dir}ipAddress ] && ( cat ${dir}ipAddress|grep -q -w $1 ) && return - - local ip_mac=`getmac $1` - local ip_name=`getname ${1} ${ip_mac}` - local ip_interface=`getinterface ${ip_mac}` - getping ${1} ${ip_mac} ${up_timeout} "1";local ping_online=$? +up() { + local ip="$1" + [ -z "$ip" ] && return 1 + # 如果当前 IP 为在线,退出 + jq -e --arg ip "$ip" '.devices[] | select(.ip == $ip and .status == "online") | .ip' "$devices_json" >/dev/null && return + local mac=$(getmac "$ip") + getping "$ip" "$mac" "$up_timeout" "1" + local ping_online=$? # 连通 if [ "$ping_online" -eq "0" ]; then - LockFile lock - [ ! -z "$up_down_push_blacklist" ] && local tmp_mac=`echo "${up_down_push_blacklist}"|grep -w -i ${ip_mac}` - [ ! -z "$up_down_push_whitelist" ] && local tmp_mac=`echo "${up_down_push_whitelist}"|grep -w -i ${ip_mac}` - echo "{'ip': '${1}','mac': '${ip_mac}','name': '${ip_name}','uptime': '0秒','interface': '${ip_interface}','usage': ''}" > ${dir}client/${1} - # ??? - if [ ! -z "$tmp_mac" ] && ( cat ${dir}ipAddress|grep -q -w -i ${tmp_mac} ); then - usage down ${1} - echo "${1} ${ip_mac} ${ip_name// /_} `date +%s` ${ip_interface}" >> ${dir}ipAddress - LockFile unlock && return - # ??? - elif [ ! -z "$tmp_mac" ] && [ -f "${dir}tmp_downlist" ] && ( cat ${dir}tmp_downip|grep -q -w -i ${tmp_mac} ); then - local tmp_downip=`cat ${dir}tmp_downlist|grep -w -i ${tmp_mac}|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` - usage down $tmp_downip - sed -i "/^${tmp_downip} /d" ${dir}tmp_downlist - LockFile unlock && return - fi - # 从离线二次验证区恢复信息 - [ -f "${dir}tmp_downlist" ] && local tmp_downip=`cat ${dir}tmp_downlist|grep -w ${1}|grep -v "^$"|sort -u|head -n1` - if [ ! -z "$tmp_downip" ]; then - cat ${dir}tmp_downlist|grep -w ${1}|grep -v "^$"|sort -u|head -n1 >> ${dir}ipAddress - sed -i "/^${1} /d" ${dir}tmp_downlist + silent_run LockFile lock + local time_up=$(date +%s) + + # 如果 IP 不存在,或者 http_access 键值为空,检查 http_access 参数 + # 检查 http_access 参数时间过长,考虑只在第一次连通时检查 + #jq -e --arg ip "$ip" 'any(.devices[]; .ip == $ip and (.http_access | length == 0)) or (.devices | map(select(.ip == $ip)) | length == 0)' "$devices_json" >/dev/null && local http_access=$(check_http_access "$ip") + + # 如果 json 文件中能找到相同的 MAC,恢复数据 + if [ "$mac" != "unknown" ] && jq -e --arg mac "$mac" '.devices[] | select(.mac == $mac) | .mac' "$devices_json" >/dev/null; then + # 如果是待检测离线设备,json 文件中依然有信息,重置 IP、在线时间、流量,修改在线状态为 online + jq --arg ip "$ip" --arg mac "$mac" --arg time_up "$time_up" '.devices |= map(if .mac == $mac then .ip = $ip | .uptime = $time_up | .usage = "" | .status = "online" else . end)' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" # up else - usage down $1 - echo "$1 ${ip_mac} ${ip_name// /_} `date +%s` ${ip_interface}" >> ${dir}ipAddress - blackwhitelist ${ip_mac};local ip_blackwhite=$? - [ -f "${dir}send_enable.lock" ] || [ -z "$notification_online" ] || [ -z "$ip_blackwhite" ] && LockFile unlock && return - [ -z "$ip_blackwhite" ] || [ "$ip_blackwhite" -ne "0" ] && LockFile unlock && return - - [ -f "${dir}title" ] && local title=`cat ${dir}title` - [ -f "${dir}content" ] && local content=`cat ${dir}content` + usage down ${ip} + # 删除当前 IP 对应的键值,重新写入 + jq --arg ip "$ip" 'del(.devices[] | select(.ip == $ip))' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" + local name=$(getname "$ip" "$mac") + local interface=$(getinterface "ip" "$mac") + local http_access=$(check_http_access "$ip") + new_device='{ + "name": "'"${name}"'", + "ip": "'"${ip}"'", + "mac": "'"${mac}"'", + "interface": "'"${interface}"'", + "uptime": "'"${time_up}"'", + "usage": "", + "http_access": "'"${http_access}"'", + "status": "online" + }' + jq --argjson newdevice "$new_device" '.devices += [$newdevice]' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" + + blackwhitelist ${mac} + local ip_blackwhite=$? + [ -f "${dir}/send_enable.lock" ] || [ -z "$notification_online" ] || [ -z "$ip_blackwhite" ] && silent_run LockFile unlock && return + [ -z "$ip_blackwhite" ] || [ "$ip_blackwhite" -ne "0" ] && silent_run LockFile unlock && return + + [ -f "${dir}/title" ] && local title=$(cat "${dir}/title") + [ -f "${dir}/content" ] && local content=$(cat "${dir}/content") + if [ -z "$title" ]; then - local title="$ip_name 连接了你的路由器" - local content="${str_splitline}${str_title_start} 新设备连接${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" - elif ( echo ${title}|grep -q "连接了你的路由器" ); then - local title="${ip_name} ${title}" - local content="${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" + local title="${name} 连接了你的路由器" + local content_title="${str_title_start} 新设备连接${str_title_end}" + elif echo "$title" | grep -q "连接了你的路由器"; then + local title="${name} ${title}" else local title="设备状态变化" - local content="${str_splitline}${str_title_start} 新设备连接${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" + local content_title="${str_title_start} 新设备连接${str_title_end}" fi - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}新设备 ${ip_name} ${1} 连接了">> ${logfile} - #[ ! -z "$up_down_push_blacklist" ] && local title="你偷偷关注的设备上线了" - [ ! -z "$title" ] && echo "$title" >${dir}title - [ ! -z "$content" ] && echo -n "$content" >>${dir}content + + local content_name="${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${name}" + local content_ip="${str_linefeed}${str_tab}客户端IP:${str_space}${str_space}${str_space}${str_space}${ip}" + local content_mac="${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}" + local content_interface="${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${interface}" + + content="${str_splitline}${content_title}${content_name}${content_ip}${content_mac}${content_interface}" + + log_change "${disturb_text}新设备 ${name} ${ip} 连接了" + #[ -n "$up_down_push_blacklist" ] && local title="你偷偷关注的设备上线了" + [ -n "$title" ] && echo "$title" >"${dir}/title" + [ -n "$content" ] && echo -n "$content" >>"${dir}/content" fi fi - LockFile unlock + silent_run LockFile unlock } # 检测设备离线 -function down(){ - local ip_mac=`getmac $1` - local ip_name=`getname ${1} ${ip_mac}` - local ip_interface=`getinterface ${ip_mac}` +down() { + local ip="$1" + [ -z "$ip" ] && return 1 + local mac=$(getmac "$ip") tmp_timeout=$down_timeout && tmp_retry_count=$timeout_retry_count - [ -n "$only_timeout_push" ] && blackwhitelist ${ip_mac};local ip_blackwhite=$? && [ "$ip_blackwhite" -ne "0" ] && tmp_timeout=10 && tmp_retry_count=2 - getping ${1} ${ip_mac} ${tmp_timeout} ${tmp_retry_count};local ping_online=$? - # 离线,置入二次验证区 + [ -n "$only_timeout_push" ] && blackwhitelist "$mac" + local ip_blackwhite=$? && [ "$ip_blackwhite" -ne "0" ] && tmp_timeout=10 && tmp_retry_count=2 + getping "$ip" "$mac" "$tmp_timeout" "$tmp_retry_count" + local ping_online=$? + silent_run LockFile lock + # 离线,置入待验证区 if [ "$ping_online" -eq "1" ]; then - LockFile lock - [ ! -f "${dir}send_enable.lock" ] && cat ${dir}ipAddress|grep -w ${1}|grep -v "^$"|sort -u|head -n1 >> ${dir}tmp_downlist - sed -i "/^${1} /d" ${dir}ipAddress - rm -f ${dir}client/${1} >/dev/null 2>&1 - LockFile unlock + # 修改为 unknown + if jq -e --arg ip "$ip" '.devices[] | select(.ip == $ip and .status == "online") | .ip' "$devices_json" >/dev/null; then + jq --arg ip "$ip" '.devices |= map(if .ip == $ip then .status = "unknown" else . end)' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" + elif jq -e --arg ip "$ip" '.devices[] | select(.ip == $ip and .status == "unknown") | .ip' "$devices_json" >/dev/null; then + jq --arg ip "$ip" '.devices |= map(if .ip == $ip then .status = "offline" else . end)' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" + fi # 更新主机名或 MAC else - local tmp_name=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` - local tmp_mac=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` - if [ "${ip_name// /_}" != "${tmp_name}" ] || [ "${ip_mac}" != "${tmp_mac}" ]; then - LockFile lock - olduptime=$(cat ${dir}ipAddress|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1) - sed -i "/^${1} /d" ${dir}ipAddress - [ "${ip_mac}" != "${tmp_mac}" ] && ip_name="unknown" # MAC 变化时应删除主机名重新获取 - echo "$1 ${ip_mac} ${ip_name// /_} ${olduptime} ${ip_interface}" >> ${dir}ipAddress - LockFile unlock + local name=$(getname "$ip" "$mac") + local interface=$(getinterface "ip" "$mac") + + # 检查是否有变化 + local current_mac=$(jq -r --arg ip "$ip" '.devices[] | select(.ip == $ip) | .mac' "$devices_json") + local current_name=$(jq -r --arg ip "$ip" '.devices[] | select(.ip == $ip) | .name' "$devices_json") + local current_interface=$(jq -r --arg ip "$ip" '.devices[] | select(.ip == $ip) | .interface' "$devices_json") + + if [[ "$current_mac" != "$mac" || "$current_name" != "$name" || "$current_interface" != "$interface" ]]; then + jq --arg ip "$ip" --arg new_mac "$mac" --arg new_name "$name" --arg new_interface "$interface" ' + .devices |= map( + if .ip == $ip then + .mac = $new_mac | + .name = $new_name | + .interface = $new_interface + else . + end + ) + ' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" fi - local time_up=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - local time1=`date +%s` - local time1=$(time_for_humans `expr ${time1} - ${time_up}`) - echo "{'ip': '${1}','mac': '${ip_mac}','name': '${ip_name}','uptime': '${time1}','interface': '${ip_interface}','usage': '`usage get $ip_mac`'}" > ${dir}client/${1} + fi + silent_run LockFile unlock } # 设备离线通知 -function down_send(){ - [ ! -f "${dir}tmp_downlist" ] && return +down_send() { + # 如果没有离线设备,退出 + [ "$(jq '.devices | map(select(.status == "offline")) | length' "$devices_json")" -eq 0 ] && return - local IPLIST=`cat ${dir}tmp_downlist|awk '{print $1}'` + local IPLIST=$(jq -r '.devices[] | select(.status == "offline") | .ip' "$devices_json" | sort -u) for ip in $IPLIST; do - local ip_mac=`getmac ${ip}` - blackwhitelist ${ip_mac};local ip_blackwhite=$? + local mac=$(getmac "$ip") + blackwhitelist "$mac" + local ip_blackwhite=$? [ -z "$notification_offline" ] || [ -z "$ip_blackwhite" ] && continue [ -z "$ip_blackwhite" ] || [ "$ip_blackwhite" -ne "0" ] && continue - [ ! -z "$up_down_push_blacklist" ] && local tmp_mac=`echo "${up_down_push_blacklist}"|grep -w -i ${ip_mac}` - [ ! -z "$up_down_push_whitelist" ] && local tmp_mac=`echo "${up_down_push_whitelist}"|grep -w -i ${ip_mac}` - [ ! -z "$tmp_mac" ] && ( cat ${dir}ipAddress|grep -q -w -i ${tmp_mac} ) && continue - - local ip_name=`getname ${ip} ${ip_mac}` - local time_up=`cat ${dir}tmp_downlist|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - local ip_total=`usage get $ip_mac` && [ ! -z "$ip_total" ] && local ip_total="${str_linefeed}${str_tab}总计流量: ${str_space}${str_space}${str_space}${str_space}${ip_total}" - local time1=`date +%s` - local time1=$(time_for_humans `expr ${time1} - ${time_up}`) + + local name=$(getname "$ip" "$mac") + local time_up=$(jq -r --arg ip "$ip" '.devices[] | select(.ip == $ip and .status == "unknown") | .uptime' "$devices_json") + local ip_total=$(usage get "$mac") + [ -n "$ip_total" ] && ip_total="${str_linefeed}${str_tab}总计流量: ${str_space}${str_space}${str_space}${str_space}${ip_total}" + local time1=$(date +%s) + local time1=$(time_for_humans $(expr ${time1} - ${time_up})) + if [ -z "$title" ]; then - title="${ip_name} 断开连接" - content="${content}${str_splitline}${str_title_start} 设备断开连接${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" - elif ( echo "$title"|grep -q "断开连接" ); then - title="${ip_name} ${title}" - content="${content}${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + title="${name} 断开连接" + content_title="${str_splitline}${str_title_start} 设备断开连接${str_title_end}" + elif echo "$title" | grep -q "断开连接"; then + title="${name} ${title}" + content_title="" else title="设备状态变化" - content="${content}${str_splitline}${str_title_start} 设备断开连接${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + content_title="${str_splitline}${str_title_start} 设备断开连接${str_title_end}" fi - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}设备 ${ip_name} ${ip} 断开连接 " >> ${logfile} - done - rm -f ${dir}tmp_downlist >/dev/null 2>&1 + local content_name="${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${name}" + local content_ip="${str_linefeed}${str_tab}客户端IP:${str_space}${str_space}${str_space}${str_space}${ip}" + local content_mac="${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${ip_total}" + local content_time="${str_linefeed}${str_tab}在线时间:${str_space}${str_space}${str_space}${str_space}${time1}" + + content="${content}${content_title}${content_name}${content_ip}${content_mac}${content_time}" + + log_change "${disturb_text}设备 ${name} ${ip} 断开连接 " + done + silent_run LockFile lock + jq 'del(.devices[] | select(.status == "offline"))' "$devices_json" >"$tmp_devices_json" && mv "$tmp_devices_json" "$devices_json" + silent_run LockFile unlock } # 当前设备列表 -function current_device(){ - ( echo "$lite_enable"|grep -q "content" ) || ( echo "$lite_enable"|grep -q "device" ) && return - [ -f ${dir}ipAddress ] && local logrow=$(grep -c "" ${dir}ipAddress) || local logrow="0";[ $logrow -eq "0" ] && return +current_device() { + (echo "$lite_enable" | grep -q "content") || (echo "$lite_enable" | grep -q "device") && return + local logrow=$(jq '.devices | map(select(.status == "online")) | length' "$devices_json") + [ $logrow -eq "0" ] && return + + [ -f "${dir}/usage.db" ] && local ip_total_db="总计流量${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}" + local explain="${str_splitline}${str_title_start} 现有在线设备 ${logrow} 台,具体如下${str_title_end}" + [ $logrow -eq "0" ] && local explain="${send_content}${str_splitline}${str_title_start} 当前无在线设备${str_title_end}" + local header="${str_linefeed}${str_tab}IP 地址${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${ip_total_db}${str_space}${str_space}${str_space}${str_space}在线时间${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}客户端名" + + content="${explain}${header}" + + if [ -z "$defaultSortColumn" ] || [ "$defaultSortColumn" = "ip" ]; then + # 按 IP 排序 + local IPLIST=$(jq -r '.devices[] | select(.status == "online") | .ip' "$devices_json" | sort -uV) + + else + # 按在线时间(短到长) + local IPLIST=$(jq -r '.devices | sort_by(-(.uptime | tonumber)) | .[] | select(.status == "online") | .ip' "$devices_json") + fi + + local longest_ip=$(echo "$IPLIST" | awk '{ print length, $0 }' | sort -nr | head -n 1 | cut -d " " -f2-) + #local max_length=$(("${#longest_ip}" + 1)) + local max_length="${#longest_ip}" - [ -f ${dir}usage.db ] && local ip_total_db="总计流量${str_space}${str_space}${str_space}${str_space}" - content="${content}${str_splitline}${str_title_start} 现有在线设备 ${logrow} 台,具体如下${str_title_end}${str_linefeed}${str_tab}IP 地址${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${ip_total_db}客户端名" - local IPLIST=`cat ${dir}ipAddress|awk '{print $1}'` for ip in $IPLIST; do - local ip_mac=`getmac ${ip}` - local ip_total=`usage get ${ip_mac}` - local ip_name=`getname ${ip} ${ip_mac}` - local ip_name=`cut_str "$ip_name" "15"` - if [ "${#ip}" -lt "15" ]; then - local n=`expr 15 - ${#ip}` - for i in `seq 1 $n`; do - local ip="${ip}${str_space}" + local mac=$(getmac "$ip") + local total=$(usage get "$mac") + local name=$(getname "$ip" "$mac") + local name=$(cut_str "$name" "15") + local time_up=$(jq -r --arg ip "$ip" '.devices[] | select(.ip == $ip) | .uptime' "$devices_json") + local time_online=$(time_for_humans $(($(date +%s) - time_up))) + # 如果 IP 的字符长度小于 max_length,用空格补齐,方便对齐 + if [ "${#ip}" -lt "$max_length" ]; then + local n=$(expr "$max_length" - "${#ip}") + for i in $(seq 1 $n); do + ip="${ip}${str_space}" done unset i n fi - if [ ! -z "$ip_total" ]; then - local n=`expr 11 - ${#ip_total}` - for i in `seq 1 $n`; do - local ip_total="${ip_total}${str_space}" + if [ -n "$total" ]; then + local n=$(expr 11 - ${#total}) + for i in $(seq 1 $n); do + total="${total}${str_space}" done + unset i n + total="${total}${str_space}|${str_space}" fi - content="${content}${str_linefeed}${str_tab}${ip}${ip_total}${ip_name}" - unset i n ip_total ip_mac ip_name + content="${content}${str_linefeed}${str_space}${ip}${str_space}|${str_space}${total}${time_online}${str_space}|${str_space}${name}" done + [ -f "${dir}/send.lock" ] && echo "$content" >"$output_dir/current_device" && unset content } # 检测 cpu 状态 -function cpu_load(){ +cpu_load() { + [ -z "$cpu_notification_duration" ] && cpu_notification_duration=$(time_for_humans $cpu_notification_delay) if [ -n "$notification_temp" ] && [ -n "$temperature_threshold" ]; then - [ -z "$temp_last_overload_time" ] && temp_last_overload_time=`date +%s` - local cpu_temp=`soc_temp`; + [ -z "$temp_last_overload_time" ] && temp_last_overload_time=$(date +%s) + local cpu_temp=$(soc_temp) - if [ ! -z "$cpu_temp" ] && [ `expr $cpu_temp \> $temperature_threshold` -eq "1" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 温度过高: ${cpu_temp}" >> ${logfile} + if [ -n "$cpu_temp" ] && [ $(expr $cpu_temp \> $temperature_threshold) -eq "1" ]; then + log_change "【!!警报!!】 CPU 温度过高: ${cpu_temp}" else - temp_last_overload_time=`date +%s` + temp_last_overload_time=$(date +%s) fi - if [ ! -z "$cpu_temp" ] && [ "$((`date +%s`-$temp_last_overload_time))" -ge "$cpu_threshold_duration" ] && [ -z "$temperaturecd_time" ]; then + if [ -n "$cpu_temp" ] && [ "$(($(date +%s) - $temp_last_overload_time))" -ge "$cpu_threshold_duration" ] && [ -z "$temperaturecd_time" ]; then title="CPU 温度过高!" - temperaturecd_time=`date +%s` - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text} CPU 温 度过高: ${cpu_temp}" >> ${logfile} - content="${content}${str_splitline}${str_title_start} CPU 温度过高${str_title_end}${str_linefeed}${str_tab}CPU 温度已连续 $(time_for_humans $cpu_load_threshold) 超过预设${str_linefeed}${str_tab}接下来 $(time_for_humans $cpu_notification_delay) 不再提示${str_linefeed}${str_tab}当前温度:${cpu_temp}℃" - elif [ ! -z "$temperaturecd_time" ] && [ "$((`date +%s`-$temperaturecd_time))" -ge "$cpu_notification_delay" ] ;then + temperaturecd_time=$(date +%s) + log_change "${disturb_text} CPU 温度过高: ${cpu_temp}" + + local cpu_overload_duration=$(time_for_humans $(($(date +%s) - $cpu_last_overload_time))) + + local cpu_overload_info="${str_splitline}${str_title_start} CPU 温度过高${str_title_end}" + local cpu_overload_info="${cpu_overload_info}${str_linefeed}${str_tab}CPU 温度已连续 ${cpu_overload_duration} 超过预设" + local cpu_overload_info="${cpu_overload_info}${str_linefeed}${str_tab}接下来 ${cpu_notification_duration} 不再提示" + local cpu_overload_info="${cpu_overload_info}${str_linefeed}${str_tab}当前温度:${cpu_temp}℃" + + content="${content}${cpu_overload_info}" + elif [ -n "$temperaturecd_time" ] && [ "$(($(date +%s) - $temperaturecd_time))" -ge "$cpu_notification_delay" ]; then unset temperaturecd_time fi fi -$(time_for_humans $cpu_notification_delay) + if [ -n "$notification_load" ] && [ -n "$cpu_load_threshold" ]; then - [ -z "$cpu_last_overload_time" ] && cpu_last_overload_time=`date +%s` - local cpu_fuzai=`cat /proc/loadavg|awk '{print $1}'` 2>/dev/null + [ -z "$cpu_last_overload_time" ] && cpu_last_overload_time=$(date +%s) + local cpu_fuzai=$(cat /proc/loadavg | awk '{print $1}') 2>/dev/null - if [ ! -z "$cpu_fuzai" ] && [ `expr $cpu_fuzai \> $cpu_load_threshold` -eq "1" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 负载过高: ${cpu_fuzai}" >> ${logfile} + if [ -n "$cpu_fuzai" ] && [ $(expr $cpu_fuzai \> $cpu_load_threshold) -eq "1" ]; then + log_change "【!!警报!!】 CPU 负载过高: ${cpu_fuzai}" cputop log - elif [ ! -z "$cpu_fuzai" ]; then - cpu_last_overload_time=`date +%s` + elif [ -n "$cpu_fuzai" ]; then + cpu_last_overload_time=$(date +%s) fi - if [ ! -z "$cpu_fuzai" ] && [ "$((`date +%s`-$cpu_last_overload_time))" -ge "$cpu_threshold_duration" ] && [ -z "$cpucd_time" ]; then + if [ -n "$cpu_fuzai" ] && [ "$(($(date +%s) - $cpu_last_overload_time))" -ge "$cpu_threshold_duration" ] && [ -z "$cpucd_time" ]; then unset getlogtop - if [ ! -z "$title" ] && ( echo "$title"|grep -q "过高" ); then + + if [ -n "$title" ] && (echo "$title" | grep -q "过高"); then title="设备报警!" else title="CPU 负载过高!" fi - cpucd_time=`date +%s` - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text} CPU 负 载过高: ${cpu_fuzai}" >> ${logfile} - content="${content}${str_splitline}${str_title_start} CPU 负载过高${str_title_end}${str_linefeed}${str_tab}CPU 负载已连续 $(time_for_humans $cpu_load_threshold) 超过预设${str_linefeed}${str_tab}接下来 $(time_for_humans $cpu_notification_delay) 不再提示${str_linefeed}${str_tab}当前负载:${cpu_fuzai}" + + cpucd_time=$(date +%s) + log_change "${disturb_text} CPU 负载过高: ${cpu_fuzai}" + + local cpu_load_duration=$(time_for_humans $(($(date +%s) - $cpu_last_overload_time))) + + local cpu_load_info="${str_splitline}${str_title_start} CPU 负载过高${str_title_end}" + local cpu_load_info="${cpu_load_info}${str_linefeed}${str_tab}CPU 负载已连续 ${cpu_load_duration} 超过预设" + local cpu_load_info="${cpu_load_info}${str_linefeed}${str_tab}接下来 ${cpu_notification_duration} 不再提示" + local cpu_load_info="${cpu_load_info}${str_linefeed}${str_tab}当前负载:${cpu_fuzai}" + + content="${content}${cpu_load_info}" + cputop - elif [ ! -z "$cpucd_time" ] && [ "$((`date +%s`-$cpucd_time))" -ge "$cpu_notification_delay" ] ;then + elif [ -n "$cpucd_time" ] && [ "$(($(date +%s) - $cpucd_time))" -ge "$cpu_notification_delay" ]; then unset cpucd_time fi fi } # CPU 占用前三 -function cputop(){ +cputop() { [ -z "$1" ] && content="${content}${str_splitline}${str_title_start} 当前 CPU 占用前三的进程${str_title_end}" - local gettop=`top -bn 1|grep -v "top -bn 1"` - for i in `seq 5 7`; do - local top_name=`echo "${gettop}"|awk 'NR=='${i}|awk '{print ($8 ~ /\/bin\/sh|\/bin\/bash/) ? $9 : $8}'` - local top_load=`echo "${gettop}"|awk 'NR=='${i}|awk '{print $7}'` + local gettop=$(top -bn 1 | grep -v "top -bn 1") + for i in $(seq 5 7); do + local top_name=$(echo "${gettop}" | awk 'NR=='${i} | awk '{print ($8 ~ /\/bin\/sh|\/bin\/bash/) ? $9 : $8}') + local top_load=$(echo "${gettop}" | awk 'NR=='${i} | awk '{print $7}') local temp_top="${top_name} ${top_load}" - [ ! -z "$1" ] && local logtop="$logtop $temp_top" + [ -n "$1" ] && local logtop="$logtop $temp_top" [ -z "$1" ] && content="${content}${str_linefeed}${str_tab}${temp_top}" done unset i - [ ! -z "$1" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 占用前三: ${logtop}" >> ${logfile} + [ -n "$1" ] && log_change "【!!警报!!】 CPU 占用前三: ${logtop}" } # 检测硬盘状态 -function get_disk() { - mkdir -p "${dir}disk_info" +get_disk() { + mkdir -p "${dir}/disk_info" local_disk_names=() - pve_disk_names=() + remote_disk_names=() + + # 获取磁盘名称函数 + get_disk_names() { + local disk_names=($(lsblk -dno NAME,TYPE 2>/dev/null | awk '$2=="disk" && $1 !~ /^mtd/ && $1 !~ /^ubiblock/ {print $1}' | sort -u)) + [ -z "$disk_names" ] && disk_names=($(df -h 2>/dev/null | awk '$1 ~ /^\/dev/ && !/^\/dev\/(mtd|ubiblock)/ {if ($NF ~ /^\/[a-zA-Z0-9]/) { sub("/dev/", "", $1); sub(/[0-9]+$/, "", $1); print $1 }}' | sort -u)) + echo "${disk_names[@]}" + } # 查询本地硬盘名 - local_disk_names=($(lsblk 2>/dev/null | awk '$NF=="disk" {print $1}' | sort -u)) - # 如未安装 lsblk,查找以 /dev/ 开头,但挂载点不是根目录 / 的设备,然后提取硬盘的名称(准确性较低) - [ -z "$local_disk_names" ] && local_disk_names=($(df -h 2>/dev/null | awk '$1 ~ /^\/dev/ {if ($NF ~ /^\/[a-zA-Z0-9]/) { sub("/dev/", "", $1); sub(/[0-9]+$/, "", $1); print $1 }}' | sort -u)) + local_disk_names=($(get_disk_names)) local_disk_tags=($(for _ in "${local_disk_names[@]}"; do echo "local"; done)) # 查询远程硬盘名 if [ -n "$server_host" ]; then ssh_command="ssh -o StrictHostKeyChecking=yes -o BatchMode=yes -i /root/.ssh/id_rsa root@${server_host} -p ${server_port}" - pve_disk_names=($(eval ${ssh_command} lsblk | awk '$NF=="disk" {print $1}' | sort -u)) - pve_disk_tags=($(for _ in "${pve_disk_names[@]}"; do echo "pve"; done)) + remote_disk_names=$($ssh_command "$(declare -f get_disk_names); get_disk_names" | tr -d '\r') + remote_disk_names=($remote_disk_names) + remote_disk_tags=($(for _ in "${remote_disk_names[@]}"; do echo "remote"; done)) fi # 合并本地和远程硬盘名及标记 - all_disk_names=("${local_disk_names[@]}" "${pve_disk_names[@]}") - all_disk_tags=("${local_disk_tags[@]}" "${pve_disk_tags[@]}") + all_disk_names=("${local_disk_names[@]}" "${remote_disk_names[@]}") + all_disk_tags=("${local_disk_tags[@]}" "${remote_disk_tags[@]}") for i in "${!all_disk_names[@]}"; do tmp_name=${all_disk_names[i]} @@ -1150,11 +1379,11 @@ function get_disk() { unset tmp_command disk_type disk_err # 判断硬盘类型 - if [ "$tmp_tag" == "pve" ]; then + if [ "$tmp_tag" == "remote" ]; then tmp_command="$ssh_command" - disk_type="_pve" + disk_type="_remote" fi - file_path="${dir}disk_info/${tmp_name}${disk_type}" + file_path="${dir}/disk_info/${tmp_name}${disk_type}" # 如果不能获取值,使用分区名重试(因为不清楚是 OpenWrt 的原因还是 smartctl 版本的原因,使用出错重试的方式) eval ${tmp_command} smartctl -i -n standby "/dev/${tmp_name}" 2>/dev/null | grep -qE "$error_pattern" && { @@ -1165,25 +1394,25 @@ function get_disk() { # 手上的硬盘不能休眠,不确定命令是否会唤醒硬盘,每天只运行一次 last_disk_time=$(date -r "${file_path}" +%s 2>/dev/null) || last_disk_time=0 - if [ $(( $(date +%s) - $last_disk_time )) -gt 86000 ]; then + if [ $(($(date +%s) - $last_disk_time)) -gt 86000 ]; then disk_info=$(eval ${tmp_command} smartctl -i -n standby "/dev/${tmp_name}" 2>/dev/null) echo "$disk_info" | grep -qE "$error_pattern" && { continue - } || \ - echo "$disk_info" | grep -q "STANDBY" && { - echo "$disk_info" > "${file_path}" - } || \ - eval ${tmp_command} smartctl -a -j /dev/${tmp_name} 2>/dev/null> ${file_path} + } || + echo "$disk_info" | grep -q "STANDBY" && { + echo "$disk_info" >"${file_path}" + } || + eval ${tmp_command} smartctl -a -j /dev/${tmp_name} 2>/dev/null >${file_path} fi # 硬盘状态 - if [ -f "${file_path}" ] && [ -s "${file_path}" ] && ( ! cat "${file_path}" | grep -q -v "STANDBY" ); then + if [ -f "${file_path}" ] && [ -s "${file_path}" ] && (! cat "${file_path}" | grep -q -v "STANDBY"); then disk_name=$(awk '/Device Model/{print $NF}' "$file_path") [[ -n $disk_name && $disk_name != null && $disk_name != 0 ]] && { disk_name=$(cut_str "$disk_name" "20") disk_name="${disk_name}_$(eval ${tmp_command} lsblk -o NAME,SIZE | awk "/^${tmp_name}/ {print \$NF}")" } - printf "${str_linefeed}${str_title_start} 硬盘名称:${disk_name}${disk_type}${str_title_end}${str_linefeed}${str_tab}硬盘休眠中" >> "$output_dir/get_disk" + printf "${str_linefeed}${str_title_start} 硬盘名称:${disk_name}${disk_type}${str_title_end}${str_linefeed}${str_tab}硬盘休眠中" >>"$output_dir/get_disk" elif [ -f "${file_path}" ]; then # 硬盘名称 disk_name=$(jq -r .model_name ${file_path}) @@ -1194,213 +1423,153 @@ function get_disk() { [ -z "$disk_size" ] && disk_size=$(eval ${tmp_command} df -h 2>/dev/null | awk -v tmp_disk_name="${tmp_name}" '$1 ~ "^/dev/"tmp_disk_name && !disk_found {print $2; disk_found=1}') disk_name="${disk_name}_${disk_size}" } - printf "${str_linefeed}${str_title_start} 硬盘名称:${disk_name}${disk_type}${str_title_end}" >> "$output_dir/get_disk" + printf "${str_linefeed}${str_title_start} 硬盘名称:${disk_name}${disk_type}${str_title_end}" >>"$output_dir/get_disk" # 硬盘温度 disk_temp=$(jq -r .temperature.current ${file_path}) - [[ -n $disk_temp && $disk_temp != null && $disk_temp != 0 ]] && printf "${str_linefeed}${str_tab}硬盘温度:${disk_temp}℃" >> "$output_dir/get_disk" + [[ -n $disk_temp && $disk_temp != null && $disk_temp != 0 ]] && printf "${str_linefeed}${str_tab}硬盘温度:${disk_temp}℃" >>"$output_dir/get_disk" # 通电时间 disk_time=$(jq -r .power_on_time.hours ${file_path}) - [[ -n $disk_time && $disk_time != null ]] && printf "${str_linefeed}${str_tab}通电时间:${disk_time}h" >> "$output_dir/get_disk" + [[ -n $disk_time && $disk_time != null ]] && printf "${str_linefeed}${str_tab}通电时间:${disk_time}h" >>"$output_dir/get_disk" # 空间使用 disk_use=$(eval ${tmp_command} lsblk -o NAME,FSUSE%,TYPE 2>/dev/null | awk -v part_name="${tmp_name}" '$NF == "part" && $1 ~ part_name && NF > 2 {sub(".*" part_name, part_name, $1); printf "%s: %s ", $1, $2} END {print ""}') [ -z "$disk_use" ] && disk_use=$(eval ${tmp_command} df -h 2>/dev/null | awk -v part_name="${tmp_name}" '$1 ~ "^/dev/"part_name && NF > 1 {sub("/dev/", "", $1); if (!seen[$1]) { printf "%s: %s ", $1, $5; seen[$1] = 1; }} END {print ""}') - [ -n "$disk_use" ] && [ -n "${disk_use// }" ] && echo -e -n "${str_linefeed}${str_tab}空间使用:${disk_use}" >> "$output_dir/get_disk" + [ -n "$disk_use" ] && [ -n "${disk_use// /}" ] && echo -e -n "${str_linefeed}${str_tab}空间使用:${disk_use}" >>"$output_dir/get_disk" # 寿命使用 disk_health=$(jq -r .nvme_smart_health_information_log.percentage_used ${file_path}) - [[ -n $disk_health && $disk_health != null ]] && echo -e -n "${str_linefeed}${str_tab}寿命使用:${disk_health}%" >> "$output_dir/get_disk" + [[ -n $disk_health && $disk_health != null ]] && echo -e -n "${str_linefeed}${str_tab}寿命使用:${disk_health}%" >>"$output_dir/get_disk" # 错误日志 disk_log_err=$(jq -r .ata_smart_error_log.summary.count ${file_path}) - [[ -n $disk_log_err && $disk_log_err != null && $disk_log_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}错误日志:${disk_log_err}" >> "$output_dir/get_disk" + [[ -n $disk_log_err && $disk_log_err != null && $disk_log_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}错误日志:${disk_log_err}" >>"$output_dir/get_disk" # 自检错误 disk_test_err=$(jq -r .ata_smart_self_test_log.standard.error_count_total ${file_path}) - [[ -n $disk_test_err && $disk_test_err != null && $disk_test_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}自检错误:${disk_test_err}" >> "$output_dir/get_disk" + [[ -n $disk_test_err && $disk_test_err != null && $disk_test_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}自检错误:${disk_test_err}" >>"$output_dir/get_disk" # 0E 错误 disk_0e_err=$(jq -r .nvme_smart_health_information_log.media_errors ${file_path}) - [[ -n $disk_0e_err && $disk_0e_err != null && $disk_0e_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}0E 错误:${disk_0e_err}" >> "$output_dir/get_disk" + [[ -n $disk_0e_err && $disk_0e_err != null && $disk_0e_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}0E 错误:${disk_0e_err}" >>"$output_dir/get_disk" # 整体健康 smart_status=$(jq -r .smart_status.passed ${file_path}) [[ -n $smart_status && $smart_status != null && $smart_status != "true" ]] && { - echo -e -n "${str_linefeed}${str_tab}${str_title_start}硬盘整体健康评估不通过!!!${str_title_end}" >> "$output_dir/get_disk" + echo -e -n "${str_linefeed}${str_tab}${str_title_start}硬盘整体健康评估不通过!!!${str_title_end}" >>"$output_dir/get_disk" disk_err="true" } - [ -n "$disk_err" ] && echo -e -n "${str_linefeed}${str_tab}${str_title_start}硬盘存在错误,请及时备份数据!!!${str_title_end}" >> "$output_dir/get_disk" + [ -n "$disk_err" ] && echo -e -n "${str_linefeed}${str_tab}${str_title_start}硬盘存在错误,请及时备份数据!!!${str_title_end}" >>"$output_dir/get_disk" fi done + unset i } # 查询 IP 归属地 -function get_ip_attribution(){ +get_ip_attribution() { ip="$1" - [ -f ${dir}ipAddress ] && ( cat ${dir}ipAddress|grep -q -w -i "$ip" ) && echo "本地局域网" && return + jq -e --arg ip "$ip" '.devices[] | select(.ip == $ip) | .ip' "$devices_json" >/dev/null && echo "本地局域网" && return ip_attribution_urls=$(cat /usr/share/wechatpush/api/ip_attribution.list) - local url_number=`echo "$ip_attribution_urls"|wc -l` - local rand_number=`rand 1 $url_number` - function get_attribution() - { - local ip_attribution_url=`echo "$ip_attribution_urls"| sed -n "${1}p"|sed -e 's/\r//g'` + local url_number=$(echo "$ip_attribution_urls" | wc -l) + local rand_number=$(rand 1 $url_number) + get_attribution() { + local ip_attribution_url=$(echo "$ip_attribution_urls" | sed -n "${1}p" | sed -e 's/\r//g') local login_ip_attribution=$(eval curl --connect-timeout 2 -m 2 -k -s "$ip_attribution_url" 2>/dev/null) - #logfile=logfile="${dir}wechatpush.log" + #logfile=logfile="${dir}/wechatpush.log" #[ -z "$login_ip_attribution" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】归属地获取超时,当前使用的 API 为 $ip_attribution_url" >> ${logfile} echo "$login_ip_attribution" } - local ip_attribution=`get_attribution ${rand_number}` - [ -z "$ip_attribution" ] && local rand_number=`expr $rand_number + 1` && [ $rand_number -gt $url_number ] && local rand_number=1;[ -z "$ip_attribution" ] && local ip_attribution=`get_attribution ${rand_number}` + local ip_attribution=$(get_attribution ${rand_number}) + [ -z "$ip_attribution" ] && local rand_number=$(expr $rand_number + 1) && [ $rand_number -gt $url_number ] && local rand_number=1 + [ -z "$ip_attribution" ] && local ip_attribution=$(get_attribution ${rand_number}) echo "$ip_attribution" } # 登录提醒通知 -function login_send(){ +login_send() { + # 初始化黑名单和白名单 [ -n "$login_web_black" ] && [ "$login_web_black" -eq 1 ] && init_ip_black "ipv4" [ -n "$login_web_black" ] && [ "$login_web_black" -eq 1 ] && init_ip_black "ipv6" [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq 1 ] && init_ip_white "ipv4" [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq 1 ] && init_ip_white "ipv6" + + # 如果没有登录日志或失败日志,则直接返回 [ -z "$web_logged" ] && [ -z "$ssh_logged" ] && [ -z "$web_login_failed" ] && [ -z "$ssh_login_failed" ] && return + + # 设置 IP 黑名单 set_ip_black + + # 获取系统日志 sys_log=$(logread notice) + local login_title local login_content - # Web 登录提醒 - [ -f ${dir}web_login ] && for login_ip in `cat ${dir}web_login | awk '{print $1}' | sort -u`; do - [ -z "$login_ip" ] && continue - local login_time=$(cat ${dir}web_login | grep -w ${login_ip} | awk '{print $2}' | tail -n 1) - local login_mode=$(echo "$sys_log" | grep -E ".* $login_time.*$login_ip.*" | awk '{print $13}' | tail -n 1) - [ "$login_mode" = "/" ] && login_mode="/ (首页登录)" - unset log_only content_attribution content_mode - echo "$login_ip_white_list" | grep -w -q "$login_ip" && log_only="1" && [ -n "$login_log_enable" ] && continue - if [ -z "$log_only" ] && [ ! -z "$login_disturb" ] && [ "$login_disturb" -eq 2 ]; then - [ -f "$logfile" ] && login_log=$(grep -w "$login_ip" "$logfile" | grep -v "\【info\】" | tail -n 1) - [ ! -z "$login_log" ] && log_timestamp=$(date -d "$(echo "$login_log" | awk '{print $1, $2}')" +%s) || log_timestamp=0 - [ $(($(date +%s) - log_timestamp)) -lt $login_notification_delay ] && log_only="1" && [ -n "$login_log_enable" ] && continue - fi - [ -n "$log_only" ] && echo "$(date "+%Y-%m-%d") ${login_time} 【info】设备 ${login_ip} 通过 Web ${login_mode} 登录了路由器 " >> ${logfile} && continue - - local login_ip_attribution=$(get_ip_attribution ${login_ip}) - [ -n "$login_ip_attribution" ] && content_attribution="${str_linefeed}${str_tab}归属地:${str_space}${str_space}${login_ip_attribution}" - [ -n "$login_mode" ] && content_mode="${str_linefeed}${str_tab}登录方式:${str_space}Web ${login_mode}" - if [ -z "$login_disturb" ] || [ "$login_disturb" -ne "1" ]; then - local title_prefix="${login_ip} 登录了路由器" - if [ -z "$login_title" ]; then - login_title="${title_prefix}" - elif ( echo "$login_title" | grep -q "登录了路由器" ); then - login_title="${login_ip} ${login_title}" - else - login_title="设备状态变化" - fi - content_title="${str_splitline}${str_title_start} 登录信息${str_title_end}" - content_time="${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${login_time}" - content_ip="${str_linefeed}${str_tab}设备 IP:${str_space}${str_space}${login_ip}" - login_content="${login_content}${content_title}${content_time}${content_ip}${content_attribution}${content_mode}" - fi - unset title_prefix content_time content_ip content_attribution content_mode - echo "$(date "+%Y-%m-%d") ${login_time} ${disturb_text}设备 ${login_ip} (${login_ip_attribution}) 通过 URL ${login_mode} 登录了路由器 " >> ${logfile} - done - rm -f ${dir}web_login >/dev/null 2>&1 - unset login_ip login_time login_mode log_only - # SSH 登录提醒 - [ -f ${dir}ssh_login ] && for login_ip in `cat ${dir}ssh_login | awk '{print $1}' | sort -u`; do - [ -z "$login_ip" ] && continue - local login_time=$(cat ${dir}ssh_login | grep -w ${login_ip} | awk '{print $2}' | tail -n 1) - local login_mode=$(echo "$sys_log" | grep -E ".* $login_time.*$login_ip.*" | awk '{print $8}' | tail -n 1) - echo "$login_ip_white_list"|grep -w -q "$login_ip" && log_only="1" && [ -n "$login_log_enable" ] && continue - if [ -z "$log_only" ] && [ ! -z "$login_disturb" ] && [ "$login_disturb" -eq 2 ]; then - [ -f "$logfile" ] && login_log=$(grep -w "$login_ip" "$logfile" | grep -v "\【info\】" | tail -n 1) - [ ! -z "$login_log" ] && log_timestamp=$(date -d "$(echo "$login_log" | awk '{print $1, $2}')" +%s) || log_timestamp=0 - [ $(($(date +%s) - log_timestamp)) -lt $login_notification_delay ] && log_only="1" && [ -n "$login_log_enable" ] && continue - fi - [ -n "$log_only" ] && echo "$(date "+%Y-%m-%d") ${login_time} 【info】设备 ${login_ip} 通过 SSH ${login_mode} 登录了路由器 " >> ${logfile} && continue - - local login_ip_attribution=$(get_ip_attribution ${login_ip}) - [ -n "$login_ip_attribution" ] && content_attribution="${str_linefeed}${str_tab}归属地:${str_space}${str_space}${login_ip_attribution}" - [ ! -z "$login_mode" ] && content_mode="${str_linefeed}${str_tab}登录方式:${str_space}SSH ${login_mode}" - if [ -z "$login_disturb" ] || [ "$login_disturb" -ne "1" ]; then - local title_prefix="${login_ip} 登录了路由器" - if [ -z "$login_title" ]; then - login_title="${title_prefix}" - elif ( echo "$login_title" | grep -q "登录了路由器" ); then - login_title="${login_ip} ${login_title}" - else - login_title="设备状态变化" - fi - content_title="${str_splitline}${str_title_start} 登录信息${str_title_end}" - content_time="${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${login_time}" - content_ip="${str_linefeed}${str_tab}设备 IP:${str_space}${str_space}${login_ip}" - login_content="${login_content}${content_title}${content_time}${content_ip}${content_attribution}${content_mode}" - fi - unset title_prefix content_time content_ip content_attribution content_mode - echo "$(date "+%Y-%m-%d") ${login_time} ${disturb_text}设备 ${login_ip} (${login_ip_attribution}) 通过 SSH ${login_mode} 登录了路由器 " >> ${logfile} - done - rm -f ${dir}ssh_login >/dev/null 2>&1 - unset login_ip login_time login_mode log_only - # Web 非法登录 - [ -f ${dir}web_failed ] && for login_ip in `cat ${dir}web_failed | awk '{print $1}' | sort -u`; do - [ -z "$login_ip" ] && continue - local login_time=$(cat ${dir}web_failed | grep -w ${login_ip} | awk '{print $2}' | tail -n 1) - local login_mode=$(echo "$sys_log" | grep -E ".* $login_time.*$login_ip.*" | awk '{print $13}' | tail -n 1) - [ "$login_mode" = "/" ] && login_mode="/ (首页登录)" - local login_ip_attribution=$(get_ip_attribution ${login_ip}) - [ -n "$login_ip_attribution" ] && content_attribution="${str_linefeed}${str_tab}归属地:${str_space}${str_space}${login_ip_attribution}" - echo "$(date "+%Y-%m-%d") ${login_time} 【!!!】设备 ${login_ip} (${login_ip_attribution}) 通过 Web 频繁尝试登录" >> ${logfile} - [ -n "$login_disturb" ] && [ "$login_disturb" -eq 1 ] && continue - local title_prefix="${login_ip} 频繁尝试登录" - if [ -z "$login_title" ]; then - login_title="${title_prefix}" - elif ( echo "$login_title" | grep -q "频繁尝试登录" ); then - login_title="${login_ip} ${login_title}" - else - login_title="设备状态变化" - fi - content_title="${str_splitline}${str_title_start} 登录信息${str_title_end}" - content_time="${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${login_time}" - content_ip="${str_linefeed}${str_tab}设备 IP:${str_space}${str_space}${login_ip}" - content_mode="${str_linefeed}${str_tab}登录方式:${str_space}Web ${login_mode}" - login_content="${login_content}${content_title}${content_time}${content_ip}${content_attribution}${content_mode}" - unset title_prefix content_time content_ip content_attribution content_mode - done - rm -f ${dir}web_failed >/dev/null 2>&1 - unset login_ip login_time login_mode - # SSH 非法登录 - [ -f ${dir}ssh_failed ] && for login_ip in `cat ${dir}ssh_failed | awk '{print $1}' | sort -u`; do - [ -z "$login_ip" ] && continue - local login_time=$(cat ${dir}ssh_failed | grep -w ${login_ip} | awk '{print $2}' | tail -n 1) - local login_ip_attribution=$(get_ip_attribution ${login_ip}) - [ -n "$login_ip_attribution" ] && content_attribution="${str_linefeed}${str_tab}归属地:${str_space}${str_space}${login_ip_attribution}" - echo "$(date "+%Y-%m-%d") ${login_time} 【!!!】设备 ${login_ip} (${login_ip_attribution}) 通过 SSH 频繁尝试登录" >> ${logfile} - [ -n "$login_disturb" ] && [ "$login_disturb" -eq 1 ] && continue - local title_prefix="${login_ip} 频繁尝试登录" - if [ -z "$login_title" ]; then - login_title="${title_prefix}" - elif ( echo "$login_title" | grep -q "频繁尝试登录" ); then - login_title="${login_ip} ${login_title}" - else - login_title="设备状态变化" + # 处理 Web 和 SSH 登录提醒 + for log_type in "web_login" "ssh_login" "web_failed" "ssh_failed"; do + local log_file="${dir}/${log_type}" + if [ -f "$log_file" ]; then + while read -r login_ip login_time; do + [ -z "$login_ip" ] && continue + echo "$login_ip_white_list" | grep -w -q "$login_ip" && [ -n "$login_log_enable" ] && continue + + [[ "$log_type" == "web"* ]] && local log_type_short="Web" || local log_type_short="SSH" + if [ -n "$login_disturb" ] && [ "$login_disturb" -eq 2 ]; then + [ -f "$logfile" ] && login_log=$(grep -w "$login_ip" "$logfile" | grep -v "\【info\】" | tail -n 1) + [ -n "$login_log" ] && log_timestamp=$(date -d "$(echo "$login_log" | awk '{print $1, $2}')" +%s) || log_timestamp=0 + [ $(($(date +%s) - log_timestamp)) -lt $login_notification_delay ] && local log_only="1" && [ -n "$login_log_enable" ] && continue + fi + [ -n "$log_only" ] && log_change "【info】设备 ${login_ip} 通过 ${log_type_short} ${login_mode} 登录了路由器 " && continue + + # 查询 IP 归属地 + local login_ip_attribution=$(get_ip_attribution "${login_ip}") + # 登录方式 + if [[ "$log_type" == "web"* ]]; then + # Web 登录、非法登录 + local login_mode=$(logread notice | grep -E ".* $login_time.*$login_ip.*" | awk '{print $13}' | tail -n 1) + [ "$login_mode" = "/" ] && login_mode="/ (首页登录)" + elif [ "$log_type" == "ssh_login" ]; then + # SSH 登录 + local login_mode=$(logread notice | grep -E ".* $login_time.*$login_ip.*" | awk '{print $8}' | tail -n 1) + else + local login_mode=$(logread notice | grep -E ".* $login_time.*$login_ip.*" | awk '{for(i=8;i>"${logfile}" + + local login_content_time="${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${login_time}" + local login_content_ip="${str_linefeed}${str_tab}设备 IP:${str_space}${str_space}${login_ip}" + [ -n "$login_ip_attribution" ] && local login_content_attribution="${str_linefeed}${str_tab}归属地:${str_space}${str_space}${login_ip_attribution}" + local login_content_mode="${str_linefeed}${str_tab}登录方式:${str_space}${log_type_short} ${login_mode}" + + login_content="${login_content_info}${login_content_time}${login_content_ip}${login_content_attribution}${login_content_mode}" + + done <"$log_file" + rm -f "$log_file" >/dev/null 2>&1 fi - content_title="${str_splitline}${str_title_start} 登录信息${str_title_end}" - content_time="${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${login_time}" - content_ip="${str_linefeed}${str_tab}设备 IP:${str_space}${str_space}${login_ip}" - content_mode="${str_linefeed}${str_tab}登录方式:${str_space}SSH" - login_content="${login_content}${content_title}${content_time}${content_ip}${content_attribution}${content_mode}" - unset title_prefix content_time content_ip content_attribution content_mode done - rm -f ${dir}ssh_failed >/dev/null 2>&1 - unset login_ip login_time login_mode - - disturb;disturb=$? + + # 发送通知 + disturb + disturb=$? [ -z "$login_title" ] && return - [ ! -z "$device_name" ] && login_title="【$device_name】$login_title" - ( echo "$lite_enable"|grep -q "login_content" ) && login_content="$login_title" + [ -n "$device_name" ] && login_title="【$device_name】$login_title" + (echo "$lite_enable" | grep -q "login_content") && login_content="$login_title" [ "$disturb" -eq 0 ] && [ -n "$login_title" ] && diy_send "${login_title}" "${login_content}" "${jsonpath}" >/dev/null 2>&1 } # 添加白名单,懒得写删除项和信息显示了,纯粹就是懒 -function add_ip_white() { +add_ip_white() { [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] || return [ -z "$2" ] && timeout=$login_ip_white_timeout || timeout=$2 # 检查 IP 版本 unset ipset_name - ( echo "$1"|grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$' ) && local ipset_name="wechatpush_whitelist" - ( echo "$1"|grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$' ) && local ipset_name="wechatpush_whitelistv6" - [ -z "$ipset_name" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】白名单添加失败,IP 格式错误" >> ${logfile} && return + (echo "$1" | grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$') && local ipset_name="wechatpush_whitelist" + (echo "$1" | grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$') && local ipset_name="wechatpush_whitelistv6" + [ -z "$ipset_name" ] && log_change "【!!!】白名单添加失败,IP 格式错误" && return [ -n "$nftables_version" ] && nft delete element inet fw4 $ipset_name { $1 } >/dev/null 2>&1 [ -n "$nftables_version" ] && nft add element inet fw4 $ipset_name { $1 expires ${timeout}s } && return #没找到刷新时间的命令,删除再添加 @@ -1408,7 +1577,7 @@ function add_ip_white() { } # 初始化白名单 -function init_ip_white() { +init_ip_white() { [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] || return # 设置 IP 版本变量 if [ $1 == "ipv4" ]; then @@ -1429,15 +1598,15 @@ function init_ip_white() { fi # 端口放行 - if [ ! -z "$login_port_white" ]; then - local login_port_white=`echo "$login_port_white"|sed 's/ //g'|sed 's/,/, /g'` 2>/dev/null + if [ -n "$login_port_white" ]; then + local login_port_white=$(echo "$login_port_white" | sed 's/ //g' | sed 's/,/, /g') 2>/dev/null if [ -n "$nftables_version" ]; then - local count_accept_rules=`nft list ruleset | grep -c "tcp dport.* ${login_port_white}.* $ip_version saddr @${ipset_name} counter packets .* accept comment \"\!wechatpush Accept rule\""` + local count_accept_rules=$(nft list ruleset | grep -c "tcp dport.* ${login_port_white}.* $ip_version saddr @${ipset_name} counter packets .* accept comment \"\!wechatpush Accept rule\"") if [ $count_accept_rules -eq 0 ]; then nft insert rule inet fw4 input tcp dport { $login_port_white } $ip_version saddr @$ipset_name counter accept comment \"\!wechatpush Accept rule\" >/dev/null 2>&1 elif [ $count_accept_rules -ne 1 ]; then local i=0 - local handles=`nft --handle list ruleset | grep "\!wechatpush Accept rule" | grep -v "tcp dport.* ${login_port_white}.* $ip_version saddr @${ipset_name} counter packets .* accept comment \"\!wechatpush Accept rule\"" | awk '{print $NF}'` + local handles=$(nft --handle list ruleset | grep "\!wechatpush Accept rule" | grep -v "tcp dport.* ${login_port_white}.* $ip_version saddr @${ipset_name} counter packets .* accept comment \"\!wechatpush Accept rule\"" | awk '{print $NF}') for handle in $handles; do [ $i -eq 0 ] && i=1 && continue nft delete rule $handle @@ -1450,25 +1619,25 @@ function init_ip_white() { unset handle # 端口转发 while IFS= read -r port_forward; do - port_forward=`echo "$port_forward"|sed 's/,/ /g'` 2>/dev/null - [ `echo $port_forward| awk -F" " '{print NF}'` -ne "4" ] && continue - local src_ip=`echo ${port_forward}|awk '{print $1}'` - local src_port=`echo ${port_forward}|awk '{print $2}'` - local dst_ip=`echo ${port_forward}|awk '{print $3}'` - local dst_port=`echo ${port_forward}|awk '{print $4}'` + port_forward=$(echo "$port_forward" | sed 's/,/ /g') 2>/dev/null + [ $(echo $port_forward | awk -F" " '{print NF}') -ne "4" ] && continue + local src_ip=$(echo ${port_forward} | awk '{print $1}') + local src_port=$(echo ${port_forward} | awk '{print $2}') + local dst_ip=$(echo ${port_forward} | awk '{print $3}') + local dst_port=$(echo ${port_forward} | awk '{print $4}') if [ -n "$nftables_version" ]; then - ! nft list ruleset|grep "$ip_version saddr @${ipset_name} tcp dport $src_port counter .* dnat $ip_version to $dst_ip:$dst_port comment \"\!wechatpush DNAT rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 wechatpush_dstnat meta nfproto $1 $ip_version saddr @${ipset_name} tcp dport $src_port counter dnat to "$dst_ip:$dst_port" comment \"\!wechatpush DNAT rule\" >/dev/null 2>&1 - ! nft list ruleset|grep "$ip_version daddr $dst_ip tcp dport $dst_port counter .* snat $ip_version to $src_ip comment \"\!wechatpush SNAT rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 wechatpush_srcnat $ip_version daddr $dst_ip tcp dport $dst_port counter snat to $src_ip comment \"\!wechatpush SNAT rule\" >/dev/null 2>&1 + ! nft list ruleset | grep "$ip_version saddr @${ipset_name} tcp dport $src_port counter .* dnat $ip_version to $dst_ip:$dst_port comment \"\!wechatpush DNAT rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 wechatpush_dstnat meta nfproto $1 $ip_version saddr @${ipset_name} tcp dport $src_port counter dnat to "$dst_ip:$dst_port" comment \"\!wechatpush DNAT rule\" >/dev/null 2>&1 + ! nft list ruleset | grep "$ip_version daddr $dst_ip tcp dport $dst_port counter .* snat $ip_version to $src_ip comment \"\!wechatpush SNAT rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 wechatpush_srcnat $ip_version daddr $dst_ip tcp dport $dst_port counter snat to $src_ip comment \"\!wechatpush SNAT rule\" >/dev/null 2>&1 else ${ip_version}tables -t nat -C PREROUTING -m set --match-set $ipset_name src -p tcp --dport $src_port -j DNAT --to-destination "$dst_ip:$dst_port" >/dev/null 2>&1 || ${ip_version}tables -t nat -I PREROUTING -m set --match-set $ipset_name src -p tcp --dport $src_port -j DNAT --to-destination "$dst_ip:$dst_port" >/dev/null 2>&1 ${ip_version}tables -t nat -C POSTROUTING -m set --match-set $ipset_name src -p tcp -d $dst_ip --dport $dst_port -j SNAT --to-source $src_ip >/dev/null 2>&1 || ${ip_version}tables -t nat -I POSTROUTING -m set --match-set $ipset_name src -p tcp -d $dst_ip --dport $dst_port -j SNAT --to-source $src_ip >/dev/null 2>&1 fi - done <<< "$login_port_forward_list" + done <<<"$login_port_forward_list" unset port_forward } # 初始化黑名单规则 -function init_ip_black(){ +init_ip_black() { [ -n "$login_web_black" ] && [ "$login_web_black" -eq "1" ] || return # 设置 IP 版本变量 if [ $1 == "ipv4" ]; then @@ -1479,10 +1648,10 @@ function init_ip_black(){ ip_version="ip6" nat_table_cmd="family inet6" fi - + if [ -n "$nftables_version" ]; then ! nft list set inet fw4 ${ipset_name} >/dev/null 2>&1 && nft add set inet fw4 ${ipset_name} { type ${1}_addr\; flags timeout\; timeout ${login_ip_black_timeout}s\; } - ! nft list ruleset|grep "$ip_version saddr @${ipset_name} counter .* comment \"\!wechatpush Drop rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 input $ip_version saddr @${ipset_name} counter drop comment \"\!wechatpush Drop rule\" >/dev/null 2>&1 + ! nft list ruleset | grep "$ip_version saddr @${ipset_name} counter .* comment \"\!wechatpush Drop rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 input $ip_version saddr @${ipset_name} counter drop comment \"\!wechatpush Drop rule\" >/dev/null 2>&1 else ipset list $ipset_name >/dev/null 2>&1 || ipset create ${ipset_name} hash:ip timeout ${login_ip_black_timeout} ${nat_table_cmd} >/dev/null 2>&1 ${ip_version}tables -C INPUT -m set --match-set ${ipset_name} src -j DROP >/dev/null 2>&1 || ${ip_version}tables -I INPUT -m set --match-set ${ipset_name} src -j DROP >/dev/null 2>&1 @@ -1490,16 +1659,16 @@ function init_ip_black(){ } # 添加黑名单 -function add_ip_black(){ - [ ! "$1" ] && return 1 - echo "$login_ip_white_list"|grep -w -q "$1" && return 1 +add_ip_black() { + [ -z "$1" ] && return 1 + echo "$login_ip_white_list" | grep -w -q "$1" && return 1 # 检查 IP 版本 unset ipset_name - ( echo "$1"|grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$' ) && ipset_name="wechatpush_blacklist" - ( echo "$1"|grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$' ) && ipset_name="wechatpush_blacklistv6" - [ -z "$ipset_name" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】黑名单添加失败,IP 格式错误" >> ${logfile} && return 1 + (echo "$1" | grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$') && ipset_name="wechatpush_blacklist" + (echo "$1" | grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$') && ipset_name="wechatpush_blacklistv6" + [ -z "$ipset_name" ] && log_change "【!!!】黑名单添加失败,IP 格式错误" && return 1 - ! cat ${ip_blacklist_path}|grep -q -w -i ${1} && echo "$1 timeout ${login_ip_black_timeout}" >> ${ip_blacklist_path} + ! cat ${ip_blacklist_path} | grep -q -w -i ${1} && echo "$1 timeout ${login_ip_black_timeout}" >>${ip_blacklist_path} [ -n "$nftables_version" ] && nft add element inet fw4 ${ipset_name} { $1 expires ${login_ip_black_timeout}s } >/dev/null 2>&1 [ -n "$nftables_version" ] && return @@ -1508,28 +1677,28 @@ function add_ip_black(){ } # 移出黑名单 -function del_ip_black(){ - [ ! "$1" ] && return +del_ip_black() { + [ -z "$1" ] && return sed -i "/^${1}/d" ${ip_blacklist_path} - + # 检查 IP 版本 unset ipset_name - ( echo "$1"|grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$' ) && ipset_name="wechatpush_blacklist" - ( echo "$1"|grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$' ) && ipset_name="wechatpush_blacklistv6" - [ -z "$ipset_name" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】黑名单删除失败,IP 格式错误" >> ${logfile} && return - + (echo "$1" | grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$') && ipset_name="wechatpush_blacklist" + (echo "$1" | grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$') && ipset_name="wechatpush_blacklistv6" + [ -z "$ipset_name" ] && log_change "【!!!】黑名单删除失败,IP 格式错误" && return + [ -n "$nftables_version" ] && nft delete element inet fw4 ${ipset_name} { $1 } >/dev/null 2>&1 [ -n "$nftables_version" ] && return ipset list ${ipset_name} >/dev/null 2>&1 && ipset -! del ${ipset_name} ${1} } # 设置防火墙列表 -function set_ip_black(){ +set_ip_black() { # 检查换行,避免出错 - [ `tail -n1 "${ip_blacklist_path}" | wc -l` -eq "0" ] && echo -e >> ${ip_blacklist_path} + [ $(tail -n1 "${ip_blacklist_path}" | wc -l) -eq "0" ] && echo -e >>${ip_blacklist_path} # 从 ip_blacklist 文件逐行添加黑名单,add_ip_black() 处验证是否重复 - for ip_black in `cat ${ip_blacklist_path}|awk '{print $1}'`; do + for ip_black in $(cat ${ip_blacklist_path} | awk '{print $1}'); do add_ip_black ${ip_black} done # 当 ip_blacklist 文件清除 IP 时,从集合中清除 IP @@ -1538,52 +1707,93 @@ function set_ip_black(){ [ -z "$nftables_version" ] && fw_info_blacklist=$(ipset list wechatpush_blacklist | grep "timeout" 2>/dev/null) [ -z "$nftables_version" ] && fw_info_blacklistv6=$(ipset list wechatpush_blacklistv6 | grep "timeout" 2>/dev/null) - fw_info_blacklist="${fw_info_blacklist}\n${fw_info_blacklistv6}" + [ -n "$fw_info_blacklist" ] && [ -n "$fw_info_blacklistv6" ] && combined_fw_info_blacklist="${fw_info_blacklist}\n${fw_info_blacklistv6}" + [ -z "$fw_info_blacklist" ] && combined_fw_info_blacklist="${fw_info_blacklistv6}" || combined_fw_info_blacklist="${fw_info_blacklist}" + while IFS= read -r ip_black_info; do - ip_black=$(echo "$ip_black_info"|grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}") - [ -z "$ip_black" ] && ip_black=$(echo "$ip_black_info"|grep -Eo "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}") + ip_black=$(echo "$ip_black_info" | grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}") + [ -z "$ip_black" ] && ip_black=$(echo "$ip_black_info" | grep -Eo "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}") [ -z "$ip_black" ] && continue - cat ${ip_blacklist_path}|grep -q -w -i ${ip_black} && sed -i "/^${ip_black}/d" ${ip_blacklist_path} && echo ${ip_black_info} >> ${ip_blacklist_path} || del_ip_black ${ip_black} - done <<< "$fw_info_blacklist" + cat ${ip_blacklist_path} | grep -q -w -i ${ip_black} && sed -i "/^${ip_black}/d" ${ip_blacklist_path} && echo ${ip_black_info} >>${ip_blacklist_path} || del_ip_black ${ip_black} + done <<<"$combined_fw_info_blacklist" +} + +# 处理登录事件,传递参数 $1:ip $2:日志时间(从日志中读取而不是使用当前时间,避免秒对应不上) $3:数组名 +process_login() { + local login_ip=$1 + local login_time=$2 + local -n login_counts=$3 + + if [ -z "${login_counts["$login_ip"]}" ]; then + login_counts["$login_ip"]=0 + fi + login_counts["$login_ip"]=$((login_counts["$login_ip"] + 1)) + local count=${login_counts["$login_ip"]} + login_log=$(logread notice | grep -w -i "$login_ip" | tail -n 1) + + if [[ $count -eq $login_max_num && ("$3" == "web_failed_counts" || "$3" == "ssh_failed_counts") ]]; then + add_ip_black ${login_ip} && { + unset login_counts["$login_ip"] + [ "$3" == "web_failed_counts" ] && echo "${login_ip} ${login_time}" >>"${dir}/web_failed" + [ "$3" == "ssh_failed_counts" ] && echo "${login_ip} ${login_time}" >>"${dir}/ssh_failed" + } + fi + + if [[ "$3" == "web_login_counts" || "$3" == "ssh_login_counts" ]]; then + add_ip_white ${login_ip} + del_ip_black ${login_ip} # 白名单已经优先于黑名单,但白名单集合有超时限制,防止下次修改代码忘记,上保险 + unset web_failed_counts["$login_ip"] + unset ssh_failed_counts["$login_ip"] + unset login_counts["$login_ip"] + [ "$3" == "web_login_counts" ] && echo "${login_ip} ${login_time}" >>"${dir}/web_login" + [ "$3" == "ssh_login_counts" ] && echo "${login_ip} ${login_time}" >>"${dir}/ssh_login" + [ "${#login_counts[@]}" -gt "100" ] && login_counts=("${login_counts[@]: -100}") + fi + >"${dir}/send_enable.lock" && login_send && deltemp } # 发送定时数据 -function send(){ - echo "`date "+%Y-%m-%d %H:%M:%S"` 【定时数据】创建定时任务" >> ${logfile} - disturb;local send_disturb=$? +send() { + log_change "【定时数据】创建定时任务" + touch "${dir}/send.lock" + disturb + local send_disturb=$? get_config "send_title" "send_notification" - [ -z "$1" ] && cpuload=`getcpu` + [ -z "$1" ] && cpuload=$(getcpu) [ -z "$1" ] && service_status=$(ubus call service list '{"name": "wechatpush"}' | grep -o '"running": true') - [ -z "$1" ] && ( echo "$send_notification"|grep -q "client_list" ) && > ${dir}send_enable.lock && [ -z "$service_status" ] && first & - if [ -z "$1" ] && ( echo "$send_notification"|grep -q "router_status" ); then - cat /proc/loadavg|awk '{print $1" "$2" "$3}' > "$output_dir/systemload" & - free -m|sed -n '2p'|awk '{printf "%.2f%%\n",($3/$2)*100}' > "$output_dir/ramload" & - curl -o /dev/null --connect-timeout 5 -s -w %{http_code} www.google.com > "$output_dir/Qwai" & - cat /proc/uptime|awk -F. '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second=$1 % 60;printf("运行时间:%d天%d时%d分%d秒",run_days,run_hour,run_minute,run_second)}' > "$output_dir/systemstatustime" & + [ -z "$1" ] && (echo "$send_notification" | grep -q "client_list") && >"${dir}/send_enable.lock" && [ -z "$service_status" ] && first & + if [ -z "$1" ] && (echo "$send_notification" | grep -q "router_status"); then + cat /proc/loadavg | awk '{print $1" "$2" "$3}' >"$output_dir/systemload" & + free -m | sed -n '2p' | awk '{printf "%.2f%%\n",($3/$2)*100}' >"$output_dir/ramload" & + curl -o /dev/null --connect-timeout 5 -s -w %{http_code} www.google.com >"$output_dir/Qwai" & + cat /proc/uptime | awk -F. '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second=$1 % 60;printf("运行时间:%d天%d时%d分%d秒",run_days,run_hour,run_minute,run_second)}' >"$output_dir/systemstatustime" & systeminfo_enable="1" fi - [ -z "$1" ] && ( echo "$send_notification"|grep -q "router_temp" ) && soc_temp > "$output_dir/cputemp" & + [ -z "$1" ] && (echo "$send_notification" | grep -q "router_temp") && soc_temp >"$output_dir/cputemp" & - if [ -z "$1" ] && ( echo "$send_notification"|grep -q "wan_info" ); then - getip wanipv4 > "$output_dir/send_wanIP" & - getip hostipv4|jq -r '.IP' > "$output_dir/send_hostIP" & + if [ -z "$1" ] && (echo "$send_notification" | grep -q "wan_info"); then + getip wanipv4 >"$output_dir/send_wanIP" & + getip hostipv4 | jq -r '.IP' >"$output_dir/send_hostIP" & waninfo_enable="1" if [ "$get_ipv6_mode" -ne "0" ]; then - getip wanipv6 > "$output_dir/send_wanIPv6" & - getip hostipv6|jq -r '.IP' > "$output_dir/send_hostIPv6" & + getip wanipv6 >"$output_dir/send_wanIPv6" & + getip hostipv6 | jq -r '.IP' >"$output_dir/send_hostIPv6" & ipv6_enable="1" fi - getinterfaceuptime > "$output_dir/interfaceuptime" & + getinterfaceuptime >"$output_dir/interfaceuptime" & fi #[ -z "$1" ] && ( echo "$send_notification"|grep -q "disk_info" ) && get_disk & [ -z "$1" ] && get_disk & [ -z "$1" ] && send_title="路由状态:" - [ ! -z "$1" ] && send_title="发送测试:" && send_content="${str_splitline}${str_title_start}内容1${str_title_end}${str_linefeed}${str_tab}设备1${str_linefeed}${str_tab}设备2${str_splitline}${str_title_start}内容2${str_title_end}${str_linefeed}${str_tab}设备3${str_linefeed}${str_tab}设备4" + [ -n "$1" ] && send_title="发送测试:" && send_content="${str_splitline}${str_title_start}内容1${str_title_end}${str_linefeed}${str_tab}设备1${str_linefeed}${str_tab}设备2${str_splitline}${str_title_start}内容2${str_title_end}${str_linefeed}${str_tab}设备3${str_linefeed}${str_tab}设备4" wait_and_cat + # 需要等待 first & 完成 + [ -z "$1" ] && (echo "$send_notification" | grep -q "client_list") && current_device & + if [ -z "$1" ] && [ -n "$systeminfo_enable" ]; then [[ $Qwai -eq 200 || $Qwai -eq 301 || $Qwai -eq 302 ]] && Qwai_status="已连通!" || Qwai_status="已断开!" send_content="${send_content}${str_splitline}${str_title_start} 系统运行状态${str_title_end}" @@ -1594,8 +1804,8 @@ function send(){ send_content="${send_content}${str_linefeed}${str_tab}${systemstatustime}" fi - if [ -z "$1" ] && ( echo "$send_notification"|grep -q "router_temp" ); then - [ ! -z "$cputemp" ] && send_content="${send_content}${str_splitline}${str_title_start} 设备温度${str_title_end}${str_linefeed}${str_tab}CPU:${cputemp}℃" + if [ -z "$1" ] && (echo "$send_notification" | grep -q "router_temp"); then + [ -n "$cputemp" ] && send_content="${send_content}${str_splitline}${str_title_start} 设备温度${str_title_end}${str_linefeed}${str_tab}CPU:${cputemp}℃" [ -z "$cputemp" ] && send_content="${send_content}${str_splitline}${str_title_start} 设备温度${str_title_end}${str_linefeed}${str_tab}无法获取设备温度" fi @@ -1626,101 +1836,90 @@ function send(){ send_content="${send_content}${str_linefeed}${str_tab}外网 IPv6: ${send_hostIPv6}" fi fi - interfaceuptime=`getinterfaceuptime` - [ ! -z "$interfaceuptime" ] && wanstatustime=$(printf "在线时间:%d天%d时%d分%d秒" $((interfaceuptime / 86400)) $(((interfaceuptime % 86400) / 3600)) $(((interfaceuptime % 3600) / 60)) $((interfaceuptime % 60))) + interfaceuptime=$(getinterfaceuptime) + [ -n "$interfaceuptime" ] && wanstatustime=$(printf "在线时间:%d天%d时%d分%d秒" $((interfaceuptime / 86400)) $(((interfaceuptime % 86400) / 3600)) $(((interfaceuptime % 3600) / 60)) $((interfaceuptime % 60))) send_content="${send_content}${str_linefeed}${str_tab}${wanstatustime}" fi - if [ -z "$1" ] && ( echo "$send_notification"|grep -q "client_list" ); then - wait - awk '{ lines[i++] = $0 } END { for (j = i-1; j >= 0; j--) print lines[j] }' "${dir}ipAddress" > "${dir}tmp_sort_file" - logrow=$(awk 'END {print NR}' ${dir}ipAddress) - send_content="${send_content}${str_splitline}${str_title_start} 现有在线设备 ${logrow} 台${str_title_end}" - [ "$logrow" -eq "0" ] && send_content="${send_content}${str_splitline}${str_title_start} 当前无在线设备${str_title_end}" - time_now=$(date +%s) - while read -r tmp_ip tmp_mac tmp_name time_up _; do - tmp_name=${tmp_name//_/ } - tmp_name=$(cut_str "$tmp_name" "20") - time_online=$(time_for_humans $((time_now - time_up))) - ip_total=$(usage get "$tmp_mac") - [ -n "$ip_total" ] && ip_total="总计流量:${ip_total} " - send_content="${send_content}${str_linefeed}【${tmp_name}】 ${ip_total}${str_linefeed}${str_tab}${tmp_ip} 在线 ${time_online}" - done < "${dir}tmp_sort_file" + if [ -z "$1" ] && (echo "$send_notification" | grep -q "client_list"); then + wait_and_cat + send_content="${send_content}${current_device}" fi - [ ! -z "$device_name" ] && send_title="【$device_name】${send_title}" + [ -n "$device_name" ] && send_title="【$device_name】${send_title}" [ -z "$send_content" ] && send_content="${str_splitline}${str_title_start} 我遇到了一个难题${str_title_end}${str_linefeed}${str_tab}定时发送选项错误,你没有选择需要发送的项目,该怎 么办呢${str_splitline}" [ "$send_disturb" -eq "0" ] && diy_send "${send_title}" "${send_content}" "${jsonpath}" "$1" >/dev/null 2>&1 RETVAL=$? - [ $RETVAL -eq 1 ] && [ "$send_disturb" -eq "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】定时推送失败,请检查网络或设置信息" >> ${logfile} || echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}定时推送任务完成" >> ${logfile} + [ $RETVAL -eq 1 ] && [ "$send_disturb" -eq "0" ] && log_change "【!!!】定时推送失败,请检查网络或设置信息" || log_change "${disturb_text}定时推送任务完成" deltemp + rm -f "${dir}/send.lock" return $RETVAL } +# 推送 +diy_send() { + (! echo "$lite_enable" | grep -q "content") && (! echo "$lite_enable" | grep -q "nowtime") && local nowtime=$(date "+%Y-%m-%d %H:%M:%S") + ! jq -r '.' ${3} >/dev/null 2>&1 && log_change "【!!!】json 文件格式错误,这不是一个标准的 json 文件,请检查 ${3} 文件是否有特殊符号未转义或语法错误" && return 1 + local diyurl=$(jq -r .url ${3}) && local diyurl=$(eval echo ${diyurl}) + local type=$(jq -r '.type' ${3}) && local type=$(eval echo ${type}) + local data=$(jq -r '.data' ${3}) && local data=$(eval echo ${data}) + local content_type=$(jq -r '.content_type' ${3}) + ! jq "$type" ${3} >${tempjsonpath} && log_change "【!!!】type:{ } 字段转义变量后格式错误,请检查 type:{ } 字段内是否有特殊符号未转义或语法错误" && return 1 + + [ -n "$proxy_address" ] && local proxy_cmd="-x $proxy_address" + curl $proxy_cmd --connect-timeout 30 -m 60 --retry 1 -X POST -H "$content_type" -d "$data" "${diyurl}" + local RETVAL=$? + if [ -n "$4" ] || [ $RETVAL -ne 0 ]; then + log_change "【!!!】网络错误或 URL 错误,推送失败,curl 返回值为 ${RETVAL},请参考如下调试信息进行排查" + echo '{"url":"'${diyurl}'","content_type":"'${content_type}'","type":'$(jq "$type" ${3})'}' >${dir}/debug_send_json + echo -e "${send_title}" "${send_content}" >${dir}/debug_send_content + cat ${tempjsonpath} >${dir}/debug_send_data + ! jq -r '.' ${dir}/debug_send_json && log_change "【!!!】转义变量后格式错误,请检查 ${dir}/debug_send_json 字段内是否有特殊符号未转义或语法错误" + log_change "【debug】json 文件已保存至:${dir}/debug_send_json" + log_change "【debug】推送内容预览文件保存至:${dir}/debug_send_content" + log_change "【debug】如果收不到信息,请检查 ${dir}/debug_send_data 文件,或使用下列命令手动测试返回值 (可能需要关闭日志自动刷新方便选中)" + log_change "【debug】curl -X POST -H \"$content_type\" -d \"@${dir}debug_send_data\" \"${diyurl}\"" + [ -n "$4" ] && [ $RETVAL -eq 0 ] && return 0 + return 1 + else + return 0 + fi +} + +# 限制并发进程 +dir="/tmp/wechatpush" && mkdir -p ${dir} +get_config "thread_num" +[ -z "$thread_num" ] || [ "$thread_num" -eq "0" ] && thread_num=5 +[ -e "${dir}/fd1" ] || mkfifo "${dir}/fd1" +exec 5<>"${dir}/fd1" +rm -f "${dir}/fd1" >/dev/null 2>&1 +for i in $(seq 1 $thread_num); do + echo >&5 +done +unset i + +lock_file="${dir}/wechatpush.lock" +touch "$lock_file" + # 初始化 -if [ "$1" ] ;then +if [ "$1" ]; then [ $1 == "soc" ] && get_config "soc_code" "server_host" "server_port" [ $1 == "soc" ] && soc_temp && exit $? [ $1 == "getip" ] && { get_config "get_ipv4_mode" "ipv4_interface" "get_ipv6_mode" "ipv6_interface" - ipv4_urllist=`cat /usr/share/wechatpush/api/ipv4.list` 2>/dev/null - ipv6_urllist=`cat /usr/share/wechatpush/api/ipv6.list` 2>/dev/null - dir="/tmp/wechatpush/" && mkdir -p ${dir} - output_dir="${dir}json_output" && mkdir -p "$output_dir" + ipv4_urllist=$(cat /usr/share/wechatpush/api/ipv4.list) 2>/dev/null + ipv6_urllist=$(cat /usr/share/wechatpush/api/ipv6.list) 2>/dev/null + output_dir="${dir}/json_output" && mkdir -p "$output_dir" ip_changes getip && exit $? - } - read_config + } + silent_run read_config [ $1 == "send" ] && send && exit $? [ $1 == "test" ] && send test && exit $? [ $1 == "t1" ] && thread_num=1 || exit else - read_config + silent_run read_config fi -# 限制并发进程 -[ -z "$thread_num" ] || [ "$thread_num" -eq "0" ] && thread_num=5 -[ -e ${dir}fd1 ] || mkfifo ${dir}fd1 -exec 5<>${dir}fd1 -rm -f ${dir}fd1 >/dev/null 2>&1 -for i in `seq 1 $thread_num`; do - echo >&5 -done -unset i - -# 处理登录事件,传递参数 $1:login_ip $2:日志时间(从日志中读取而不是使用当前时间,避免秒对应不上) $3:数组名 -process_login() { - local login_ip=$1 - local login_time=$2 - local -n login_counts=$3 - - if [ -z "${login_counts["$login_ip"]}" ]; then - login_counts["$login_ip"]=0 - fi - login_counts["$login_ip"]=$((login_counts["$login_ip"]+1)) - local count=${login_counts["$login_ip"]} - login_log=$(logread notice | grep -w -i "$login_ip" | tail -n 1) - - if [[ $count -eq $login_max_num && ( "$3" == "web_failed_counts" || "$3" == "ssh_failed_counts" ) ]]; then - add_ip_black ${login_ip} && { - unset login_counts["$login_ip"] - [ "$3" == "web_failed_counts" ] && echo "${login_ip} ${login_time}" >> ${dir}web_failed - [ "$3" == "ssh_failed_counts" ] && echo "${login_ip} ${login_time}" >> ${dir}ssh_failed - } - fi - - if [[ "$3" == "web_login_counts" || "$3" == "ssh_login_counts" ]]; then - add_ip_white ${login_ip} - del_ip_black ${login_ip} # 白名单已经优先于黑名单,但白名单集合有超时限制,防止下次修改代码忘记,上保险 - unset web_failed_counts["$login_ip"] - unset ssh_failed_counts["$login_ip"] - unset login_counts["$login_ip"] - [ "$3" == "web_login_counts" ] && echo "${login_ip} ${login_time}" >> ${dir}web_login - [ "$3" == "ssh_login_counts" ] && echo "${login_ip} ${login_time}" >> ${dir}ssh_login - [ "${#login_counts[@]}" -gt "100" ] && login_counts=("${login_counts[@]: -100}") - fi - > ${dir}send_enable.lock && login_send && deltemp -} - # 监听登录事件 if [ -n "$web_logged" ] || [ -n "$ssh_logged" ] || [ -n "$web_login_failed" ] || [ -n "$ssh_login_failed" ]; then # 声明关联数组 @@ -1728,111 +1927,116 @@ if [ -n "$web_logged" ] || [ -n "$ssh_logged" ] || [ -n "$web_login_failed" ] || declare -A ssh_login_counts declare -A web_failed_counts declare -A ssh_failed_counts - [ -f ${dir}child_pid ] && child_pid=$(cat ${dir}child_pid) + [ -f "${dir}/child_pid" ] && child_pid=$(cat "${dir}/child_pid") [ -n "$child_pid" ] && kill $child_pid >/dev/null 2>&1 ( - # 监听系统日志,并分类处理 - logread -f -p notice | while IFS= read -r line; do - [ -n "$web_logged" ] && { - web_login_ip=$(echo "$line" | grep -i "accepted login" | awk '{print $NF}') - [ -n "$web_login_ip" ] && process_login "$web_login_ip" $(echo "$line" | awk '{print $4}') web_login_counts - } + # 监听系统日志,-f 表示跟随实时日志,-p 表示日志级别为 notice + logread -f -p notice | while IFS= read -r line; do + [ -n "$web_logged" ] && { + web_login_ip=$(echo "$line" | grep -i "accepted login" | awk '{print $NF}') + [ -n "$web_login_ip" ] && process_login "$web_login_ip" $(echo "$line" | awk '{print $4}') web_login_counts + } - [ -n "$ssh_logged" ] && { - ssh_login_ip=$(echo "$line" | grep -i "Password auth succeeded\|Pubkey auth succeeded" | awk '{print $NF}' | sed -nr 's#^(.*):.[0-9]{1,5}#\1#gp' | sed -e 's/%.*//') - [ -n "$ssh_login_ip" ] && process_login "$ssh_login_ip" $(echo "$line" | awk '{print $4}') ssh_login_counts - } - - [ -n "$web_login_failed" ] && { - web_failed_ip=$(echo "$line" | grep -i "failed login"|awk '{print $NF}') - [ -n "$web_failed_ip" ] && process_login "$web_failed_ip" $(echo "$line" | awk '{print $4}') web_failed_counts - } + [ -n "$ssh_logged" ] && { + ssh_login_ip=$(echo "$line" | grep -i "Password auth succeeded\|Pubkey auth succeeded" | awk '{print $NF}' | sed -nr 's#^(.*):.[0-9]{1,5}#\1#gp' | sed -e 's/%.*//') + [ -n "$ssh_login_ip" ] && process_login "$ssh_login_ip" $(echo "$line" | awk '{print $4}') ssh_login_counts + } - [ -n "$ssh_login_failed" ] && { - ssh_failed_ip=$(echo "$line" | grep -i "Bad password attempt\|Login attempt for nonexistent user from" | awk '{print $NF}' | sed -nr 's#^(.*):.[0-9]{1,5}#\1#gp' | sed -e 's/%.*//') - [ -z "$ssh_failed_ip" ] && ssh_failed_num=$(echo "$line" | sed -n 's/.*authpriv\.warn dropbear\[\([0-9]\+\)\]: Login attempt for nonexistent user/\1/p') && [ -n "$ssh_failed_num" ] && ssh_failed_ip=$(logread notice | grep "authpriv\.info dropbear\[${failed_user_id}\].*Child connection from" | awk '{print $NF}' | sed -nr 's#^(.*):.[0-9]{1,5}#\1#gp' | sed -e 's/%.*//') - [ -n "$ssh_failed_ip" ] && process_login "$ssh_failed_ip" $(echo "$line" | awk '{print $4}') ssh_failed_counts - } - done + [ -n "$web_login_failed" ] && { + web_failed_ip=$(echo "$line" | grep -i "failed login" | awk '{print $NF}') + [ -n "$web_failed_ip" ] && process_login "$web_failed_ip" $(echo "$line" | awk '{print $4}') web_failed_counts + } + + [ -n "$ssh_login_failed" ] && { + # 匹配特定的 SSH 登录失败情况并提取 IP 地址和时间 + ssh_failed_ip=$(echo "$line" | grep -iE "Bad password attempt|Login attempt for nonexistent user|Max auth tries reached" | awk '{print $NF}' | sed -nr 's#^(.*):[0-9]{1,5}#\1#gp' | sed -e 's/%.*//') + + # 如果未能提取到 IP,从日志标识符提取失败用户的 ID,并再次提取 IP + if [ -z "$ssh_failed_ip" ]; then + ssh_failed_num=$(echo "$line" | sed -n 's/.*authpriv\.warn dropbear\[\([0-9]\+\)\]: Login attempt for nonexistent user/\1/p') + [ -n "$ssh_failed_num" ] && ssh_failed_ip=$(logread notice | grep "authpriv\.info dropbear\[${ssh_failed_num}\].*Child connection from" | awk '{print $NF}' | sed -nr 's#^(.*):[0-9]{1,5}#\1#gp' | sed -e 's/%.*//' | sort -u) + fi + + # 如果成功提取到 IP 地址,调用 process_login 处理 + [ -n "$ssh_failed_ip" ] && process_login "$ssh_failed_ip" $(echo "$line" | awk '{print $4}') ssh_failed_counts + } + + done ) & # 分离子shell,避免影响 wait child_pid=$! sleep 1 disown "$child_pid" - echo $child_pid > ${dir}child_pid + echo $child_pid >"${dir}/child_pid" fi # 载入在线设备 -init;[ $? -eq 1 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】读取设置出错,请检查设置项 " >> ${logfile} && exit -echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】载入在线设备..." >> ${logfile} -> ${dir}send_enable.lock && first && deltemp -echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】初始化完成" >> ${logfile} +init +[ $? -eq 1 ] && log_change "【!!!】读取设置出错,请检查设置项 " && exit +log_change "【初始化】载入在线设备..." +>"${dir}/send_enable.lock" && first && deltemp +log_change "【初始化】初始化完成" # 循环 while [ "$enable" -eq "1" ]; do deltemp usage update - disturb;disturb=$? - device_aliases=`cat /usr/share/wechatpush/api/device_aliases.list` 2>/dev/null - - [ -f ${dir}ipAddress ] && ipAddress_logrow=$(grep -c "" ${dir}ipAddress) || ipAddress_logrow="0"; - if [ $ipAddress_logrow -ne "0" ]; then - online_list=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` - for online_mac in $online_list; do - [ ! -z "$online_mac" ] && mac_online_status="`echo "$mark_mac_list"|grep -i $online_mac|grep -v "^$"|sort -u|head -n1`${mac_online_status}" - done - fi + disturb + disturb=$? # 网络状态与 IP 变动 if [ "$get_ipv4_mode" -ne "0" ] || [ "$get_ipv6_mode" -ne "0" ]; then - rand_geturl + check_connect ip_changes fi # 设备列表 - if [ ! -f "${dir}send_enable.lock" ]; then - [ ! -z "$title" ] && echo "$title" > ${dir}title - [ ! -z "$content" ] && echo "$content" > ${dir}content + if [ ! -f "${dir}/send_enable.lock" ]; then + [ -n "$title" ] && echo "$title" >"${dir}/title" + [ -n "$content" ] && echo "$content" >"${dir}/content" first - [ -f "${dir}title" ] && title=`cat ${dir}title` && rm -f ${dir}title >/dev/null 2>&1 - [ -f "${dir}content" ] && content=`cat ${dir}content` && rm -f ${dir}content >/dev/null 2>&1 + [ -f "${dir}/title" ] && title=$(cat "${dir}/title") && rm -f "${dir}/title" >/dev/null 2>&1 + [ -f "${dir}/content" ] && content=$(cat "${dir}/content") && rm -f "${dir}/content" >/dev/null 2>&1 fi # 离线二次验证区推送 - [ ! -f "${dir}send_enable.lock" ] && down_send + [ ! -f "${dir}/send_enable.lock" ] && down_send # 当前设备列表 - [ ! -z "$content" ] && [ ! -f "${dir}send_enable.lock" ] && current_device + [ -n "$content" ] && [ ! -f "${dir}/send_enable.lock" ] && current_device # 无人值守任务 - [ ! -f "${dir}send_enable.lock" ] && unattended + [ ! -f "${dir}/send_enable.lock" ] && unattended # CPU 检测 - [ ! -f "${dir}send_enable.lock" ] && cpu_load + [ ! -f "${dir}/send_enable.lock" ] && cpu_load # 硬盘检测 - #[ ! -f "${dir}send_enable.lock" ] && get_disk + #[ ! -f "${dir}/send_enable.lock" ] && get_disk # 异常流量检测 - [ ! -f "${dir}send_enable.lock" ] && get_client_usage + [ ! -f "${dir}/send_enable.lock" ] && get_client_usage # 登录提醒通知 - #[ ! -f "${dir}send_enable.lock" ] && login_send + #[ ! -f "${dir}/send_enable.lock" ] && login_send # 因修改为实时推送,白名单设备并不会再更新时间,暂时性修复 - tmp_ip_list=`echo "$login_ip_white_list"|grep -v "^$"|sort -u` + # 修改为实时推送后,防火墙列表读取和设置需单独列出,移除黑名单无法实时,待改善 + set_ip_black + + tmp_ip_list=$(echo "$login_ip_white_list" | grep -v "^$" | sort -u) while IFS= read -r tmp_ip; do [ -n "$tmp_ip" ] && add_ip_white "$tmp_ip" "0" - done <<< "$tmp_ip_list" + done <<<"$tmp_ip_list" # 推送 - if [ ! -f "${dir}send_enable.lock" ] && [ ! -z "$title" ] && [ ! -z "$content" ]; then - [ ! -z "$device_name" ] && title="【$device_name】$title" - ( echo "$lite_enable"|grep -q "content" ) && content="$title" + if [ ! -f "${dir}/send_enable.lock" ] && [ -n "$title" ] && [ -n "$content" ]; then + [ -n "$device_name" ] && title="【$device_name】$title" + (echo "$lite_enable" | grep -q "content") && content="$title" [ "$disturb" -eq 0 ] && diy_send "${title}" "${content}" "${jsonpath}" >/dev/null 2>&1 fi # 等待定时任务推送完成 - while [ -f "${dir}send_enable.lock" ]; do + while [ -f "${dir}/send_enable.lock" ]; do sleep $sleeptime done