New module: mod_public_ip

This commit is contained in:
gSpot
2022-08-13 17:17:45 +03:00
parent 78067be67f
commit f81cfc930f
18 changed files with 349 additions and 55 deletions

View File

@@ -18,15 +18,16 @@ Internet-detector is an application for checking the availability of the Interne
**OpenWrt >= 21.02:**
wget --no-check-certificate -O /tmp/internet-detector_0.5-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_0.5-0_all.ipk
opkg install /tmp/internet-detector_0.5-0_all.ipk
rm /tmp/internet-detector_0.5-0_all.ipk
opkg update
wget --no-check-certificate -O /tmp/internet-detector_0.6-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_0.6-0_all.ipk
opkg install /tmp/internet-detector_0.6-0_all.ipk
rm /tmp/internet-detector_0.6-0_all.ipk
/etc/init.d/internet-detector start
/etc/init.d/internet-detector enable
wget --no-check-certificate -O /tmp/luci-app-internet-detector_0.5-1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_0.5-1_all.ipk
opkg install /tmp/luci-app-internet-detector_0.5-1_all.ipk
rm /tmp/luci-app-internet-detector_0.5-1_all.ipk
wget --no-check-certificate -O /tmp/luci-app-internet-detector_0.6-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_0.6-0_all.ipk
opkg install /tmp/luci-app-internet-detector_0.6-0_all.ipk
rm /tmp/luci-app-internet-detector_0.6-0_all.ipk
/etc/init.d/rpcd restart
Email notification:
@@ -35,9 +36,9 @@ Email notification:
i18n-ru:
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_0.5-1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_0.5-1_all.ipk
opkg install /tmp/luci-i18n-internet-detector-ru_0.5-1_all.ipk
rm /tmp/luci-i18n-internet-detector-ru_0.5-1_all.ipk
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_0.6-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_0.6-0_all.ipk
opkg install /tmp/luci-i18n-internet-detector-ru_0.6-0_all.ipk
rm /tmp/luci-i18n-internet-detector-ru_0.6-0_all.ipk
**[OpenWrt 19.07](https://github.com/gSpotx2f/luci-app-internet-detector/tree/19.07)**

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=internet-detector
PKG_VERSION:=0.5
PKG_VERSION:=0.6
PKG_RELEASE:=0
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
@@ -48,6 +48,7 @@ define Package/$(PKG_NAME)/install
$(INSTALL_BIN) ./files/usr/bin/internet-detector $(1)/usr/bin/internet-detector
$(INSTALL_DIR) $(1)/usr/lib/internet-detector
$(INSTALL_DATA) ./files/usr/lib/internet-detector/mod_email.lua $(1)/usr/lib/internet-detector/mod_email.lua
$(INSTALL_DATA) ./files/usr/lib/internet-detector/mod_public_ip.lua $(1)/usr/lib/internet-detector/mod_public_ip.lua
$(INSTALL_DATA) ./files/usr/lib/internet-detector/mod_led_control.lua $(1)/usr/lib/internet-detector/mod_led_control.lua
$(INSTALL_DATA) ./files/usr/lib/internet-detector/mod_modem_restart.lua $(1)/usr/lib/internet-detector/mod_modem_restart.lua
$(INSTALL_DATA) ./files/usr/lib/internet-detector/mod_network_restart.lua $(1)/usr/lib/internet-detector/mod_network_restart.lua

View File

@@ -33,6 +33,12 @@ config module 'mod_modem_restart'
option dead_period '600'
option any_band '0'
config module 'mod_public_ip'
option enabled '0'
option provider 'opendns1'
option interval '600'
option timeout '3'
config module 'mod_email'
option enabled '0'
option alive_period '0'

View File

@@ -132,6 +132,16 @@ local function readValueFromFile(filePath)
return retValue
end
local function statusJson(inet, t)
local lines = { [1] = string.format('"inet":%d', inet) }
if t then
for k, v in pairs(t) do
lines[#lines + 1] = string.format('"%s":"%s"', k, v)
end
end
return "{" .. table.concat(lines, ",") .. "}"
end
local function writeLogMessage(level, msg)
if Config.enableLogger then
nixio.syslog(level, msg)
@@ -203,7 +213,7 @@ local function TCPConnectionToHost(host, port)
if addrInfo then
local family = addrInfo[1].family
if family then
local socket = nixio.socket(family, "stream")
local socket = nixio.socket(family, "stream")
socket:setopt("socket", "sndtimeo", Config.connectionTimeout)
socket:setopt("socket", "rcvtimeo", Config.connectionTimeout)
if Config.iface then
@@ -264,23 +274,22 @@ local function main()
if counter == 0 or counter >= interval then
currentStatus = checkHosts()
if not nixio.fs.access(Config.statusFile, "r") then
writeValueToFile(Config.statusFile, currentStatus)
writeValueToFile(Config.statusFile, statusJson(currentStatus))
end
if currentStatus == 0 then
interval = Config.intervalUp
if lastStatus ~= nil and currentStatus ~= lastStatus then
writeValueToFile(Config.statusFile, currentStatus)
writeValueToFile(Config.statusFile, statusJson(currentStatus))
writeLogMessage("notice", "Internet connected")
end
else
interval = Config.intervalDown
if lastStatus ~= nil and currentStatus ~= lastStatus then
writeValueToFile(Config.statusFile, currentStatus)
writeValueToFile(Config.statusFile, statusJson(currentStatus))
writeLogMessage("notice", "Internet disconnected")
end
end
counter = 0
end
@@ -295,6 +304,17 @@ local function main()
lastTime = timeNow
e:run(currentStatus, lastStatus, timeDiff)
end
local modulesStatus = {}
for k, v in ipairs(Config.modules) do
if v.status ~= nil then
modulesStatus[v.name] = v.status
end
end
if next(modulesStatus) then
writeValueToFile(Config.statusFile, statusJson(currentStatus, modulesStatus))
end
lastStatus = currentStatus
nixio.nanosleep(1)
counter = counter + 1
@@ -326,24 +346,29 @@ local function poll(attempts, timeout)
Config.connectionTimeout = timeout
end
if checkHosts() == 0 then
return "up"
return statusJson(0)
else
return "down"
return statusJson(1)
end
end
local function inetStatus()
local inetStat = "down"
local function inetStatus(json)
local inetStat = 1
if nixio.fs.access(Config.statusFile, "r") then
local inetStatVal = readValueFromFile(Config.statusFile)
if inetStatVal ~= nil and tonumber(inetStatVal) == 0 then
inetStat = "up"
end
inetStat = inetStatVal
elseif Config.mode == 1 then
inetStat = poll()
else
os.exit(126)
end
if not json then
local sVal = inetStat:match('"inet":[0-9]')
if sVal then
sVal = sVal:match("[0-9]")
inetStat = (tonumber(sVal) == 0) and "up" or "down"
end
end
return inetStat
end
@@ -489,7 +514,7 @@ parseHosts()
local function help()
return string.format(
"Usage: %s [start|stop|restart|no-daemon|debug|status|inet-status|poll [<attempts num>] [<timeout sec>]|--help]",
"Usage: %s [start|stop|restart|no-daemon|debug|status|inet-status|inet-status-json|poll [<attempts num>] [<timeout sec>]|--help]",
arg[0]
)
end
@@ -509,6 +534,8 @@ elseif arg[1] == "status" then
print(status())
elseif arg[1] == "inet-status" then
print(inetStatus())
elseif arg[1] == "inet-status-json" then
print(inetStatus(true))
elseif arg[1] == "poll" then
local attempts, timeout
if arg[2] and arg[2]:match("[0-9]+") then

View File

@@ -20,6 +20,7 @@ local Module = {
mailSmtp = "smtp.gmail.com",
mailSmtpPort = '587',
mailSecurity = "tls",
status = nil,
_enabled = false,
_aliveCounter = 0,
_msgSent = true,

View File

@@ -10,6 +10,7 @@ local Module = {
runInterval = 5,
sysLedsDir = "/sys/class/leds",
ledName = nil,
status = nil,
_enabled = false,
_ledDir = nil,
_ledMaxBrightnessFile = nil,

View File

@@ -15,6 +15,7 @@ local Module = {
deadPeriod = 0,
iface = nil,
anyBand = false,
status = nil,
_enabled = false,
_deadCounter = 0,
_restarted = false,

View File

@@ -11,6 +11,7 @@ local Module = {
attempts = 0,
deadPeriod = 0,
restartTimeout = 0,
status = nil,
_attemptsCounter = 0,
_deadCounter = 0,
}

View File

@@ -0,0 +1,139 @@
local nixio = require("nixio")
local Module = {
name = "mod_public_ip",
config = {},
syslog = function(level, msg) return true end,
writeValue = function(filePath, str) return false end,
readValue = function(filePath) return nil end,
runInterval = 600,
nslookup = "/usr/bin/nslookup",
timeout = 3,
providers = {
opendns1 = {
name = "opendns1", server = "208.67.222.222",
host = "myip.opendns.com", queryType = "a"
},
opendns2 = {
name = "opendns2", server = "208.67.220.220",
host = "myip.opendns.com", queryType = "a"
},
opendns3 = {
name = "opendns3", server = "208.67.222.220",
host = "myip.opendns.com", queryType = "a"
},
opendns4 = {
name = "opendns4", server = "208.67.220.222",
host = "myip.opendns.com", queryType = "a"
},
akamai = {
name = "akamai", server = "ns1-1.akamaitech.net",
host = "whoami.akamai.net", queryType = "a"
},
google = {
name = "google", server = "ns1.google.com",
host = "o-o.myaddr.l.google.com", queryType = "txt"
},
},
status = nil,
_provider = nil,
_nslookupCmd = nil,
_currentIp = nil,
_enabled = false,
_counter = 0,
}
function Module:parseA(str)
res = str:match("Name:%s+" .. self._provider.host .. "\nAddress:%s+[%w.:]+")
if res then
return res:match("[%w.:]+$")
end
end
function Module:parseGoogle(str)
res = str:match(self._provider.host .. '%s+text%s+=%s+"[%w.:]+"')
if res then
return res:gsub('"', ''):match("[%w.:]+$")
end
end
function Module:resolveIP()
local res
local fh = io.popen(self._nslookupCmd, "r")
if fh then
output = fh:read("*a")
fh:close()
if self._provider.name == "google" then
res = self:parseGoogle(output)
else
res = self:parseA(output)
end
else
self.syslog("err", string.format(
"%s: Nslookup call failed (%s)", self.name, self.nslookup))
end
return res or "Undefined"
end
function Module:init(t)
if t.interval then
self.runInterval = tonumber(t.interval)
end
if t.timeout then
self.timeout = tonumber(t.timeout)
end
if t.provider then
self._provider = self.providers[t.provider]
else
self._provider = self.providers.opendns1
end
if not nixio.fs.access(self.nslookup, "x") then
self._enabled = false
self.syslog(
"warning",
string.format("%s: '%s' does not exists", self.name, self.nslookup)
)
else
self._enabled = true
self._nslookupCmd = string.format(
"%s -type=%s -timeout=%d %s %s",
self.nslookup,
self._provider.queryType,
self.timeout,
self._provider.host,
self._provider.server
)
end
end
function Module:run(currentStatus, lastStatus, timeDiff)
if not self._enabled then
return
end
if currentStatus == 0 then
if self._counter == 0 or self._counter >= self.runInterval or currentStatus ~= lastStatus then
local ip = self:resolveIP()
if ip ~= self._currentIp then
self.status = ip
self.syslog(
"notice",
string.format("%s: public IP address %s", self.name, ip)
)
else
self.status = nil
end
self._currentIp = ip
self._counter = 0
else
self.status = nil
end
else
self.status = nil
self._currentIp = nil
self._counter = 0
end
self._counter = self._counter + timeDiff
end
return Module

View File

@@ -9,6 +9,7 @@ local Module = {
readValue = function(filePath) return nil end,
deadPeriod = 0,
forceRebootDelay = 0,
status = nil,
_deadCounter = 0,
}

View File

@@ -11,6 +11,7 @@ local Module = {
alivePeriod = 0,
upScript = "",
downScript = "",
status = nil,
_deadCounter = 0,
_aliveCounter = 0,
_upScriptExecuted = true,

View File

@@ -4,7 +4,7 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=0.5-1
PKG_VERSION:=0.6-0
LUCI_TITLE:=LuCI support for internet-detector
LUCI_DEPENDS:=+internet-detector
LUCI_PKGARCH:=all

View File

@@ -88,6 +88,7 @@ return view.extend({
appStatus : 'stoped',
initStatus : null,
inetStatus : null,
publicIp : null,
inetStatusLabel : E('span', { 'class': 'label', 'id': 'inetStatusLabel' }),
inetStatusSpinner : E('span', { 'style': 'margin-top:1em' }, ' '),
serviceStatusLabel : E('em', { 'id': 'serviceStatusLabel' }),
@@ -149,13 +150,13 @@ return view.extend({
},
setInternetStatus: function() {
if(this.inetStatus === 'up') {
if(this.inetStatus === 0) {
this.inetStatusLabel.style.background = '#46a546';
this.inetStatusLabel.textContent = _('Connected');
this.inetStatusLabel.textContent = _('Connected') + (this.publicIp ? ' | %s: %s'.format(_('Public IP'), _(this.publicIp)) : '');
this.inetStatusLabel.style.color = '#ffffff';
this.unsetInetStatusSpinner();
}
else if(this.inetStatus === 'down') {
else if(this.inetStatus === 1) {
this.inetStatusLabel.textContent = _('Disconnected');
this.inetStatusLabel.style.background = '#ff4953';
this.inetStatusLabel.style.color = '#ffffff';
@@ -178,29 +179,43 @@ return view.extend({
};
},
inetStatusFromJson: function(res) {
let curInetStatus = null;
let curPubIp = null;
if(res.code === 0) {
try {
let json = JSON.parse(res.stdout.trim());
curInetStatus = json.inet;
curPubIp = json.mod_public_ip;
} catch(e) {};
};
return [ curInetStatus, curPubIp ];
},
servicePoll: function() {
return Promise.all([
fs.exec(this.execPath, [ 'status' ]),
fs.exec(this.execPath, [ 'inet-status' ]),
fs.exec(this.execPath, [ 'inet-status-json' ]),
]).then(stat => {
let curAppStatus = (stat[0].code === 0) ? stat[0].stdout.trim() : null;
let curInetStatus = (stat[1].code === 0) ? stat[1].stdout.trim() : null;
if(this.inetStatus === curInetStatus && this.appStatus === curAppStatus) {
let [ curInetStatus, curPubIp ] = this.inetStatusFromJson(stat[1]);
if(this.inetStatus === curInetStatus && this.appStatus === curAppStatus && this.publicIp === curPubIp) {
return;
};
this.appStatus = curAppStatus;
this.inetStatus = curInetStatus;
this.publicIp = curPubIp;
this.setInternetStatus();
}).catch(e => {
this.appStatus = 'stoped';
this.inetStatus = null;
this.publicIp = null
});
},
uiPoll: function() {
let curInetStatus = null;
this.uiPollCounter = ++this.uiPollCounter;
if((this.uiPollState === 0 && this.uiPollCounter % this.uiCheckIntervalUp) ||
(this.uiPollState === 1 && this.uiPollCounter % this.uiCheckIntervalDown)) {
return;
@@ -208,17 +223,12 @@ return view.extend({
this.uiPollCounter = 0;
return fs.exec(this.execPath, [ 'inet-status' ]).then(res => {
this.uiPollState = (res.code === 0 && res.stdout.trim() === 'up') ? 0 : 1;
if(this.uiPollState === 0) {
curInetStatus = 'up';
} else {
curInetStatus = 'down';
};
if(this.inetStatus !== curInetStatus) {
this.inetStatus = (this.currentAppMode === '0') ? null : curInetStatus;
return fs.exec(this.execPath, [ 'inet-status-json' ]).then(res => {
let curPubIp;
[ this.uiPollState, curPubIp ] = this.inetStatusFromJson(res);
if(this.inetStatus !== this.uiPollState || this.publicIp !== curPubIp) {
this.inetStatus = (this.currentAppMode === '0') ? null : this.uiPollState;
this.publicIp = (this.currentAppMode === '0') ? null : curPubIp;
this.setInternetStatus();
};
});
@@ -875,6 +885,58 @@ return view.extend({
'</em></div>';
};
// Public IP address
s.tab('public_ip', _('Public IP address'));
o = s.taboption('public_ip', form.DummyValue, '_dummy');
o.rawhtml = true;
o.default = '<div class="cbi-section-descr">' +
_('Checking the real public IP address.') +
'</div>';
o = s.taboption('public_ip', form.SectionValue, 'mod_public_ip', form.NamedSection,
'mod_public_ip', 'mod_public_ip'
);
ss = o.subsection;
// enabled
o = ss.option(form.Flag, 'enabled',
_('Enable'));
o.rmempty = false;
// provider
o = ss.option(form.ListValue,
'provider', _('DNS provider'),
_('Service for determining the public IP address through DNS.')
);
o.value('opendns1');
o.value('opendns2');
o.value('opendns3');
o.value('opendns4');
o.value('akamai');
o.value('google');
// interval
o = ss.option(form.ListValue,
'interval', _('Polling interval'),
_('Interval between IP address requests.')
);
o.value(60, '1' + ' ' + _('min'));
o.value(300, '5' + ' ' + _('min'));
o.value(600, '10' + ' ' + _('min'));
o.value(1800, '30' + ' ' + _('min'));
o.value(3600, '1' + ' ' + _('hour'));
o.value(10800, '3' + ' ' + _('hour'));
// timeout
o = ss.option(form.ListValue,
'timeout', _('Server response timeout')
);
for(let i=1; i<=5; i++) {
o.value(i, i + ' ' + _('sec'));
};
// Email notification
s.tab('email', _('Email notification'));

View File

@@ -8,6 +8,20 @@ return baseclass.extend({
appName : 'internet-detector',
execPath : '/usr/bin/internet-detector',
inetStatus : null,
publicIp : null,
inetStatusFromJson: function(res) {
let curInetStatus = null;
let curPubIp = null;
if(res.code === 0) {
try {
let json = JSON.parse(res.stdout.trim());
curInetStatus = json.inet;
curPubIp = json.mod_public_ip;
} catch(e) {};
};
return [ curInetStatus, curPubIp ];
},
load: async function() {
if(!(
@@ -29,7 +43,6 @@ return baseclass.extend({
if(!('internetDetectorState' in window)) {
window.internetDetectorState = 2;
};
if(window.currentAppMode === '1' && (
(window.internetDetectorState === 0 && window.internetDetectorCounter % window.uiCheckIntervalUp) ||
(window.internetDetectorState === 1 && window.internetDetectorCounter % window.uiCheckIntervalDown)
@@ -38,7 +51,7 @@ return baseclass.extend({
};
window.internetDetectorCounter = 0;
return L.resolveDefault(fs.exec(this.execPath, [ 'inet-status' ]), null);
return L.resolveDefault(fs.exec(this.execPath, [ 'inet-status-json' ]), null);
}
else {
window.internetDetectorState = 2;
@@ -51,22 +64,13 @@ return baseclass.extend({
};
if(data) {
this.inetStatus = (data.code === 0) ? data.stdout.trim() : null;
if(this.inetStatus === 'up') {
window.internetDetectorState = 0;
}
else if(this.inetStatus === 'down') {
window.internetDetectorState = 1;
}
else {
window.internetDetectorState = 2;
};
[ window.internetDetectorState, this.publicIp ] = this.inetStatusFromJson(data);
};
let internetStatus = E('span', { 'class': 'label' });
if(window.internetDetectorState === 0) {
internetStatus.textContent = _('Connected');
internetStatus.textContent = _('Connected') + (this.publicIp ? ' | %s: %s'.format(_('Public IP'), _(this.publicIp)) : '');
internetStatus.style.background = '#46a546';
internetStatus.style.color = '#ffffff';
}

View File

@@ -47,6 +47,9 @@ msgstr "Тип проверки"
msgid "Checking Internet availability."
msgstr "Проверка доступности Интернет."
msgid "Checking the real public IP address."
msgstr "Проверка реального публичного IP адреса."
msgid "Command failed"
msgstr "Команда не выполнена"
@@ -89,6 +92,9 @@ msgstr "Отключен"
msgid "Dismiss"
msgstr "Закрыть"
msgid "DNS provider"
msgstr "DNS провайдер"
msgid "Edit"
msgstr "Изменить"
@@ -119,6 +125,12 @@ msgstr "Включен"
msgid "Expecting:"
msgstr "Ожидается:"
msgid "Public IP"
msgstr "Публичный IP"
msgid "Public IP address"
msgstr "Публичный IP адрес"
msgid "Failed to get %s init status: %s"
msgstr "Не удалось получить статус инициализации %s: %s"
@@ -171,6 +183,9 @@ msgstr "Режим интернет-детектора"
msgid "Internet status"
msgstr "Статус Интернет"
msgid "Interval between IP address requests."
msgstr "Интервал между запросами IP адреса."
msgid "Jumbo: 9000 bytes"
msgstr "Гигантский: 9000 байт"
@@ -286,6 +301,9 @@ msgstr "Пинг хоста"
msgid "Ping packet size"
msgstr "Размер пакета Ping"
msgid "Polling interval"
msgstr "Интервал опроса"
msgid "Reboot device"
msgstr "Перезагрузка устройства"
@@ -334,6 +352,9 @@ msgstr "Безопасность"
msgid "Sender"
msgstr "Отправитель"
msgid "Server response timeout"
msgstr "Таймаут ответа сервера"
msgid "Service"
msgstr "Служба"
@@ -343,6 +364,9 @@ msgstr "Не удалось выполнить действие службы \"%
msgid "Service configuration"
msgstr "Конфигурация службы"
msgid "Service for determining the public IP address through DNS."
msgstr "Сервис для определения публичного IP адреса через DNS."
msgid "Service modules"
msgstr "Модули службы"

View File

@@ -35,6 +35,9 @@ msgstr ""
msgid "Checking Internet availability."
msgstr ""
msgid "Checking the real public IP address."
msgstr ""
msgid "Command failed"
msgstr ""
@@ -77,6 +80,9 @@ msgstr ""
msgid "Dismiss"
msgstr ""
msgid "DNS provider"
msgstr ""
msgid "Edit"
msgstr ""
@@ -107,6 +113,12 @@ msgstr ""
msgid "Expecting:"
msgstr ""
msgid "Public IP"
msgstr ""
msgid "Public IP address"
msgstr ""
msgid "Failed to get %s init status: %s"
msgstr ""
@@ -157,6 +169,9 @@ msgstr ""
msgid "Internet status"
msgstr ""
msgid "Interval between IP address requests."
msgstr ""
msgid "Jumbo: 9000 bytes"
msgstr ""
@@ -254,6 +269,9 @@ msgstr ""
msgid "Ping packet size"
msgstr ""
msgid "Polling interval"
msgstr ""
msgid "Reboot device"
msgstr ""
@@ -302,6 +320,9 @@ msgstr ""
msgid "Sender"
msgstr ""
msgid "Server response timeout"
msgstr ""
msgid "Service"
msgstr ""
@@ -311,6 +332,9 @@ msgstr ""
msgid "Service configuration"
msgstr ""
msgid "Service for determining the public IP address through DNS."
msgstr ""
msgid "Service modules"
msgstr ""

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 65 KiB