mirror of
https://github.com/gSpotx2f/luci-app-internet-detector.git
synced 2025-12-06 11:36:49 +03:00
v1.3. Refactoring. internet-detector-mod-modem-restart, internet-detector-mod-email
This commit is contained in:
62
README.md
62
README.md
@@ -1,49 +1,63 @@
|
|||||||
# Internet detector for OpenWrt.
|
# Internet detector for OpenWrt.
|
||||||
Internet-detector is an application for checking the availability of the Internet. Performs periodic connections to a known public host (8.8.8.8, 1.1.1.1) and determines the actual Internet availability.
|
Internet-detector is an application for checking the availability of the Internet. Performs periodic connections to a known public host and determines the actual Internet availability.
|
||||||
|
|
||||||
**OpenWrt** >= 19.07.
|
|
||||||
|
|
||||||
**Dependences:** lua, luaposix, libuci-lua.
|
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
- It can run continuously as a system service or only in an open web interface.
|
- It can run continuously as a system service or only in an open web interface.
|
||||||
- Checking the availability of a host using ping or by connecting via TCP to a specified port.
|
- Checking the availability of a host using ping or by connecting via TCP to a specified port.
|
||||||
- LED indication of Internet availability.
|
- LED indication of Internet availability.
|
||||||

|

|
||||||
- Performing actions when connecting and disconnecting the Internet (Restarting network, modem or device. Executing custom shell scripts).
|
- Performing actions when connecting and disconnecting the Internet: rebooting device, restarting network or modem (internet-detector-mod-modem-restart), executing custom shell scripts.
|
||||||
- Sending email notification when Internet access is restored.
|
- Sending email notification when Internet access is restored (internet-detector-mod-email).
|
||||||
- The daemon is written entirely in Lua using the luaposix library.
|
- The daemon is written entirely in Lua using the luaposix library.
|
||||||
|
|
||||||
## Installation notes
|
**Dependences:** lua, luaposix, libuci-lua.
|
||||||
|
|
||||||
**OpenWrt >= 21.02:**
|
## Installation notes (OpenWrt >= 21.02)
|
||||||
|
|
||||||
opkg update
|
opkg update
|
||||||
wget --no-check-certificate -O /tmp/internet-detector_1.2-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_1.2-0_all.ipk
|
wget --no-check-certificate -O /tmp/internet-detector_1.3-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_1.3-0_all.ipk
|
||||||
opkg install /tmp/internet-detector_1.2-0_all.ipk
|
opkg install /tmp/internet-detector_1.3-0_all.ipk
|
||||||
rm /tmp/internet-detector_1.2-0_all.ipk
|
rm /tmp/internet-detector_1.3-0_all.ipk
|
||||||
/etc/init.d/internet-detector start
|
/etc/init.d/internet-detector start
|
||||||
/etc/init.d/internet-detector enable
|
/etc/init.d/internet-detector enable
|
||||||
|
|
||||||
wget --no-check-certificate -O /tmp/luci-app-internet-detector_1.2-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_1.2-0_all.ipk
|
wget --no-check-certificate -O /tmp/luci-app-internet-detector_1.3-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_1.3-0_all.ipk
|
||||||
opkg install /tmp/luci-app-internet-detector_1.2-0_all.ipk
|
opkg install /tmp/luci-app-internet-detector_1.3-0_all.ipk
|
||||||
rm /tmp/luci-app-internet-detector_1.2-0_all.ipk
|
rm /tmp/luci-app-internet-detector_1.3-0_all.ipk
|
||||||
/etc/init.d/rpcd restart
|
/etc/init.d/rpcd restart
|
||||||
|
|
||||||
Email notification:
|
|
||||||
|
|
||||||
opkg install mailsend
|
|
||||||
|
|
||||||
i18n-ru:
|
i18n-ru:
|
||||||
|
|
||||||
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_1.2-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_1.2-0_all.ipk
|
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_1.3-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_1.3-0_all.ipk
|
||||||
opkg install /tmp/luci-i18n-internet-detector-ru_1.2-0_all.ipk
|
opkg install /tmp/luci-i18n-internet-detector-ru_1.3-0_all.ipk
|
||||||
rm /tmp/luci-i18n-internet-detector-ru_1.2-0_all.ipk
|
rm /tmp/luci-i18n-internet-detector-ru_1.3-0_all.ipk
|
||||||
|
|
||||||
**[OpenWrt 19.07](https://github.com/gSpotx2f/luci-app-internet-detector/tree/19.07)**
|
|
||||||
|
|
||||||
## Screenshots:
|
## Screenshots:
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
## Modem restart module (internet-detector-mod-modem-restart):
|
||||||
|
|
||||||
|
**Dependences:** modemmanager.
|
||||||
|
|
||||||
|
wget --no-check-certificate -O /tmp/internet-detector-mod-modem-restart_1.3-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-modem-restart_1.3-0_all.ipk
|
||||||
|
opkg install /tmp/internet-detector-mod-modem-restart_1.3-0_all.ipk
|
||||||
|
rm /tmp/internet-detector-mod-modem-restart_1.3-0_all.ipk
|
||||||
|
/etc/init.d/internet-detector restart
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Email notification module (internet-detector-mod-email):
|
||||||
|
|
||||||
|
**Dependences:** mailsend.
|
||||||
|
|
||||||
|
wget --no-check-certificate -O /tmp/internet-detector-mod-email_1.3-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-email_1.3-0_all.ipk
|
||||||
|
opkg install /tmp/internet-detector-mod-email_1.3-0_all.ipk
|
||||||
|
rm /tmp/internet-detector-mod-email_1.3-0_all.ipk
|
||||||
|
/etc/init.d/internet-detector restart
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## [OpenWrt 19.07](https://github.com/gSpotx2f/luci-app-internet-detector/tree/19.07)
|
||||||
|
|||||||
41
internet-detector-mod-email/Makefile
Normal file
41
internet-detector-mod-email/Makefile
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# (с) 2024 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=internet-detector-mod-email
|
||||||
|
PKG_VERSION:=1.3
|
||||||
|
PKG_RELEASE:=0
|
||||||
|
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=Email module for internet-detector
|
||||||
|
URL:=https://github.com/gSpotx2f/luci-app-internet-detector
|
||||||
|
PKGARCH:=all
|
||||||
|
DEPENDS:=+internet-detector +mailsend
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/description
|
||||||
|
Email support for internet-detector.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Configure
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib/lua/internet-detector
|
||||||
|
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/mod_email.lua $(1)/usr/lib/lua/internet-detector/mod_email.lua
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||||
@@ -13,27 +13,42 @@ local Module = {
|
|||||||
syslog = function(level, msg) return true end,
|
syslog = function(level, msg) return true end,
|
||||||
writeValue = function(filePath, str) return false end,
|
writeValue = function(filePath, str) return false end,
|
||||||
readValue = function(filePath) return nil end,
|
readValue = function(filePath) return nil end,
|
||||||
|
deadPeriod = 0,
|
||||||
alivePeriod = 0,
|
alivePeriod = 0,
|
||||||
|
mode = 0, -- 0: connected, 1: disconnected, 2: both
|
||||||
hostAlias = "OpenWrt",
|
hostAlias = "OpenWrt",
|
||||||
mta = "/usr/bin/mailsend",
|
mta = "/usr/bin/mailsend",
|
||||||
mailRecipient = "email@gmail.com",
|
mtaConnectTimeout = 5, -- default = 5
|
||||||
mailSender = "email@gmail.com",
|
mtaReadTimeout = 5, -- default = 5
|
||||||
mailUser = "email@gmail.com",
|
mailRecipient = nil,
|
||||||
mailPassword = "password",
|
mailSender = nil,
|
||||||
mailSmtp = "smtp.gmail.com",
|
mailUser = nil,
|
||||||
mailSmtpPort = '587',
|
mailPassword = nil,
|
||||||
|
mailSmtp = nil,
|
||||||
|
mailSmtpPort = nil,
|
||||||
mailSecurity = "tls",
|
mailSecurity = "tls",
|
||||||
status = nil,
|
status = nil,
|
||||||
_enabled = false,
|
_enabled = false,
|
||||||
|
_deadCounter = 0,
|
||||||
_aliveCounter = 0,
|
_aliveCounter = 0,
|
||||||
_msgSent = true,
|
_msgSentDisconnect = true,
|
||||||
|
_msgSentConnect = true,
|
||||||
_disconnected = true,
|
_disconnected = true,
|
||||||
_lastDisconnection = nil,
|
_lastDisconnection = nil,
|
||||||
_lastConnection = nil,
|
_lastConnection = nil,
|
||||||
|
_message = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
function Module:init(t)
|
function Module:init(t)
|
||||||
|
if t.mode ~= nil then
|
||||||
|
self.mode = tonumber(t.mode)
|
||||||
|
end
|
||||||
|
if t.dead_period ~= nil then
|
||||||
|
self.deadPeriod = tonumber(t.dead_period)
|
||||||
|
end
|
||||||
|
if t.alive_period ~= nil then
|
||||||
self.alivePeriod = tonumber(t.alive_period)
|
self.alivePeriod = tonumber(t.alive_period)
|
||||||
|
end
|
||||||
if t.host_alias then
|
if t.host_alias then
|
||||||
self.hostAlias = t.host_alias
|
self.hostAlias = t.host_alias
|
||||||
else
|
else
|
||||||
@@ -46,7 +61,10 @@ function Module:init(t)
|
|||||||
self.mailPassword = t.mail_password
|
self.mailPassword = t.mail_password
|
||||||
self.mailSmtp = t.mail_smtp
|
self.mailSmtp = t.mail_smtp
|
||||||
self.mailSmtpPort = t.mail_smtp_port
|
self.mailSmtpPort = t.mail_smtp_port
|
||||||
|
|
||||||
|
if t.mail_security ~= nil then
|
||||||
self.mailSecurity = t.mail_security
|
self.mailSecurity = t.mail_security
|
||||||
|
end
|
||||||
|
|
||||||
if unistd.access(self.mta, "x") then
|
if unistd.access(self.mta, "x") then
|
||||||
self._enabled = true
|
self._enabled = true
|
||||||
@@ -83,11 +101,19 @@ function Module:sendMessage(msg)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local mtaCmd = string.format(
|
local mtaCmd = string.format(
|
||||||
'%s%s %s -smtp "%s" -port %s -cs utf-8 -user "%s" -pass "%s" -f "%s" -t "%s" -sub "%s" -M "%s"',
|
'%s%s %s -smtp "%s" -port %s -ct %s -read-timeout %s -cs utf-8 -user "%s" -pass "%s" -f "%s" -t "%s" -sub "%s" -M "%s"',
|
||||||
self.mta, verboseArg, securityArgs, self.mailSmtp, self.mailSmtpPort,
|
self.mta, verboseArg, securityArgs, self.mailSmtp, self.mailSmtpPort,
|
||||||
|
self.mtaConnectTimeout, self.mtaReadTimeout,
|
||||||
self.mailUser, self.mailPassword, self.mailSender, self.mailRecipient,
|
self.mailUser, self.mailPassword, self.mailSender, self.mailRecipient,
|
||||||
string.format("%s notification", self.hostAlias),
|
string.format("%s notification", self.hostAlias),
|
||||||
string.format("%s:\n%s", self.hostAlias, msg))
|
string.format("[%s]: %s:\n%s", self.hostAlias, self.config.serviceConfig.instance, msg))
|
||||||
|
|
||||||
|
-- Debug
|
||||||
|
if self.config.debug then
|
||||||
|
io.stdout:write(string.format("%s: %s\n", self.name, mtaCmd))
|
||||||
|
io.stdout:flush()
|
||||||
|
self.syslog("debug", string.format("%s: %s", self.name, mtaCmd))
|
||||||
|
end
|
||||||
|
|
||||||
if os.execute(mtaCmd) ~= 0 then
|
if os.execute(mtaCmd) ~= 0 then
|
||||||
self.syslog("err", string.format(
|
self.syslog("err", string.format(
|
||||||
@@ -102,44 +128,46 @@ function Module:run(currentStatus, lastStatus, timeDiff)
|
|||||||
if not self._enabled then
|
if not self._enabled then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if currentStatus == 1 then
|
if currentStatus == 1 then
|
||||||
self._aliveCounter = 0
|
self._aliveCounter = 0
|
||||||
self._msgSent = false
|
self._msgSentConnect = false
|
||||||
self._lastConnection = nil
|
self._lastConnection = nil
|
||||||
if not self._disconnected then
|
if not self._disconnected then
|
||||||
self._disconnected = true
|
self._disconnected = true
|
||||||
if not self._lastDisconnection then
|
if not self._lastDisconnection then
|
||||||
self._lastDisconnection = os.date("%Y.%m.%d %H:%M:%S", os.time())
|
self._lastDisconnection = os.date("%Y.%m.%d %H:%M:%S", os.time())
|
||||||
end
|
end
|
||||||
|
self._message[#self._message + 1] = string.format(
|
||||||
|
"Internet disconnected: %s", self._lastDisconnection)
|
||||||
end
|
end
|
||||||
|
if not self._msgSentDisconnect and (self.mode == 1 or self.mode == 2) then
|
||||||
|
if self._deadCounter >= self.deadPeriod then
|
||||||
|
self._lastDisconnection = nil
|
||||||
|
self:sendMessage(table.concat(self._message, ", "))
|
||||||
|
self._message = {}
|
||||||
|
self._msgSentDisconnect = true
|
||||||
else
|
else
|
||||||
|
self._deadCounter = self._deadCounter + timeDiff
|
||||||
if not self._msgSent then
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self._deadCounter = 0
|
||||||
|
self._msgSentDisconnect = false
|
||||||
|
if not self._msgSentConnect and (self.mode == 0 or self.mode == 2) then
|
||||||
if not self._lastConnection then
|
if not self._lastConnection then
|
||||||
self._lastConnection = os.date("%Y.%m.%d %H:%M:%S", os.time())
|
self._lastConnection = os.date("%Y.%m.%d %H:%M:%S", os.time())
|
||||||
end
|
end
|
||||||
|
|
||||||
if self._aliveCounter >= self.alivePeriod then
|
if self._aliveCounter >= self.alivePeriod then
|
||||||
local message = {}
|
self._message[#self._message + 1] = string.format(
|
||||||
if self._lastDisconnection then
|
|
||||||
message[#message + 1] = string.format(
|
|
||||||
"Internet disconnected: %s", self._lastDisconnection)
|
|
||||||
self._lastDisconnection = nil
|
|
||||||
end
|
|
||||||
if self._lastConnection then
|
|
||||||
message[#message + 1] = string.format(
|
|
||||||
"Internet connected: %s", self._lastConnection)
|
"Internet connected: %s", self._lastConnection)
|
||||||
self:sendMessage(table.concat(message, ", "))
|
self:sendMessage(table.concat(self._message, "; "))
|
||||||
self._msgSent = true
|
self._message = {}
|
||||||
end
|
self._msgSentConnect = true
|
||||||
else
|
else
|
||||||
self._aliveCounter = self._aliveCounter + timeDiff
|
self._aliveCounter = self._aliveCounter + timeDiff
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self._disconnected = false
|
self._disconnected = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
41
internet-detector-mod-modem-restart/Makefile
Normal file
41
internet-detector-mod-modem-restart/Makefile
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# (с) 2024 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=internet-detector-mod-modem-restart
|
||||||
|
PKG_VERSION:=1.3
|
||||||
|
PKG_RELEASE:=0
|
||||||
|
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=Modem restart module for internet-detector
|
||||||
|
URL:=https://github.com/gSpotx2f/luci-app-internet-detector
|
||||||
|
PKGARCH:=all
|
||||||
|
DEPENDS:=+internet-detector +modemmanager
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/description
|
||||||
|
Support modem restart for internet detector.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Configure
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib/lua/internet-detector
|
||||||
|
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/mod_modem_restart.lua $(1)/usr/lib/lua/internet-detector/mod_modem_restart.lua
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||||
@@ -13,7 +13,7 @@ local Module = {
|
|||||||
readValue = function(filePath) return nil end,
|
readValue = function(filePath) return nil end,
|
||||||
mmcli = "/usr/bin/mmcli",
|
mmcli = "/usr/bin/mmcli",
|
||||||
mmInit = "/etc/init.d/modemmanager",
|
mmInit = "/etc/init.d/modemmanager",
|
||||||
deadPeriod = 0,
|
deadPeriod = 600,
|
||||||
iface = nil,
|
iface = nil,
|
||||||
anyBand = false,
|
anyBand = false,
|
||||||
status = nil,
|
status = nil,
|
||||||
@@ -23,12 +23,21 @@ local Module = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Module:toggleIface(flag)
|
function Module:toggleIface(flag)
|
||||||
|
if not self.iface then
|
||||||
|
return
|
||||||
|
end
|
||||||
return os.execute(
|
return os.execute(
|
||||||
string.format("%s %s", (flag and "/sbin/ifup" or "/sbin/ifdown"), self.iface)
|
string.format("%s %s", (flag and "/sbin/ifup" or "/sbin/ifdown"), self.iface)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Module:restartMM()
|
function Module:restartMM()
|
||||||
|
if os.execute(string.format("%s enabled", self.mmInit)) ~= 0 then
|
||||||
|
self.syslog("warning", string.format(
|
||||||
|
"%s: modemmanager service is disabled", self.name))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if self.anyBand then
|
if self.anyBand then
|
||||||
self.syslog("info", string.format(
|
self.syslog("info", string.format(
|
||||||
"%s: resetting current-bands to 'any'", self.name))
|
"%s: resetting current-bands to 'any'", self.name))
|
||||||
@@ -47,15 +56,22 @@ function Module:restartMM()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Module:init(t)
|
function Module:init(t)
|
||||||
|
if t.dead_period ~= nil then
|
||||||
self.deadPeriod = tonumber(t.dead_period)
|
self.deadPeriod = tonumber(t.dead_period)
|
||||||
|
end
|
||||||
|
if t.iface ~= nil then
|
||||||
self.iface = t.iface
|
self.iface = t.iface
|
||||||
|
end
|
||||||
|
if t.any_band ~= nil then
|
||||||
self.anyBand = (tonumber(t.any_band) ~= 0)
|
self.anyBand = (tonumber(t.any_band) ~= 0)
|
||||||
|
end
|
||||||
|
|
||||||
if not unistd.access(self.mmcli, "x") then
|
if not unistd.access(self.mmcli, "x") then
|
||||||
self.anyBand = false
|
self.anyBand = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if unistd.access(self.mmInit, "x") then
|
if (unistd.access(self.mmInit, "x")
|
||||||
|
and os.execute(string.format("%s enabled", self.mmInit)) == 0) then
|
||||||
self._enabled = true
|
self._enabled = true
|
||||||
else
|
else
|
||||||
self._enabled = false
|
self._enabled = false
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=internet-detector
|
PKG_NAME:=internet-detector
|
||||||
PKG_VERSION:=1.2
|
PKG_VERSION:=1.3
|
||||||
PKG_RELEASE:=0
|
PKG_RELEASE:=0
|
||||||
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||||
|
|
||||||
@@ -50,14 +50,13 @@ define Package/$(PKG_NAME)/install
|
|||||||
$(INSTALL_BIN) ./files/etc/init.d/internet-detector $(1)/etc/init.d/internet-detector
|
$(INSTALL_BIN) ./files/etc/init.d/internet-detector $(1)/etc/init.d/internet-detector
|
||||||
$(INSTALL_DIR) $(1)/usr/bin
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
$(INSTALL_BIN) ./files/usr/bin/internet-detector $(1)/usr/bin/internet-detector
|
$(INSTALL_BIN) ./files/usr/bin/internet-detector $(1)/usr/bin/internet-detector
|
||||||
$(INSTALL_DIR) $(1)/usr/lib/internet-detector
|
$(INSTALL_DIR) $(1)/usr/lib/lua/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/lua/internet-detector/main.lua $(1)/usr/lib/lua/internet-detector/main.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/lua/internet-detector/mod_led_control.lua $(1)/usr/lib/lua/internet-detector/mod_led_control.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/lua/internet-detector/mod_reboot.lua $(1)/usr/lib/lua/internet-detector/mod_reboot.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/lua/internet-detector/mod_network_restart.lua $(1)/usr/lib/lua/internet-detector/mod_network_restart.lua
|
||||||
$(INSTALL_DATA) ./files/usr/lib/internet-detector/mod_network_restart.lua $(1)/usr/lib/internet-detector/mod_network_restart.lua
|
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/mod_public_ip.lua $(1)/usr/lib/lua/internet-detector/mod_public_ip.lua
|
||||||
$(INSTALL_DATA) ./files/usr/lib/internet-detector/mod_reboot.lua $(1)/usr/lib/internet-detector/mod_reboot.lua
|
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/mod_user_scripts.lua $(1)/usr/lib/lua/internet-detector/mod_user_scripts.lua
|
||||||
$(INSTALL_DATA) ./files/usr/lib/internet-detector/mod_user_scripts.lua $(1)/usr/lib/internet-detector/mod_user_scripts.lua
|
|
||||||
endef
|
endef
|
||||||
|
|
||||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
config main 'config'
|
config main 'config'
|
||||||
option mode '1'
|
option mode '1'
|
||||||
option enable_logger '1'
|
option enable_logger '1'
|
||||||
@@ -12,7 +13,6 @@ config instance 'internet'
|
|||||||
option interval_down '5'
|
option interval_down '5'
|
||||||
option connection_attempts '2'
|
option connection_attempts '2'
|
||||||
option connection_timeout '2'
|
option connection_timeout '2'
|
||||||
option mod_led_control_enabled '0'
|
|
||||||
option mod_reboot_enabled '0'
|
option mod_reboot_enabled '0'
|
||||||
option mod_reboot_dead_period '3600'
|
option mod_reboot_dead_period '3600'
|
||||||
option mod_reboot_force_reboot_delay '300'
|
option mod_reboot_force_reboot_delay '300'
|
||||||
@@ -30,9 +30,8 @@ config instance 'internet'
|
|||||||
option mod_public_ip_timeout '3'
|
option mod_public_ip_timeout '3'
|
||||||
option mod_public_ip_enable_ip_script '0'
|
option mod_public_ip_enable_ip_script '0'
|
||||||
option mod_email_enabled '0'
|
option mod_email_enabled '0'
|
||||||
|
option mod_email_mode '0'
|
||||||
option mod_email_alive_period '0'
|
option mod_email_alive_period '0'
|
||||||
option mod_email_mail_smtp 'smtp.gmail.com'
|
|
||||||
option mod_email_mail_smtp_port '587'
|
|
||||||
option mod_email_mail_security 'tls'
|
option mod_email_mail_security 'tls'
|
||||||
option mod_user_scripts_enabled '0'
|
option mod_user_scripts_enabled '0'
|
||||||
option mod_user_scripts_alive_period '0'
|
option mod_user_scripts_alive_period '0'
|
||||||
|
|||||||
@@ -11,633 +11,7 @@
|
|||||||
(с) 2024 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
(с) 2024 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||||
--]]
|
--]]
|
||||||
|
|
||||||
-- Importing packages
|
local InternetDetector = require("internet-detector.main")
|
||||||
|
|
||||||
local dirent = require("posix.dirent")
|
|
||||||
local fcntl = require("posix.fcntl")
|
|
||||||
local signal = require("posix.signal")
|
|
||||||
local socket = require("posix.sys.socket")
|
|
||||||
local stat = require("posix.sys.stat")
|
|
||||||
local syslog = require("posix.syslog")
|
|
||||||
local time = require("posix.time")
|
|
||||||
local unistd = require("posix.unistd")
|
|
||||||
local uci = require("uci")
|
|
||||||
|
|
||||||
-- Default settings
|
|
||||||
|
|
||||||
local InternetDetector = {
|
|
||||||
mode = 0, -- 0: disabled, 1: Service, 2: UI detector
|
|
||||||
enableLogger = true,
|
|
||||||
hostname = "OpenWrt",
|
|
||||||
appName = "internet-detector",
|
|
||||||
commonDir = "/tmp/run",
|
|
||||||
pingCmd = "/bin/ping",
|
|
||||||
pingParams = "-c 1",
|
|
||||||
uiRunTime = 30,
|
|
||||||
noModules = false,
|
|
||||||
uiAvailModules = { mod_public_ip = true },
|
|
||||||
debug = false,
|
|
||||||
serviceConfig = {
|
|
||||||
hosts = {
|
|
||||||
[1] = "8.8.8.8",
|
|
||||||
[2] = "1.1.1.1",
|
|
||||||
},
|
|
||||||
check_type = 0, -- 0: TCP, 1: ICMP
|
|
||||||
tcp_port = 53,
|
|
||||||
icmp_packet_size = 56,
|
|
||||||
interval_up = 30,
|
|
||||||
interval_down = 5,
|
|
||||||
connection_attempts = 2,
|
|
||||||
connection_timeout = 2,
|
|
||||||
iface = nil,
|
|
||||||
instance = nil,
|
|
||||||
},
|
|
||||||
modules = {},
|
|
||||||
parsedHosts = {},
|
|
||||||
uiCounter = 0,
|
|
||||||
}
|
|
||||||
InternetDetector.configDir = string.format("/etc/%s", InternetDetector.appName)
|
|
||||||
InternetDetector.modulesDir = string.format("/usr/lib/%s", InternetDetector.appName)
|
|
||||||
|
|
||||||
-- Loading settings from UCI
|
|
||||||
|
|
||||||
local uciCursor = uci.cursor()
|
|
||||||
InternetDetector.mode = tonumber(
|
|
||||||
uciCursor:get(InternetDetector.appName, "config", "mode"))
|
|
||||||
InternetDetector.enableLogger = (tonumber(
|
|
||||||
uciCursor:get(InternetDetector.appName, "config", "enable_logger")) ~= 0)
|
|
||||||
local hostname = uciCursor:get("system", "@[0]", "hostname")
|
|
||||||
if hostname ~= nil then
|
|
||||||
InternetDetector.hostname = hostname
|
|
||||||
end
|
|
||||||
|
|
||||||
local RUNNING
|
|
||||||
|
|
||||||
function InternetDetector:prequire(package)
|
|
||||||
local retVal, pkg = pcall(require, package)
|
|
||||||
return retVal and pkg
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:loadUCIConfig(sType, instance)
|
|
||||||
local success
|
|
||||||
local num = 0
|
|
||||||
uciCursor:foreach(
|
|
||||||
self.appName,
|
|
||||||
sType,
|
|
||||||
function(s)
|
|
||||||
if s[".name"] == instance then
|
|
||||||
for k, v in pairs(s) do
|
|
||||||
if type(v) == "string" and v:match("^[%d]+$") then
|
|
||||||
v = tonumber(v)
|
|
||||||
end
|
|
||||||
self.serviceConfig[k] = v
|
|
||||||
end
|
|
||||||
success = true
|
|
||||||
self.serviceConfig.instanceNum = num
|
|
||||||
end
|
|
||||||
num = num + 1
|
|
||||||
end
|
|
||||||
)
|
|
||||||
self.serviceConfig.instance = instance
|
|
||||||
self.pidFile = string.format(
|
|
||||||
"%s/%s.%s.pid", self.commonDir, self.appName, instance)
|
|
||||||
self.statusFile = string.format(
|
|
||||||
"%s/%s.%s.status", self.commonDir, self.appName, instance)
|
|
||||||
return success
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:writeValueToFile(filePath, str)
|
|
||||||
local retValue = false
|
|
||||||
local fh = io.open(filePath, "w")
|
|
||||||
if fh then
|
|
||||||
fh:setvbuf("no")
|
|
||||||
fh:write(string.format("%s\n", str))
|
|
||||||
fh:close()
|
|
||||||
retValue = true
|
|
||||||
end
|
|
||||||
return retValue
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:readValueFromFile(filePath)
|
|
||||||
local retValue
|
|
||||||
local fh = io.open(filePath, "r")
|
|
||||||
if fh then
|
|
||||||
retValue = fh:read("*l")
|
|
||||||
fh:close()
|
|
||||||
end
|
|
||||||
return retValue
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:statusJson(inet, instance, t)
|
|
||||||
local lines = { [1] = string.format(
|
|
||||||
'{"instance":"%s","num":"%d","inet":%d',
|
|
||||||
instance,
|
|
||||||
self.serviceConfig.instanceNum,
|
|
||||||
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
|
|
||||||
|
|
||||||
function InternetDetector:writeLogMessage(level, msg)
|
|
||||||
if self.enableLogger then
|
|
||||||
local levels = {
|
|
||||||
emerg = syslog.LOG_EMERG,
|
|
||||||
alert = syslog.LOG_ALERT,
|
|
||||||
crit = syslog.LOG_CRIT,
|
|
||||||
err = syslog.LOG_ERR,
|
|
||||||
warning = syslog.LOG_WARNING,
|
|
||||||
notice = syslog.LOG_NOTICE,
|
|
||||||
info = syslog.LOG_INFO,
|
|
||||||
debug = syslog.LOG_DEBUG,
|
|
||||||
}
|
|
||||||
syslog.syslog(levels[level] or syslog.LOG_INFO, string.format(
|
|
||||||
"%s: %s", self.serviceConfig.instance or "", msg))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:loadModules()
|
|
||||||
package.path = string.format("%s;%s/?.lua", package.path, self.modulesDir)
|
|
||||||
self.modules = {}
|
|
||||||
local ok, modulesDir = pcall(dirent.files, self.modulesDir)
|
|
||||||
if ok then
|
|
||||||
for item in modulesDir do
|
|
||||||
if item:match("^mod_") then
|
|
||||||
local modName = item:gsub("%.lua$", "")
|
|
||||||
if self.noModules and not self.uiAvailModules[modName] then
|
|
||||||
else
|
|
||||||
local modConfig = {}
|
|
||||||
for k, v in pairs(self.serviceConfig) do
|
|
||||||
if k:match("^" .. modName) then
|
|
||||||
modConfig[k:gsub("^" .. modName .. "_", "")] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if modConfig.enabled == 1 then
|
|
||||||
local m = self:prequire(modName)
|
|
||||||
if m then
|
|
||||||
m.config = self
|
|
||||||
m.syslog = function(level, msg) self:writeLogMessage(level, msg) end
|
|
||||||
m.writeValue = function(filePath, str) return self:writeValueToFile(filePath, str) end
|
|
||||||
m.readValue = function(filePath) return self:readValueFromFile(filePath) end
|
|
||||||
m:init(modConfig)
|
|
||||||
self.modules[#self.modules + 1] = m
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(self.modules, function(a, b) return a.runPrio < b.runPrio end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:parseHost(host)
|
|
||||||
local addr, port = host:match("^([^%[%]:]+):?(%d?%d?%d?%d?%d?)$")
|
|
||||||
if not addr then
|
|
||||||
addr, port = host:match("^%[?([^%[%]]+)%]?:?(%d?%d?%d?%d?%d?)$")
|
|
||||||
end
|
|
||||||
return addr, tonumber(port)
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:parseHosts()
|
|
||||||
self.parsedHosts = {}
|
|
||||||
for k, v in ipairs(self.serviceConfig.hosts) do
|
|
||||||
local addr, port = self:parseHost(v)
|
|
||||||
self.parsedHosts[k] = { addr = addr, port = port }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:pingHost(host)
|
|
||||||
local ping = string.format(
|
|
||||||
"%s %s -W %d -s %d%s %s > /dev/null 2>&1",
|
|
||||||
self.pingCmd,
|
|
||||||
self.pingParams,
|
|
||||||
self.serviceConfig.connection_timeout,
|
|
||||||
self.serviceConfig.icmp_packet_size,
|
|
||||||
self.serviceConfig.iface and (" -I " .. self.serviceConfig.iface) or "",
|
|
||||||
host
|
|
||||||
)
|
|
||||||
local retCode = os.execute(ping)
|
|
||||||
|
|
||||||
if self.debug then
|
|
||||||
io.stdout:write(string.format(
|
|
||||||
"--- Ping ---\ntime = %s\n%s\nretCode = %s\n", os.time(), ping, retCode)
|
|
||||||
)
|
|
||||||
io.stdout:flush()
|
|
||||||
end
|
|
||||||
|
|
||||||
return retCode
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:TCPConnectionToHost(host, port)
|
|
||||||
local retCode = 1
|
|
||||||
local saTable, errMsg, errNum = socket.getaddrinfo(host, port or self.serviceConfig.tcp_port)
|
|
||||||
|
|
||||||
if not saTable then
|
|
||||||
if self.debug then
|
|
||||||
io.stdout:write(string.format(
|
|
||||||
"GETADDRINFO ERROR: %s, %s\n", errMsg, errNum))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local family = saTable[1].family
|
|
||||||
|
|
||||||
if family then
|
|
||||||
local sock, errMsg, errNum = socket.socket(family, socket.SOCK_STREAM, 0)
|
|
||||||
|
|
||||||
if not sock then
|
|
||||||
if self.debug then
|
|
||||||
io.stdout:write(string.format(
|
|
||||||
"SOCKET ERROR: %s, %s\n", errMsg, errNum))
|
|
||||||
end
|
|
||||||
return retCode
|
|
||||||
end
|
|
||||||
|
|
||||||
socket.setsockopt(sock, socket.SOL_SOCKET,
|
|
||||||
socket.SO_SNDTIMEO, self.serviceConfig.connection_timeout, 0)
|
|
||||||
socket.setsockopt(sock, socket.SOL_SOCKET,
|
|
||||||
socket.SO_RCVTIMEO, self.serviceConfig.connection_timeout, 0)
|
|
||||||
|
|
||||||
if self.serviceConfig.iface then
|
|
||||||
local ok, errMsg, errNum = socket.setsockopt(sock, socket.SOL_SOCKET,
|
|
||||||
socket.SO_BINDTODEVICE, self.serviceConfig.iface)
|
|
||||||
if not ok then
|
|
||||||
if self.debug then
|
|
||||||
io.stdout:write(string.format(
|
|
||||||
"SOCKET ERROR: %s, %s\n", errMsg, errNum))
|
|
||||||
end
|
|
||||||
unistd.close(sock)
|
|
||||||
return retCode
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local success = socket.connect(sock, saTable[1])
|
|
||||||
|
|
||||||
if self.debug then
|
|
||||||
if not success then
|
|
||||||
io.stdout:write(string.format(
|
|
||||||
"SOCKET CONNECT ERROR: %s\n", tostring(success)))
|
|
||||||
end
|
|
||||||
local sockTable, err_s, e_s = socket.getsockname(sock)
|
|
||||||
local peerTable, err_p, e_p = socket.getpeername(sock)
|
|
||||||
if not sockTable then
|
|
||||||
sockTable = {}
|
|
||||||
io.stdout:write(
|
|
||||||
string.format("SOCKET ERROR: %s, %s\n", err_s, e_s))
|
|
||||||
end
|
|
||||||
if not peerTable then
|
|
||||||
peerTable = {}
|
|
||||||
io.stdout:write(
|
|
||||||
string.format("SOCKET ERROR: %s, %s\n", err_p, e_p))
|
|
||||||
end
|
|
||||||
io.stdout:write(string.format(
|
|
||||||
"--- TCP ---\ntime = %s\nconnection_timeout = %s\niface = %s\nhost:port = [%s]:%s\nsockname = [%s]:%s\npeername = [%s]:%s\nsuccess = %s\n",
|
|
||||||
os.time(),
|
|
||||||
self.serviceConfig.connection_timeout,
|
|
||||||
tostring(self.serviceConfig.iface),
|
|
||||||
host,
|
|
||||||
port or self.serviceConfig.tcp_port,
|
|
||||||
tostring(sockTable.addr),
|
|
||||||
tostring(sockTable.port),
|
|
||||||
tostring(peerTable.addr),
|
|
||||||
tostring(peerTable.port),
|
|
||||||
tostring(success))
|
|
||||||
)
|
|
||||||
io.stdout:flush()
|
|
||||||
end
|
|
||||||
|
|
||||||
socket.shutdown(sock, socket.SHUT_RDWR)
|
|
||||||
unistd.close(sock)
|
|
||||||
retCode = success and 0 or 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return retCode
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:checkHosts()
|
|
||||||
local checkFunc = (self.serviceConfig.check_type == 1) and self.pingHost or self.TCPConnectionToHost
|
|
||||||
local retCode = 1
|
|
||||||
for k, v in ipairs(self.parsedHosts) do
|
|
||||||
for i = 1, self.serviceConfig.connection_attempts do
|
|
||||||
if checkFunc(self, v.addr, v.port) == 0 then
|
|
||||||
retCode = 0
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if retCode == 0 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return retCode
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:breakMain(signo)
|
|
||||||
RUNNING = false
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:resetUiCounter(signo)
|
|
||||||
self.uiCounter = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:main()
|
|
||||||
signal.signal(signal.SIGTERM, function(signo) self:breakMain(signo) end)
|
|
||||||
signal.signal(signal.SIGINT, function(signo) self:breakMain(signo) end)
|
|
||||||
signal.signal(signal.SIGQUIT, function(signo) self:breakMain(signo) end)
|
|
||||||
signal.signal(signal.SIGUSR1, function(signo) self:resetUiCounter(signo) end)
|
|
||||||
|
|
||||||
local lastStatus, currentStatus, mTimeNow, mTimeDiff, mLastTime, uiTimeNow, uiLastTime
|
|
||||||
local interval = self.serviceConfig.interval_up
|
|
||||||
local counter = 0
|
|
||||||
local onStart = true
|
|
||||||
RUNNING = true
|
|
||||||
while RUNNING do
|
|
||||||
if counter == 0 or counter >= interval then
|
|
||||||
currentStatus = self:checkHosts()
|
|
||||||
if onStart or not stat.stat(self.statusFile) then
|
|
||||||
self:writeValueToFile(self.statusFile, self:statusJson(
|
|
||||||
currentStatus, self.serviceConfig.instance))
|
|
||||||
onStart = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if currentStatus == 0 then
|
|
||||||
interval = self.serviceConfig.interval_up
|
|
||||||
if lastStatus ~= nil and currentStatus ~= lastStatus then
|
|
||||||
self:writeValueToFile(self.statusFile, self:statusJson(
|
|
||||||
currentStatus, self.serviceConfig.instance))
|
|
||||||
self:writeLogMessage("notice", "Connected")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
interval = self.serviceConfig.interval_down
|
|
||||||
if lastStatus ~= nil and currentStatus ~= lastStatus then
|
|
||||||
self:writeValueToFile(self.statusFile, self:statusJson(
|
|
||||||
currentStatus, self.serviceConfig.instance))
|
|
||||||
self:writeLogMessage("notice", "Disconnected")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
counter = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
mTimeDiff = 0
|
|
||||||
for _, e in ipairs(self.modules) do
|
|
||||||
mTimeNow = time.clock_gettime(time.CLOCK_MONOTONIC).tv_sec
|
|
||||||
if mLastTime then
|
|
||||||
mTimeDiff = mTimeDiff + mTimeNow - mLastTime
|
|
||||||
else
|
|
||||||
mTimeDiff = 1
|
|
||||||
end
|
|
||||||
mLastTime = mTimeNow
|
|
||||||
e:run(currentStatus, lastStatus, mTimeDiff)
|
|
||||||
end
|
|
||||||
|
|
||||||
local modulesStatus = {}
|
|
||||||
for k, v in ipairs(self.modules) do
|
|
||||||
if v.status ~= nil then
|
|
||||||
modulesStatus[v.name] = v.status
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if next(modulesStatus) then
|
|
||||||
self:writeValueToFile(self.statusFile, self:statusJson(
|
|
||||||
currentStatus, self.serviceConfig.instance, modulesStatus))
|
|
||||||
end
|
|
||||||
|
|
||||||
lastStatus = currentStatus
|
|
||||||
unistd.sleep(1)
|
|
||||||
counter = counter + 1
|
|
||||||
|
|
||||||
if self.mode == 2 then
|
|
||||||
uiTimeNow = time.clock_gettime(time.CLOCK_MONOTONIC).tv_sec
|
|
||||||
if uiLastTime then
|
|
||||||
self.uiCounter = self.uiCounter + uiTimeNow - uiLastTime
|
|
||||||
else
|
|
||||||
self.uiCounter = self.uiCounter + 1
|
|
||||||
end
|
|
||||||
uiLastTime = uiTimeNow
|
|
||||||
if self.uiCounter >= self.uiRunTime then
|
|
||||||
self:breakMain(signal.SIGTERM)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:removeProcessFiles()
|
|
||||||
os.remove(string.format(
|
|
||||||
"%s/%s.%s.pid", self.commonDir, self.appName, self.serviceConfig.instance))
|
|
||||||
os.remove(string.format(
|
|
||||||
"%s/%s.%s.status", self.commonDir, self.appName, self.serviceConfig.instance))
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:status()
|
|
||||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
|
||||||
if ok then
|
|
||||||
local appName = self.appName:gsub("-", "%%-")
|
|
||||||
for item in commonDir do
|
|
||||||
if item:match("^" .. appName .. ".-%.pid$") then
|
|
||||||
return "running"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return "stoped"
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:inetStatus()
|
|
||||||
local inetStat = '{"instances":[]}'
|
|
||||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
|
||||||
if ok then
|
|
||||||
local appName = self.appName:gsub("-", "%%-")
|
|
||||||
local lines = {}
|
|
||||||
for item in commonDir do
|
|
||||||
if item:match("^" .. appName .. ".-%.status$") then
|
|
||||||
lines[#lines + 1] = self:readValueFromFile(
|
|
||||||
string.format("%s/%s", self.commonDir, item))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
inetStat = '{"instances":[' .. table.concat(lines, ",") .. "]}"
|
|
||||||
end
|
|
||||||
return inetStat
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:stopInstance(pidFile)
|
|
||||||
local retVal
|
|
||||||
if stat.stat(pidFile) then
|
|
||||||
pidValue = self:readValueFromFile(pidFile)
|
|
||||||
if pidValue then
|
|
||||||
local ok, errMsg, errNum
|
|
||||||
for i = 0, 10 do
|
|
||||||
ok, errMsg, errNum = signal.kill(tonumber(pidValue), signal.SIGTERM)
|
|
||||||
if ok then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not ok then
|
|
||||||
io.stderr:write(string.format(
|
|
||||||
'Process stop error: %s (%s). PID: "%s"\n', errMsg, errNum, pidValue))
|
|
||||||
end
|
|
||||||
if errNum == 3 then
|
|
||||||
os.remove(pidFile)
|
|
||||||
end
|
|
||||||
retVal = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not pidValue then
|
|
||||||
io.stderr:write(
|
|
||||||
string.format('PID file "%s" does not exist. %s not running?\n',
|
|
||||||
pidFile, self.appName))
|
|
||||||
end
|
|
||||||
return retVal
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:stop()
|
|
||||||
local appName = self.appName:gsub("-", "%%-")
|
|
||||||
local success
|
|
||||||
for i = 0, 10 do
|
|
||||||
success = true
|
|
||||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
|
||||||
if ok then
|
|
||||||
for item in commonDir do
|
|
||||||
if item:match("^" .. appName .. ".-%.pid$") then
|
|
||||||
self:stopInstance(string.format("%s/%s", self.commonDir, item))
|
|
||||||
success = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if success then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
unistd.sleep(1)
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:setSIGUSR()
|
|
||||||
local appName = self.appName:gsub("-", "%%-")
|
|
||||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
|
||||||
if ok then
|
|
||||||
for item in commonDir do
|
|
||||||
if item:match("^" .. appName .. ".-%.pid$") then
|
|
||||||
pidValue = self:readValueFromFile(string.format("%s/%s", self.commonDir, item))
|
|
||||||
if pidValue then
|
|
||||||
signal.kill(tonumber(pidValue), signal.SIGUSR1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:preRun()
|
|
||||||
-- Exit if internet-detector mode != (1 or 2)
|
|
||||||
if self.mode ~= 1 and self.mode ~= 2 then
|
|
||||||
io.stderr:write(string.format('Start failed, mode != (1 or 2)\n', self.appName))
|
|
||||||
os.exit(0)
|
|
||||||
end
|
|
||||||
if stat.stat(self.pidFile) then
|
|
||||||
io.stderr:write(
|
|
||||||
string.format('PID file "%s" already exist. %s already running?\n',
|
|
||||||
self.pidFile, self.appName))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:run()
|
|
||||||
local pidValue = unistd.getpid()
|
|
||||||
self:writeValueToFile(self.pidFile, pidValue)
|
|
||||||
if self.enableLogger then
|
|
||||||
syslog.openlog(self.appName, syslog.LOG_PID, syslog.LOG_DAEMON)
|
|
||||||
end
|
|
||||||
self:writeLogMessage("info", "started")
|
|
||||||
self:loadModules()
|
|
||||||
|
|
||||||
-- Loaded modules
|
|
||||||
local modules = {}
|
|
||||||
for _, v in ipairs(self.modules) do
|
|
||||||
modules[#modules + 1] = string.format("%s", v.name)
|
|
||||||
end
|
|
||||||
if #modules > 0 then
|
|
||||||
self:writeLogMessage(
|
|
||||||
"info", string.format("Loaded modules: %s", table.concat(modules, ", "))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.debug then
|
|
||||||
local function inspectTable()
|
|
||||||
local tables = {}, f
|
|
||||||
f = function(t, prefix)
|
|
||||||
tables[t] = true
|
|
||||||
for k, v in pairs(t) do
|
|
||||||
io.stdout:write(string.format(
|
|
||||||
"%s%s = %s\n", prefix, k, tostring(v))
|
|
||||||
)
|
|
||||||
if type(v) == "table" and not tables[v] then
|
|
||||||
f(v, string.format("%s%s.", prefix, k))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return f
|
|
||||||
end
|
|
||||||
|
|
||||||
io.stdout:write("--- Config ---\n")
|
|
||||||
inspectTable()(self, "self.")
|
|
||||||
io.stdout:flush()
|
|
||||||
end
|
|
||||||
|
|
||||||
self:writeValueToFile(
|
|
||||||
self.statusFile, self:statusJson(-1, self.serviceConfig.instance))
|
|
||||||
|
|
||||||
self:main()
|
|
||||||
|
|
||||||
self:removeProcessFiles()
|
|
||||||
if self.enableLogger then
|
|
||||||
self:writeLogMessage("info", "stoped")
|
|
||||||
syslog.closelog()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:noDaemon()
|
|
||||||
if not self:preRun() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self:run()
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:daemon()
|
|
||||||
if not self:preRun() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
-- UNIX double fork
|
|
||||||
if unistd.fork() == 0 then
|
|
||||||
unistd.setpid("s")
|
|
||||||
if unistd.fork() == 0 then
|
|
||||||
unistd.chdir("/")
|
|
||||||
stat.umask(0)
|
|
||||||
local devnull = fcntl.open("/dev/null", fcntl.O_RDWR)
|
|
||||||
io.stdout:flush()
|
|
||||||
io.stderr:flush()
|
|
||||||
unistd.dup2(devnull, 0) -- io.stdin
|
|
||||||
unistd.dup2(devnull, 1) -- io.stdout
|
|
||||||
unistd.dup2(devnull, 2) -- io.stderr
|
|
||||||
self:run()
|
|
||||||
unistd.close(devnull)
|
|
||||||
end
|
|
||||||
os.exit(0)
|
|
||||||
end
|
|
||||||
os.exit(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function InternetDetector:setServiceConfig(instance)
|
|
||||||
if self:loadUCIConfig("instance", instance) then
|
|
||||||
self:parseHosts()
|
|
||||||
if self.mode == 2 then
|
|
||||||
self.enableLogger = false
|
|
||||||
self.noModules = true
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Main section
|
|
||||||
|
|
||||||
local function help()
|
local function help()
|
||||||
return string.format(
|
return string.format(
|
||||||
|
|||||||
643
internet-detector/files/usr/lib/lua/internet-detector/main.lua
Normal file
643
internet-detector/files/usr/lib/lua/internet-detector/main.lua
Normal file
@@ -0,0 +1,643 @@
|
|||||||
|
|
||||||
|
local dirent = require("posix.dirent")
|
||||||
|
local fcntl = require("posix.fcntl")
|
||||||
|
local signal = require("posix.signal")
|
||||||
|
local socket = require("posix.sys.socket")
|
||||||
|
local stat = require("posix.sys.stat")
|
||||||
|
local syslog = require("posix.syslog")
|
||||||
|
local time = require("posix.time")
|
||||||
|
local unistd = require("posix.unistd")
|
||||||
|
local uci = require("uci")
|
||||||
|
|
||||||
|
-- Default settings
|
||||||
|
|
||||||
|
local InternetDetector = {
|
||||||
|
mode = 0, -- 0: disabled, 1: Service, 2: UI detector
|
||||||
|
enableLogger = true,
|
||||||
|
hostname = "OpenWrt",
|
||||||
|
appName = "internet-detector",
|
||||||
|
commonDir = "/tmp/run",
|
||||||
|
libDir = "/usr/lib/lua",
|
||||||
|
pingCmd = "/bin/ping",
|
||||||
|
pingParams = "-c 1",
|
||||||
|
uiRunTime = 30,
|
||||||
|
noModules = false,
|
||||||
|
uiAvailModules = { mod_public_ip = true },
|
||||||
|
debug = false,
|
||||||
|
serviceConfig = {
|
||||||
|
hosts = {
|
||||||
|
[1] = "8.8.8.8",
|
||||||
|
[2] = "1.1.1.1",
|
||||||
|
},
|
||||||
|
check_type = 0, -- 0: TCP, 1: ICMP
|
||||||
|
tcp_port = 53,
|
||||||
|
icmp_packet_size = 56,
|
||||||
|
interval_up = 30,
|
||||||
|
interval_down = 5,
|
||||||
|
connection_attempts = 2,
|
||||||
|
connection_timeout = 2,
|
||||||
|
iface = nil,
|
||||||
|
instance = nil,
|
||||||
|
},
|
||||||
|
modules = {},
|
||||||
|
parsedHosts = {},
|
||||||
|
uiCounter = 0,
|
||||||
|
}
|
||||||
|
InternetDetector.configDir = string.format("/etc/%s", InternetDetector.appName)
|
||||||
|
InternetDetector.modulesDir = string.format(
|
||||||
|
"%s/%s", InternetDetector.libDir, InternetDetector.appName)
|
||||||
|
|
||||||
|
-- Loading settings from UCI
|
||||||
|
|
||||||
|
local uciCursor = uci.cursor()
|
||||||
|
local mode, err = uciCursor:get(InternetDetector.appName, "config", "mode")
|
||||||
|
if mode ~= nil then
|
||||||
|
InternetDetector.mode = tonumber(mode)
|
||||||
|
elseif err then
|
||||||
|
io.stderr:write(string.format("Error: %s\n", err))
|
||||||
|
end
|
||||||
|
local enableLogger, err = uciCursor:get(InternetDetector.appName, "config", "enable_logger")
|
||||||
|
if enableLogger ~= nil then
|
||||||
|
InternetDetector.enableLogger = (tonumber(enableLogger) ~= 0)
|
||||||
|
elseif err then
|
||||||
|
io.stderr:write(string.format("Error: %s\n", err))
|
||||||
|
end
|
||||||
|
local hostname, err = uciCursor:get("system", "@[0]", "hostname")
|
||||||
|
if hostname ~= nil then
|
||||||
|
InternetDetector.hostname = hostname
|
||||||
|
elseif err then
|
||||||
|
io.stderr:write(string.format("Error: %s\n", err))
|
||||||
|
end
|
||||||
|
|
||||||
|
local _RUNNING
|
||||||
|
|
||||||
|
function InternetDetector:prequire(package)
|
||||||
|
local retVal, pkg = pcall(require, package)
|
||||||
|
return retVal and pkg
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:loadUCIConfig(sType, instance)
|
||||||
|
local success
|
||||||
|
local num = 0
|
||||||
|
uciCursor:foreach(
|
||||||
|
self.appName,
|
||||||
|
sType,
|
||||||
|
function(s)
|
||||||
|
if s[".name"] == instance then
|
||||||
|
for k, v in pairs(s) do
|
||||||
|
if type(v) == "string" and v:match("^[%d]+$") then
|
||||||
|
v = tonumber(v)
|
||||||
|
end
|
||||||
|
self.serviceConfig[k] = v
|
||||||
|
end
|
||||||
|
success = true
|
||||||
|
self.serviceConfig.instanceNum = num
|
||||||
|
end
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
)
|
||||||
|
self.serviceConfig.instance = instance
|
||||||
|
self.pidFile = string.format(
|
||||||
|
"%s/%s.%s.pid", self.commonDir, self.appName, instance)
|
||||||
|
self.statusFile = string.format(
|
||||||
|
"%s/%s.%s.status", self.commonDir, self.appName, instance)
|
||||||
|
return success
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:writeValueToFile(filePath, str)
|
||||||
|
local retValue = false
|
||||||
|
local fh = io.open(filePath, "w")
|
||||||
|
if fh then
|
||||||
|
fh:setvbuf("no")
|
||||||
|
fh:write(string.format("%s\n", str))
|
||||||
|
fh:close()
|
||||||
|
retValue = true
|
||||||
|
end
|
||||||
|
return retValue
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:readValueFromFile(filePath)
|
||||||
|
local retValue
|
||||||
|
local fh = io.open(filePath, "r")
|
||||||
|
if fh then
|
||||||
|
retValue = fh:read("*l")
|
||||||
|
fh:close()
|
||||||
|
end
|
||||||
|
return retValue
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:statusJson(inet, instance, t)
|
||||||
|
local lines = { [1] = string.format(
|
||||||
|
'{"instance":"%s","num":"%d","inet":%d',
|
||||||
|
instance,
|
||||||
|
self.serviceConfig.instanceNum,
|
||||||
|
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
|
||||||
|
|
||||||
|
function InternetDetector:writeLogMessage(level, msg)
|
||||||
|
if self.enableLogger then
|
||||||
|
local levels = {
|
||||||
|
emerg = syslog.LOG_EMERG,
|
||||||
|
alert = syslog.LOG_ALERT,
|
||||||
|
crit = syslog.LOG_CRIT,
|
||||||
|
err = syslog.LOG_ERR,
|
||||||
|
warning = syslog.LOG_WARNING,
|
||||||
|
notice = syslog.LOG_NOTICE,
|
||||||
|
info = syslog.LOG_INFO,
|
||||||
|
debug = syslog.LOG_DEBUG,
|
||||||
|
}
|
||||||
|
syslog.syslog(levels[level] or syslog.LOG_INFO, string.format(
|
||||||
|
"%s: %s", self.serviceConfig.instance or "", msg))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:loadModules()
|
||||||
|
self.modules = {}
|
||||||
|
local ok, modulesDir = pcall(dirent.files, self.modulesDir)
|
||||||
|
if ok then
|
||||||
|
for item in modulesDir do
|
||||||
|
if item:match("^mod_") then
|
||||||
|
local modName = item:gsub("%.lua$", "")
|
||||||
|
if self.noModules and not self.uiAvailModules[modName] then
|
||||||
|
else
|
||||||
|
local modConfig = {}
|
||||||
|
for k, v in pairs(self.serviceConfig) do
|
||||||
|
if k:match("^" .. modName) then
|
||||||
|
modConfig[k:gsub("^" .. modName .. "_", "")] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if modConfig.enabled == 1 then
|
||||||
|
local m
|
||||||
|
if self.debug then
|
||||||
|
m = require(string.format("%s.%s", self.appName, modName))
|
||||||
|
else
|
||||||
|
m = self:prequire(string.format("%s.%s", self.appName, modName))
|
||||||
|
end
|
||||||
|
if m then
|
||||||
|
m.config = self
|
||||||
|
m.syslog = function(level, msg) self:writeLogMessage(level, msg) end
|
||||||
|
m.writeValue = function(filePath, str) return self:writeValueToFile(filePath, str) end
|
||||||
|
m.readValue = function(filePath) return self:readValueFromFile(filePath) end
|
||||||
|
m:init(modConfig)
|
||||||
|
self.modules[#self.modules + 1] = m
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(self.modules, function(a, b) return a.runPrio < b.runPrio end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:parseHost(host)
|
||||||
|
local addr, port = host:match("^([^%[%]:]+):?(%d?%d?%d?%d?%d?)$")
|
||||||
|
if not addr then
|
||||||
|
addr, port = host:match("^%[?([^%[%]]+)%]?:?(%d?%d?%d?%d?%d?)$")
|
||||||
|
end
|
||||||
|
return addr, tonumber(port)
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:parseHosts()
|
||||||
|
self.parsedHosts = {}
|
||||||
|
for k, v in ipairs(self.serviceConfig.hosts) do
|
||||||
|
local addr, port = self:parseHost(v)
|
||||||
|
self.parsedHosts[k] = { addr = addr, port = port }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:pingHost(host)
|
||||||
|
local ping = string.format(
|
||||||
|
"%s %s -W %d -s %d%s %s > /dev/null 2>&1",
|
||||||
|
self.pingCmd,
|
||||||
|
self.pingParams,
|
||||||
|
self.serviceConfig.connection_timeout,
|
||||||
|
self.serviceConfig.icmp_packet_size,
|
||||||
|
self.serviceConfig.iface and (" -I " .. self.serviceConfig.iface) or "",
|
||||||
|
host
|
||||||
|
)
|
||||||
|
local retCode = os.execute(ping)
|
||||||
|
|
||||||
|
if self.debug then
|
||||||
|
io.stdout:write(string.format(
|
||||||
|
"--- Ping ---\ntime = %s\n%s\nretCode = %s\n", os.time(), ping, retCode)
|
||||||
|
)
|
||||||
|
io.stdout:flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
return retCode
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:TCPConnectionToHost(host, port)
|
||||||
|
local retCode = 1
|
||||||
|
local saTable, errMsg, errNum = socket.getaddrinfo(host, port or self.serviceConfig.tcp_port)
|
||||||
|
|
||||||
|
if not saTable then
|
||||||
|
if self.debug then
|
||||||
|
io.stdout:write(string.format(
|
||||||
|
"GETADDRINFO ERROR: %s, %s\n", errMsg, errNum))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local family = saTable[1].family
|
||||||
|
if family then
|
||||||
|
local sock, errMsg, errNum = socket.socket(family, socket.SOCK_STREAM, 0)
|
||||||
|
|
||||||
|
if not sock then
|
||||||
|
if self.debug then
|
||||||
|
io.stdout:write(string.format(
|
||||||
|
"SOCKET ERROR: %s, %s\n", errMsg, errNum))
|
||||||
|
end
|
||||||
|
return retCode
|
||||||
|
end
|
||||||
|
|
||||||
|
socket.setsockopt(sock, socket.SOL_SOCKET,
|
||||||
|
socket.SO_SNDTIMEO, self.serviceConfig.connection_timeout, 0)
|
||||||
|
socket.setsockopt(sock, socket.SOL_SOCKET,
|
||||||
|
socket.SO_RCVTIMEO, self.serviceConfig.connection_timeout, 0)
|
||||||
|
|
||||||
|
if self.serviceConfig.iface then
|
||||||
|
local ok, errMsg, errNum = socket.setsockopt(sock, socket.SOL_SOCKET,
|
||||||
|
socket.SO_BINDTODEVICE, self.serviceConfig.iface)
|
||||||
|
if not ok then
|
||||||
|
if self.debug then
|
||||||
|
io.stdout:write(string.format(
|
||||||
|
"SOCKET ERROR: %s, %s\n", errMsg, errNum))
|
||||||
|
end
|
||||||
|
|
||||||
|
unistd.close(sock)
|
||||||
|
return retCode
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local success = socket.connect(sock, saTable[1])
|
||||||
|
|
||||||
|
if self.debug then
|
||||||
|
if not success then
|
||||||
|
io.stdout:write(string.format(
|
||||||
|
"SOCKET CONNECT ERROR: %s\n", tostring(success)))
|
||||||
|
end
|
||||||
|
local sockTable, err_s, e_s = socket.getsockname(sock)
|
||||||
|
local peerTable, err_p, e_p = socket.getpeername(sock)
|
||||||
|
if not sockTable then
|
||||||
|
sockTable = {}
|
||||||
|
io.stdout:write(
|
||||||
|
string.format("SOCKET ERROR: %s, %s\n", err_s, e_s))
|
||||||
|
end
|
||||||
|
if not peerTable then
|
||||||
|
peerTable = {}
|
||||||
|
io.stdout:write(
|
||||||
|
string.format("SOCKET ERROR: %s, %s\n", err_p, e_p))
|
||||||
|
end
|
||||||
|
io.stdout:write(string.format(
|
||||||
|
"--- TCP ---\ntime = %s\nconnection_timeout = %s\niface = %s\nhost:port = [%s]:%s\nsockname = [%s]:%s\npeername = [%s]:%s\nsuccess = %s\n",
|
||||||
|
os.time(),
|
||||||
|
self.serviceConfig.connection_timeout,
|
||||||
|
tostring(self.serviceConfig.iface),
|
||||||
|
host,
|
||||||
|
port or self.serviceConfig.tcp_port,
|
||||||
|
tostring(sockTable.addr),
|
||||||
|
tostring(sockTable.port),
|
||||||
|
tostring(peerTable.addr),
|
||||||
|
tostring(peerTable.port),
|
||||||
|
tostring(success))
|
||||||
|
)
|
||||||
|
io.stdout:flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
socket.shutdown(sock, socket.SHUT_RDWR)
|
||||||
|
unistd.close(sock)
|
||||||
|
retCode = success and 0 or 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return retCode
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:checkHosts()
|
||||||
|
local checkFunc = (self.serviceConfig.check_type == 1) and self.pingHost or self.TCPConnectionToHost
|
||||||
|
local retCode = 1
|
||||||
|
for k, v in ipairs(self.parsedHosts) do
|
||||||
|
for i = 1, self.serviceConfig.connection_attempts do
|
||||||
|
if checkFunc(self, v.addr, v.port) == 0 then
|
||||||
|
retCode = 0
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if retCode == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return retCode
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:breakMainLoop(signo)
|
||||||
|
_RUNNING = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:resetUiCounter(signo)
|
||||||
|
self.uiCounter = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:mainLoop()
|
||||||
|
signal.signal(signal.SIGTERM, function(signo) self:breakMainLoop(signo) end)
|
||||||
|
signal.signal(signal.SIGINT, function(signo) self:breakMainLoop(signo) end)
|
||||||
|
signal.signal(signal.SIGQUIT, function(signo) self:breakMainLoop(signo) end)
|
||||||
|
signal.signal(signal.SIGUSR1, function(signo) self:resetUiCounter(signo) end)
|
||||||
|
|
||||||
|
local lastStatus, currentStatus, mTimeNow, mTimeDiff, mLastTime, uiTimeNow, uiLastTime
|
||||||
|
local interval = self.serviceConfig.interval_up
|
||||||
|
local counter = 0
|
||||||
|
local onStart = true
|
||||||
|
_RUNNING = true
|
||||||
|
while _RUNNING do
|
||||||
|
if counter == 0 or counter >= interval then
|
||||||
|
currentStatus = self:checkHosts()
|
||||||
|
if onStart or not stat.stat(self.statusFile) then
|
||||||
|
self:writeValueToFile(self.statusFile, self:statusJson(
|
||||||
|
currentStatus, self.serviceConfig.instance))
|
||||||
|
onStart = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if currentStatus == 0 then
|
||||||
|
interval = self.serviceConfig.interval_up
|
||||||
|
if lastStatus ~= nil and currentStatus ~= lastStatus then
|
||||||
|
self:writeValueToFile(self.statusFile, self:statusJson(
|
||||||
|
currentStatus, self.serviceConfig.instance))
|
||||||
|
self:writeLogMessage("notice", "Connected")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
interval = self.serviceConfig.interval_down
|
||||||
|
if lastStatus ~= nil and currentStatus ~= lastStatus then
|
||||||
|
self:writeValueToFile(self.statusFile, self:statusJson(
|
||||||
|
currentStatus, self.serviceConfig.instance))
|
||||||
|
self:writeLogMessage("notice", "Disconnected")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
mTimeDiff = 0
|
||||||
|
for _, e in ipairs(self.modules) do
|
||||||
|
mTimeNow = time.clock_gettime(time.CLOCK_MONOTONIC).tv_sec
|
||||||
|
if mLastTime then
|
||||||
|
mTimeDiff = mTimeDiff + mTimeNow - mLastTime
|
||||||
|
else
|
||||||
|
mTimeDiff = 1
|
||||||
|
end
|
||||||
|
mLastTime = mTimeNow
|
||||||
|
e:run(currentStatus, lastStatus, mTimeDiff)
|
||||||
|
end
|
||||||
|
|
||||||
|
local modulesStatus = {}
|
||||||
|
for k, v in ipairs(self.modules) do
|
||||||
|
if v.status ~= nil then
|
||||||
|
modulesStatus[v.name] = v.status
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if next(modulesStatus) then
|
||||||
|
self:writeValueToFile(self.statusFile, self:statusJson(
|
||||||
|
currentStatus, self.serviceConfig.instance, modulesStatus))
|
||||||
|
end
|
||||||
|
|
||||||
|
lastStatus = currentStatus
|
||||||
|
unistd.sleep(1)
|
||||||
|
counter = counter + 1
|
||||||
|
|
||||||
|
if self.mode == 2 then
|
||||||
|
uiTimeNow = time.clock_gettime(time.CLOCK_MONOTONIC).tv_sec
|
||||||
|
if uiLastTime then
|
||||||
|
self.uiCounter = self.uiCounter + uiTimeNow - uiLastTime
|
||||||
|
else
|
||||||
|
self.uiCounter = self.uiCounter + 1
|
||||||
|
end
|
||||||
|
uiLastTime = uiTimeNow
|
||||||
|
|
||||||
|
if self.uiCounter >= self.uiRunTime then
|
||||||
|
self:breakMainLoop(signal.SIGTERM)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:removeProcessFiles()
|
||||||
|
os.remove(string.format(
|
||||||
|
"%s/%s.%s.pid", self.commonDir, self.appName, self.serviceConfig.instance))
|
||||||
|
os.remove(string.format(
|
||||||
|
"%s/%s.%s.status", self.commonDir, self.appName, self.serviceConfig.instance))
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:status()
|
||||||
|
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||||
|
if ok then
|
||||||
|
local appName = self.appName:gsub("-", "%%-")
|
||||||
|
for item in commonDir do
|
||||||
|
if item:match("^" .. appName .. ".-%.pid$") then
|
||||||
|
return "running"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return "stoped"
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:inetStatus()
|
||||||
|
local inetStat = '{"instances":[]}'
|
||||||
|
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||||
|
if ok then
|
||||||
|
local appName = self.appName:gsub("-", "%%-")
|
||||||
|
local lines = {}
|
||||||
|
for item in commonDir do
|
||||||
|
if item:match("^" .. appName .. ".-%.status$") then
|
||||||
|
lines[#lines + 1] = self:readValueFromFile(
|
||||||
|
string.format("%s/%s", self.commonDir, item))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
inetStat = '{"instances":[' .. table.concat(lines, ",") .. "]}"
|
||||||
|
end
|
||||||
|
return inetStat
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:stopInstance(pidFile)
|
||||||
|
local retVal
|
||||||
|
if stat.stat(pidFile) then
|
||||||
|
pidValue = self:readValueFromFile(pidFile)
|
||||||
|
if pidValue then
|
||||||
|
local ok, errMsg, errNum
|
||||||
|
for i = 0, 10 do
|
||||||
|
ok, errMsg, errNum = signal.kill(tonumber(pidValue), signal.SIGTERM)
|
||||||
|
if ok then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not ok then
|
||||||
|
io.stderr:write(string.format(
|
||||||
|
'Process stop error: %s (%s). PID: "%s"\n', errMsg, errNum, pidValue))
|
||||||
|
end
|
||||||
|
if errNum == 3 then
|
||||||
|
os.remove(pidFile)
|
||||||
|
end
|
||||||
|
retVal = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not pidValue then
|
||||||
|
io.stderr:write(
|
||||||
|
string.format('PID file "%s" does not exist. %s not running?\n',
|
||||||
|
pidFile, self.appName))
|
||||||
|
end
|
||||||
|
return retVal
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:stop()
|
||||||
|
local appName = self.appName:gsub("-", "%%-")
|
||||||
|
local success
|
||||||
|
for i = 0, 10 do
|
||||||
|
success = true
|
||||||
|
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||||
|
if ok then
|
||||||
|
for item in commonDir do
|
||||||
|
if item:match("^" .. appName .. ".-%.pid$") then
|
||||||
|
self:stopInstance(string.format("%s/%s", self.commonDir, item))
|
||||||
|
success = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if success then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
unistd.sleep(1)
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:setSIGUSR()
|
||||||
|
local appName = self.appName:gsub("-", "%%-")
|
||||||
|
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||||
|
if ok then
|
||||||
|
for item in commonDir do
|
||||||
|
if item:match("^" .. appName .. ".-%.pid$") then
|
||||||
|
pidValue = self:readValueFromFile(string.format("%s/%s", self.commonDir, item))
|
||||||
|
if pidValue then
|
||||||
|
signal.kill(tonumber(pidValue), signal.SIGUSR1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:preRun()
|
||||||
|
-- Exit if internet-detector mode != (1 or 2)
|
||||||
|
if self.mode ~= 1 and self.mode ~= 2 then
|
||||||
|
io.stderr:write(string.format('Start failed, mode != (1 or 2)\n', self.appName))
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
if stat.stat(self.pidFile) then
|
||||||
|
io.stderr:write(
|
||||||
|
string.format('PID file "%s" already exist. %s already running?\n',
|
||||||
|
self.pidFile, self.appName))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:run()
|
||||||
|
local pidValue = unistd.getpid()
|
||||||
|
self:writeValueToFile(self.pidFile, pidValue)
|
||||||
|
if self.enableLogger then
|
||||||
|
syslog.openlog(self.appName, syslog.LOG_PID, syslog.LOG_DAEMON)
|
||||||
|
end
|
||||||
|
self:writeLogMessage("info", "started")
|
||||||
|
self:loadModules()
|
||||||
|
|
||||||
|
-- Loaded modules
|
||||||
|
local modules = {}
|
||||||
|
for _, v in ipairs(self.modules) do
|
||||||
|
modules[#modules + 1] = string.format("%s", v.name)
|
||||||
|
end
|
||||||
|
if #modules > 0 then
|
||||||
|
self:writeLogMessage(
|
||||||
|
"info", string.format("Loaded modules: %s", table.concat(modules, ", "))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.debug then
|
||||||
|
local function inspectTable()
|
||||||
|
local tables = {}, f
|
||||||
|
f = function(t, prefix)
|
||||||
|
tables[t] = true
|
||||||
|
for k, v in pairs(t) do
|
||||||
|
io.stdout:write(string.format(
|
||||||
|
"%s%s = %s\n", prefix, k, tostring(v))
|
||||||
|
)
|
||||||
|
if type(v) == "table" and not tables[v] then
|
||||||
|
f(v, string.format("%s%s.", prefix, k))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
io.stdout:write("--- Config ---\n")
|
||||||
|
inspectTable()(self, "self.")
|
||||||
|
io.stdout:flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
self:writeValueToFile(
|
||||||
|
self.statusFile, self:statusJson(-1, self.serviceConfig.instance))
|
||||||
|
|
||||||
|
self:mainLoop()
|
||||||
|
|
||||||
|
self:removeProcessFiles()
|
||||||
|
if self.enableLogger then
|
||||||
|
self:writeLogMessage("info", "stoped")
|
||||||
|
syslog.closelog()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:noDaemon()
|
||||||
|
if not self:preRun() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self:run()
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:daemon()
|
||||||
|
if not self:preRun() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- UNIX double fork
|
||||||
|
if unistd.fork() == 0 then
|
||||||
|
unistd.setpid("s")
|
||||||
|
if unistd.fork() == 0 then
|
||||||
|
unistd.chdir("/")
|
||||||
|
stat.umask(0)
|
||||||
|
local devnull = fcntl.open("/dev/null", fcntl.O_RDWR)
|
||||||
|
io.stdout:flush()
|
||||||
|
io.stderr:flush()
|
||||||
|
unistd.dup2(devnull, 0) -- io.stdin
|
||||||
|
unistd.dup2(devnull, 1) -- io.stdout
|
||||||
|
unistd.dup2(devnull, 2) -- io.stderr
|
||||||
|
self:run()
|
||||||
|
unistd.close(devnull)
|
||||||
|
end
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function InternetDetector:setServiceConfig(instance)
|
||||||
|
if self:loadUCIConfig("instance", instance) then
|
||||||
|
self:parseHosts()
|
||||||
|
if self.mode == 2 then
|
||||||
|
self.enableLogger = false
|
||||||
|
self.noModules = true
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return InternetDetector
|
||||||
@@ -38,12 +38,17 @@ function Module:resetLeds()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Module:init(t)
|
function Module:init(t)
|
||||||
self.ledName = t.led_name
|
if not t.led_name then
|
||||||
if not self.ledName then
|
|
||||||
return
|
return
|
||||||
|
else
|
||||||
|
self.ledName = t.led_name
|
||||||
end
|
end
|
||||||
|
if t.led_action_1 ~= nil then
|
||||||
self.ledAction1 = tonumber(t.led_action_1)
|
self.ledAction1 = tonumber(t.led_action_1)
|
||||||
|
end
|
||||||
|
if t.led_action_2 ~= nil then
|
||||||
self.ledAction2 = tonumber(t.led_action_2)
|
self.ledAction2 = tonumber(t.led_action_2)
|
||||||
|
end
|
||||||
self._ledDir = string.format("%s/%s", self.sysLedsDir, self.ledName)
|
self._ledDir = string.format("%s/%s", self.sysLedsDir, self.ledName)
|
||||||
self._ledMaxBrightnessFile = string.format("%s/max_brightness", self._ledDir)
|
self._ledMaxBrightnessFile = string.format("%s/max_brightness", self._ledDir)
|
||||||
self._ledBrightnessFile = string.format("%s/brightness", self._ledDir)
|
self._ledBrightnessFile = string.format("%s/brightness", self._ledDir)
|
||||||
@@ -8,9 +8,9 @@ local Module = {
|
|||||||
syslog = function(level, msg) return true end,
|
syslog = function(level, msg) return true end,
|
||||||
writeValue = function(filePath, str) return false end,
|
writeValue = function(filePath, str) return false end,
|
||||||
readValue = function(filePath) return nil end,
|
readValue = function(filePath) return nil end,
|
||||||
iface = false,
|
deadPeriod = 900,
|
||||||
attempts = 0,
|
attempts = 1,
|
||||||
deadPeriod = 0,
|
iface = nil,
|
||||||
restartTimeout = 0,
|
restartTimeout = 0,
|
||||||
status = nil,
|
status = nil,
|
||||||
_attemptsCounter = 0,
|
_attemptsCounter = 0,
|
||||||
@@ -22,6 +22,9 @@ function Module:toggleFunc(flag)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Module:toggleDevice(flag)
|
function Module:toggleDevice(flag)
|
||||||
|
if not self.iface then
|
||||||
|
return
|
||||||
|
end
|
||||||
local ip = "/sbin/ip"
|
local ip = "/sbin/ip"
|
||||||
if unistd.access(ip, "x") then
|
if unistd.access(ip, "x") then
|
||||||
return os.execute(string.format(
|
return os.execute(string.format(
|
||||||
@@ -31,6 +34,9 @@ function Module:toggleDevice(flag)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Module:toggleIface(flag)
|
function Module:toggleIface(flag)
|
||||||
|
if not self.iface then
|
||||||
|
return
|
||||||
|
end
|
||||||
return os.execute(
|
return os.execute(
|
||||||
string.format("%s %s", (flag and "/sbin/ifup" or "/sbin/ifdown"), self.iface)
|
string.format("%s %s", (flag and "/sbin/ifup" or "/sbin/ifdown"), self.iface)
|
||||||
)
|
)
|
||||||
@@ -59,9 +65,15 @@ function Module:init(t)
|
|||||||
self.toggleFunc = self.toggleDevice
|
self.toggleFunc = self.toggleDevice
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if t.attempts ~= nil then
|
||||||
self.attempts = tonumber(t.attempts)
|
self.attempts = tonumber(t.attempts)
|
||||||
|
end
|
||||||
|
if t.dead_period ~= nil then
|
||||||
self.deadPeriod = tonumber(t.dead_period)
|
self.deadPeriod = tonumber(t.dead_period)
|
||||||
|
end
|
||||||
|
if t.restart_timeout ~= nil then
|
||||||
self.restartTimeout = tonumber(t.restart_timeout)
|
self.restartTimeout = tonumber(t.restart_timeout)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Module:run(currentStatus, lastStatus, timeDiff)
|
function Module:run(currentStatus, lastStatus, timeDiff)
|
||||||
@@ -20,7 +20,6 @@ local Module = {
|
|||||||
runInterval = 600,
|
runInterval = 600,
|
||||||
runIntervalFailed = 60,
|
runIntervalFailed = 60,
|
||||||
timeout = 3,
|
timeout = 3,
|
||||||
reqAttempts = 3,
|
|
||||||
providers = {
|
providers = {
|
||||||
opendns1 = {
|
opendns1 = {
|
||||||
name = "opendns1", host = "myip.opendns.com",
|
name = "opendns1", host = "myip.opendns.com",
|
||||||
@@ -333,13 +332,13 @@ function Module:resolveIP()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Module:init(t)
|
function Module:init(t)
|
||||||
if t.interval then
|
if t.interval ~= nil then
|
||||||
self.runInterval = tonumber(t.interval)
|
self.runInterval = tonumber(t.interval)
|
||||||
end
|
end
|
||||||
if t.timeout then
|
if t.timeout ~= nil then
|
||||||
self.timeout = tonumber(t.timeout)
|
self.timeout = tonumber(t.timeout)
|
||||||
end
|
end
|
||||||
if t.provider then
|
if t.provider ~= nil then
|
||||||
self._provider = self.providers[t.provider]
|
self._provider = self.providers[t.provider]
|
||||||
else
|
else
|
||||||
self._provider = self.providers.opendns1
|
self._provider = self.providers.opendns1
|
||||||
@@ -347,11 +346,13 @@ function Module:init(t)
|
|||||||
if self.config.configDir then
|
if self.config.configDir then
|
||||||
self.ipScript = string.format(
|
self.ipScript = string.format(
|
||||||
"%s/public-ip-script.%s", self.config.configDir, self.config.serviceConfig.instance)
|
"%s/public-ip-script.%s", self.config.configDir, self.config.serviceConfig.instance)
|
||||||
if t.enable_ip_script then
|
if t.enable_ip_script ~= nil then
|
||||||
self.enableIpScript = (tonumber(t.enable_ip_script) ~= 0)
|
self.enableIpScript = (tonumber(t.enable_ip_script) ~= 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if t.qtype ~= nil then
|
||||||
self._qtype = (tonumber(t.qtype) ~= 0)
|
self._qtype = (tonumber(t.qtype) ~= 0)
|
||||||
|
end
|
||||||
self._currentIp = nil
|
self._currentIp = nil
|
||||||
self._DNSPacket = nil
|
self._DNSPacket = nil
|
||||||
self._interval = self.runInterval
|
self._interval = self.runInterval
|
||||||
@@ -8,8 +8,8 @@ local Module = {
|
|||||||
syslog = function(level, msg) return true end,
|
syslog = function(level, msg) return true end,
|
||||||
writeValue = function(filePath, str) return false end,
|
writeValue = function(filePath, str) return false end,
|
||||||
readValue = function(filePath) return nil end,
|
readValue = function(filePath) return nil end,
|
||||||
deadPeriod = 0,
|
deadPeriod = 3600,
|
||||||
forceRebootDelay = 0,
|
forceRebootDelay = 300,
|
||||||
status = nil,
|
status = nil,
|
||||||
_deadCounter = 0,
|
_deadCounter = 0,
|
||||||
}
|
}
|
||||||
@@ -26,8 +26,12 @@ function Module:rebootDevice()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Module:init(t)
|
function Module:init(t)
|
||||||
|
if t.dead_period ~= nil then
|
||||||
self.deadPeriod = tonumber(t.dead_period)
|
self.deadPeriod = tonumber(t.dead_period)
|
||||||
|
end
|
||||||
|
if t.force_reboot_delay ~= nil then
|
||||||
self.forceRebootDelay = tonumber(t.force_reboot_delay)
|
self.forceRebootDelay = tonumber(t.force_reboot_delay)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Module:run(currentStatus, lastStatus, timeDiff)
|
function Module:run(currentStatus, lastStatus, timeDiff)
|
||||||
@@ -3,7 +3,7 @@ local unistd = require("posix.unistd")
|
|||||||
|
|
||||||
local Module = {
|
local Module = {
|
||||||
name = "mod_user_scripts",
|
name = "mod_user_scripts",
|
||||||
runPrio = 70,
|
runPrio = 80,
|
||||||
config = {},
|
config = {},
|
||||||
syslog = function(level, msg) return true end,
|
syslog = function(level, msg) return true end,
|
||||||
writeValue = function(filePath, str) return false end,
|
writeValue = function(filePath, str) return false end,
|
||||||
@@ -26,8 +26,12 @@ function Module:runExternalScript(scriptPath)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Module:init(t)
|
function Module:init(t)
|
||||||
|
if t.dead_period ~= nil then
|
||||||
self.deadPeriod = tonumber(t.dead_period)
|
self.deadPeriod = tonumber(t.dead_period)
|
||||||
|
end
|
||||||
|
if t.alive_period ~= nil then
|
||||||
self.alivePeriod = tonumber(t.alive_period)
|
self.alivePeriod = tonumber(t.alive_period)
|
||||||
|
end
|
||||||
if self.config.configDir then
|
if self.config.configDir then
|
||||||
self.upScript = string.format(
|
self.upScript = string.format(
|
||||||
"%s/up-script.%s", self.config.configDir, self.config.serviceConfig.instance)
|
"%s/up-script.%s", self.config.configDir, self.config.serviceConfig.instance)
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_VERSION:=1.2-0
|
PKG_VERSION:=1.3-0
|
||||||
LUCI_TITLE:=LuCI support for internet-detector
|
LUCI_TITLE:=LuCI support for internet-detector
|
||||||
LUCI_DEPENDS:=+internet-detector
|
LUCI_DEPENDS:=+internet-detector
|
||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
|
|||||||
@@ -129,10 +129,8 @@ var Timefield = ui.Textfield.extend({
|
|||||||
|
|
||||||
return view.extend({
|
return view.extend({
|
||||||
appName : 'internet-detector',
|
appName : 'internet-detector',
|
||||||
execPath : '/usr/bin/internet-detector',
|
|
||||||
configDir : '/etc/internet-detector',
|
configDir : '/etc/internet-detector',
|
||||||
ledsPath : '/sys/class/leds',
|
ledsPath : '/sys/class/leds',
|
||||||
mtaPath : '/usr/bin/mailsend',
|
|
||||||
pollInterval : L.env.pollinterval,
|
pollInterval : L.env.pollinterval,
|
||||||
appStatus : 'stoped',
|
appStatus : 'stoped',
|
||||||
initStatus : null,
|
initStatus : null,
|
||||||
@@ -144,7 +142,9 @@ return view.extend({
|
|||||||
defaultHosts : [ '8.8.8.8', '1.1.1.1' ],
|
defaultHosts : [ '8.8.8.8', '1.1.1.1' ],
|
||||||
leds : [],
|
leds : [],
|
||||||
mm : false,
|
mm : false,
|
||||||
mta : false,
|
mmInit : false,
|
||||||
|
email : false,
|
||||||
|
emailExec : false,
|
||||||
|
|
||||||
callInitStatus: rpc.declare({
|
callInitStatus: rpc.declare({
|
||||||
object: 'luci',
|
object: 'luci',
|
||||||
@@ -185,6 +185,18 @@ return view.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
callInit: rpc.declare({
|
||||||
|
object: 'luci.internet-detector',
|
||||||
|
method: 'Init',
|
||||||
|
expect: { '': {} }
|
||||||
|
}),
|
||||||
|
|
||||||
|
getInit() {
|
||||||
|
return this.callInit().then(data => {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
callUIPoll: rpc.declare({
|
callUIPoll: rpc.declare({
|
||||||
object: 'luci.internet-detector',
|
object: 'luci.internet-detector',
|
||||||
method: 'UIPoll',
|
method: 'UIPoll',
|
||||||
@@ -197,6 +209,30 @@ return view.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
callStatus: rpc.declare({
|
||||||
|
object: 'luci.internet-detector',
|
||||||
|
method: 'Status',
|
||||||
|
expect: { '': {} }
|
||||||
|
}),
|
||||||
|
|
||||||
|
getStatus() {
|
||||||
|
return this.callStatus().then(data => {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
callInetStatus: rpc.declare({
|
||||||
|
object: 'luci.internet-detector',
|
||||||
|
method: 'InetStatus',
|
||||||
|
expect: { '': {} }
|
||||||
|
}),
|
||||||
|
|
||||||
|
getInetStatus() {
|
||||||
|
return this.callInetStatus().then(data => {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
setInternetStatus() {
|
setInternetStatus() {
|
||||||
this.inetStatusArea.innerHTML = '';
|
this.inetStatusArea.innerHTML = '';
|
||||||
|
|
||||||
@@ -240,25 +276,13 @@ return view.extend({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
inetStatusFromJson(res) {
|
|
||||||
let inetStatData = null;
|
|
||||||
if(res.code === 0) {
|
|
||||||
try {
|
|
||||||
inetStatData = JSON.parse(res.stdout.trim());
|
|
||||||
} catch(e) {};
|
|
||||||
};
|
|
||||||
return inetStatData;
|
|
||||||
},
|
|
||||||
|
|
||||||
servicePoll() {
|
servicePoll() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
fs.exec(this.execPath, [ 'status' ]),
|
this.getStatus(),
|
||||||
fs.exec(this.execPath, [ 'inet-status' ]),
|
this.getInetStatus(),
|
||||||
]).then(stat => {
|
]).then(stat => {
|
||||||
let curAppStatus = (stat[0].code === 0) ? stat[0].stdout.trim() : null;
|
this.appStatus = stat[0].status;
|
||||||
let inetStatData = this.inetStatusFromJson(stat[1]);
|
this.inetStatus = stat[1];
|
||||||
this.appStatus = curAppStatus;
|
|
||||||
this.inetStatus = inetStatData;
|
|
||||||
this.setInternetStatus();
|
this.setInternetStatus();
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
this.appStatus = 'stoped';
|
this.appStatus = 'stoped';
|
||||||
@@ -467,11 +491,10 @@ return view.extend({
|
|||||||
|
|
||||||
load() {
|
load() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
fs.exec(this.execPath, [ 'status' ]),
|
this.getStatus(),
|
||||||
this.getInitStatus(),
|
this.getInitStatus(),
|
||||||
L.resolveDefault(fs.list(this.ledsPath), []),
|
L.resolveDefault(fs.list(this.ledsPath), []),
|
||||||
this.callInitStatus('modemmanager'),
|
this.getInit(),
|
||||||
L.resolveDefault(fs.stat(this.mtaPath), null),
|
|
||||||
uci.load(this.appName),
|
uci.load(this.appName),
|
||||||
]).catch(e => {
|
]).catch(e => {
|
||||||
ui.addNotification(null, E('p', _('An error has occurred') + ': %s'.format(e.message)));
|
ui.addNotification(null, E('p', _('An error has occurred') + ': %s'.format(e.message)));
|
||||||
@@ -485,11 +508,19 @@ return view.extend({
|
|||||||
this.appStatus = (data[0].code === 0) ? data[0].stdout.trim() : null;
|
this.appStatus = (data[0].code === 0) ? data[0].stdout.trim() : null;
|
||||||
this.initStatus = data[1];
|
this.initStatus = data[1];
|
||||||
this.leds = data[2];
|
this.leds = data[2];
|
||||||
if(data[3].modemmanager) {
|
if(data[3]) {
|
||||||
|
if(data[3].mm_mod) {
|
||||||
this.mm = true;
|
this.mm = true;
|
||||||
};
|
};
|
||||||
if(data[4]) {
|
if(data[3].mm_init) {
|
||||||
this.mta = true;
|
this.mmInit = true;
|
||||||
|
};
|
||||||
|
if(data[3].email_mod) {
|
||||||
|
this.email = true;
|
||||||
|
};
|
||||||
|
if(data[3].email_exec) {
|
||||||
|
this.emailExec = true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
this.currentAppMode = uci.get(this.appName, 'config', 'mode');
|
this.currentAppMode = uci.get(this.appName, 'config', 'mode');
|
||||||
|
|
||||||
@@ -539,6 +570,7 @@ return view.extend({
|
|||||||
_('Service: detector always runs as a system service.'),
|
_('Service: detector always runs as a system service.'),
|
||||||
_('Web UI only: detector works only when the Web UI is open (UI detector).')
|
_('Web UI only: detector works only when the Web UI is open (UI detector).')
|
||||||
);
|
);
|
||||||
|
mode.default = '0';
|
||||||
|
|
||||||
|
|
||||||
/* Service instances configuration */
|
/* Service instances configuration */
|
||||||
@@ -551,6 +583,7 @@ return view.extend({
|
|||||||
_('Write messages to the system log.')
|
_('Write messages to the system log.')
|
||||||
);
|
);
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
|
o.default = '1';
|
||||||
};
|
};
|
||||||
|
|
||||||
s = m.section(form.GridSection, 'instance');
|
s = m.section(form.GridSection, 'instance');
|
||||||
@@ -591,7 +624,6 @@ return view.extend({
|
|||||||
'hosts', _('Hosts'),
|
'hosts', _('Hosts'),
|
||||||
_('Hosts to check Internet availability. Hosts are polled (in list order) until at least one of them responds.')
|
_('Hosts to check Internet availability. Hosts are polled (in list order) until at least one of them responds.')
|
||||||
);
|
);
|
||||||
//o.datatype = 'or(host,hostport)';
|
|
||||||
o.datatype = 'or(or(host,hostport),ipaddrport(1))';
|
o.datatype = 'or(or(host,hostport),ipaddrport(1))';
|
||||||
o.default = this.defaultHosts;
|
o.default = this.defaultHosts;
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -692,13 +724,17 @@ return view.extend({
|
|||||||
s.tab('led_control', _('LED control'));
|
s.tab('led_control', _('LED control'));
|
||||||
s.tab('reboot_device', _('Reboot device'));
|
s.tab('reboot_device', _('Reboot device'));
|
||||||
s.tab('restart_network', _('Restart network'));
|
s.tab('restart_network', _('Restart network'));
|
||||||
|
if(this.mm) {
|
||||||
s.tab('restart_modem', _('Restart modem'));
|
s.tab('restart_modem', _('Restart modem'));
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
s.tab('public_ip', _('Public IP address'));
|
s.tab('public_ip', _('Public IP address'));
|
||||||
|
|
||||||
if(this.currentAppMode !== '2') {
|
if(this.currentAppMode !== '2') {
|
||||||
|
if(this.email) {
|
||||||
s.tab('email', _('Email notification'));
|
s.tab('email', _('Email notification'));
|
||||||
|
};
|
||||||
s.tab('user_scripts', _('User scripts'));
|
s.tab('user_scripts', _('User scripts'));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -777,7 +813,7 @@ return view.extend({
|
|||||||
// dead_period
|
// dead_period
|
||||||
o = s.taboption('reboot_device', this.CBITimeInput,
|
o = s.taboption('reboot_device', this.CBITimeInput,
|
||||||
'mod_reboot_dead_period', _('Dead period'),
|
'mod_reboot_dead_period', _('Dead period'),
|
||||||
_('Longest period of time without Internet access until the device is rebooted.')
|
_('Period of time without Internet access until the device is rebooted.')
|
||||||
);
|
);
|
||||||
o.default = '3600';
|
o.default = '3600';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -816,7 +852,7 @@ return view.extend({
|
|||||||
// dead_period
|
// dead_period
|
||||||
o = s.taboption('restart_network', this.CBITimeInput,
|
o = s.taboption('restart_network', this.CBITimeInput,
|
||||||
'mod_network_restart_dead_period', _('Dead period'),
|
'mod_network_restart_dead_period', _('Dead period'),
|
||||||
_('Longest period of time without Internet access before network restart.')
|
_('Period of time without Internet access before network restart.')
|
||||||
);
|
);
|
||||||
o.default = '900';
|
o.default = '900';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -863,6 +899,8 @@ return view.extend({
|
|||||||
|
|
||||||
// Restart modem
|
// Restart modem
|
||||||
|
|
||||||
|
if(this.mm) {
|
||||||
|
if(this.mmInit) {
|
||||||
o = s.taboption('restart_modem', form.DummyValue, '_dummy');
|
o = s.taboption('restart_modem', form.DummyValue, '_dummy');
|
||||||
o.rawhtml = true;
|
o.rawhtml = true;
|
||||||
o.default = '<div class="cbi-section-descr">' +
|
o.default = '<div class="cbi-section-descr">' +
|
||||||
@@ -870,8 +908,6 @@ return view.extend({
|
|||||||
'</div>';
|
'</div>';
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
if(this.mm) {
|
|
||||||
|
|
||||||
// enabled
|
// enabled
|
||||||
o = s.taboption('restart_modem', form.Flag, 'mod_modem_restart_enabled',
|
o = s.taboption('restart_modem', form.Flag, 'mod_modem_restart_enabled',
|
||||||
_('Enabled'),
|
_('Enabled'),
|
||||||
@@ -882,7 +918,7 @@ return view.extend({
|
|||||||
// dead_period
|
// dead_period
|
||||||
o = s.taboption('restart_modem', this.CBITimeInput,
|
o = s.taboption('restart_modem', this.CBITimeInput,
|
||||||
'mod_modem_restart_dead_period', _('Dead period'),
|
'mod_modem_restart_dead_period', _('Dead period'),
|
||||||
_('Longest period of time without Internet access before modem restart.')
|
_('Period of time without Internet access before modem restart.')
|
||||||
);
|
);
|
||||||
o.default = '600';
|
o.default = '600';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -904,7 +940,6 @@ return view.extend({
|
|||||||
o.multiple = false;
|
o.multiple = false;
|
||||||
o.nocreate = true;
|
o.nocreate = true;
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
o = s.taboption('restart_modem', form.DummyValue, '_dummy');
|
o = s.taboption('restart_modem', form.DummyValue, '_dummy');
|
||||||
o.rawhtml = true;
|
o.rawhtml = true;
|
||||||
@@ -913,7 +948,7 @@ return view.extend({
|
|||||||
'</em></div>';
|
'</em></div>';
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
};
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public IP address
|
// Public IP address
|
||||||
@@ -996,31 +1031,54 @@ return view.extend({
|
|||||||
);
|
);
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
|
|
||||||
// Email notification
|
// Email notification
|
||||||
|
|
||||||
|
if(this.email) {
|
||||||
|
if(this.emailExec) {
|
||||||
o = s.taboption('email', form.DummyValue, '_dummy');
|
o = s.taboption('email', form.DummyValue, '_dummy');
|
||||||
o.rawhtml = true;
|
o.rawhtml = true;
|
||||||
o.default = '<div class="cbi-section-descr">' +
|
o.default = '<div class="cbi-section-descr">' +
|
||||||
_('An email will be sent when the internet connection is restored after being disconnected.') +
|
_('An email will be sent when connected or disconnected from the Internet.') +
|
||||||
'</div>';
|
'</div>';
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
if(this.mta) {
|
|
||||||
|
|
||||||
// enabled
|
// enabled
|
||||||
o = s.taboption('email', form.Flag, 'mod_email_enabled',
|
o = s.taboption('email', form.Flag, 'mod_email_enabled',
|
||||||
_('Enabled'));
|
_('Enabled'));
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
|
// mode
|
||||||
|
o = s.taboption('email', form.ListValue,
|
||||||
|
'mod_email_mode', _('When email will be sent')
|
||||||
|
);
|
||||||
|
o.modalonly = true;
|
||||||
|
o.value(0, _('after connection'));
|
||||||
|
o.value(1, _('after disconnection'));
|
||||||
|
o.value(2, _('after connection or disconnection'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
// alive_period
|
// alive_period
|
||||||
o = s.taboption('email', this.CBITimeInput,
|
o = s.taboption('email', this.CBITimeInput,
|
||||||
'mod_email_alive_period', _('Alive period'),
|
'mod_email_alive_period', _('Alive period'),
|
||||||
_('Longest period of time after connecting to the Internet before sending a message.')
|
_('Period of time after connecting to the Internet before sending a message.')
|
||||||
);
|
);
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
o.depends({ 'mod_email_mode': '0' });
|
||||||
|
o.depends({ 'mod_email_mode': '2' });
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
// dead_period
|
||||||
|
o = s.taboption('email', this.CBITimeInput,
|
||||||
|
'mod_email_dead_period', _('Dead period'),
|
||||||
|
_('Period of time after disconnecting from Internet before sending a message.')
|
||||||
|
);
|
||||||
|
o.rmempty = false;
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends({ 'mod_email_mode': '1' });
|
||||||
|
o.depends({ 'mod_email_mode': '2' });
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
// host_alias
|
// host_alias
|
||||||
o = s.taboption('email', form.Value, 'mod_email_host_alias',
|
o = s.taboption('email', form.Value, 'mod_email_host_alias',
|
||||||
@@ -1058,14 +1116,12 @@ return view.extend({
|
|||||||
'mod_email_mail_smtp', _('SMTP server'));
|
'mod_email_mail_smtp', _('SMTP server'));
|
||||||
o.description = _('Hostname/IP address of the SMTP server.');
|
o.description = _('Hostname/IP address of the SMTP server.');
|
||||||
o.datatype = 'host';
|
o.datatype = 'host';
|
||||||
o.default = 'smtp.gmail.com';
|
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
// mail_smtp_port
|
// mail_smtp_port
|
||||||
o = s.taboption('email', form.Value,
|
o = s.taboption('email', form.Value,
|
||||||
'mod_email_mail_smtp_port', _('SMTP server port'));
|
'mod_email_mail_smtp_port', _('SMTP server port'));
|
||||||
o.datatype = 'port';
|
o.datatype = 'port';
|
||||||
o.default = '587';
|
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
// mail_security
|
// mail_security
|
||||||
@@ -1079,7 +1135,6 @@ return view.extend({
|
|||||||
o.value('ssl', 'SSL');
|
o.value('ssl', 'SSL');
|
||||||
o.default = 'tls';
|
o.default = 'tls';
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
o = s.taboption('email', form.DummyValue, '_dummy');
|
o = s.taboption('email', form.DummyValue, '_dummy');
|
||||||
o.rawhtml = true;
|
o.rawhtml = true;
|
||||||
@@ -1088,6 +1143,7 @@ return view.extend({
|
|||||||
'</em></div>';
|
'</em></div>';
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// User scripts
|
// User scripts
|
||||||
|
|
||||||
@@ -1116,7 +1172,7 @@ return view.extend({
|
|||||||
// alive_period
|
// alive_period
|
||||||
o = s.taboption('user_scripts', this.CBITimeInput,
|
o = s.taboption('user_scripts', this.CBITimeInput,
|
||||||
'mod_user_scripts_alive_period', _('Alive period'),
|
'mod_user_scripts_alive_period', _('Alive period'),
|
||||||
_('Longest period of time after connecting to Internet before "up-script" runs.')
|
_('Period of time after connecting to Internet before "up-script" runs.')
|
||||||
);
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -1134,7 +1190,7 @@ return view.extend({
|
|||||||
// dead_period
|
// dead_period
|
||||||
o = s.taboption('user_scripts', this.CBITimeInput,
|
o = s.taboption('user_scripts', this.CBITimeInput,
|
||||||
'mod_user_scripts_dead_period', _('Dead period'),
|
'mod_user_scripts_dead_period', _('Dead period'),
|
||||||
_('Longest period of time after disconnecting from Internet before "down-script" runs.')
|
_('Period of time after disconnecting from Internet before "down-script" runs.')
|
||||||
);
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -1156,7 +1212,6 @@ return view.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleSaveApply(ev, mode) {
|
handleSaveApply(ev, mode) {
|
||||||
poll.stop();
|
|
||||||
return this.handleSave(ev).then(() => {
|
return this.handleSave(ev).then(() => {
|
||||||
ui.changes.apply(mode == '0');
|
ui.changes.apply(mode == '0');
|
||||||
window.setTimeout(() => this.serviceRestart(), 3000);
|
window.setTimeout(() => this.serviceRestart(), 3000);
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ document.head.append(E('style', {'type': 'text/css'},
|
|||||||
return baseclass.extend({
|
return baseclass.extend({
|
||||||
title : _('Internet'),
|
title : _('Internet'),
|
||||||
appName : 'internet-detector',
|
appName : 'internet-detector',
|
||||||
execPath : '/usr/bin/internet-detector',
|
|
||||||
currentAppMode : null,
|
currentAppMode : null,
|
||||||
inetStatus : null,
|
inetStatus : null,
|
||||||
|
|
||||||
@@ -73,14 +72,16 @@ return baseclass.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
inetStatusFromJson(res) {
|
callInetStatus: rpc.declare({
|
||||||
let inetStatData = null;
|
object: 'luci.internet-detector',
|
||||||
if(res.code === 0) {
|
method: 'InetStatus',
|
||||||
try {
|
expect: { '': {} }
|
||||||
inetStatData = JSON.parse(res.stdout.trim());
|
}),
|
||||||
} catch(e) {};
|
|
||||||
};
|
getInetStatus() {
|
||||||
return inetStatData;
|
return this.callInetStatus().then(data => {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
@@ -94,7 +95,7 @@ return baseclass.extend({
|
|||||||
return this.getUIPoll();
|
return this.getUIPoll();
|
||||||
}
|
}
|
||||||
else if(this.currentAppMode === '1') {
|
else if(this.currentAppMode === '1') {
|
||||||
return L.resolveDefault(fs.exec(this.execPath, [ 'inet-status' ]), null);
|
return L.resolveDefault(this.getInetStatus(), null);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -102,9 +103,7 @@ return baseclass.extend({
|
|||||||
if(this.currentAppMode === '0') {
|
if(this.currentAppMode === '0') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if(this.currentAppMode === '1' && data) {
|
|
||||||
data = this.inetStatusFromJson(data);
|
|
||||||
};
|
|
||||||
this.inetStatus = data;
|
this.inetStatus = data;
|
||||||
|
|
||||||
let inetStatusArea = E('div', {});
|
let inetStatusArea = E('div', {});
|
||||||
|
|||||||
@@ -38,10 +38,8 @@ msgstr "Интервал при подключении"
|
|||||||
msgid "Alive period"
|
msgid "Alive period"
|
||||||
msgstr "Период после подключения"
|
msgstr "Период после подключения"
|
||||||
|
|
||||||
msgid ""
|
msgid "An email will be sent when connected or disconnected from the Internet."
|
||||||
"An email will be sent when the internet connection is restored after being "
|
msgstr "Сообщение будет отправлено при подключении или отключении от Интернет."
|
||||||
"disconnected."
|
|
||||||
msgstr "Сообщение будет отправлено при восстановлении соединения с Интернет после отключения."
|
|
||||||
|
|
||||||
msgid "An error has occurred"
|
msgid "An error has occurred"
|
||||||
msgstr "Произошла ошибка"
|
msgstr "Произошла ошибка"
|
||||||
@@ -221,41 +219,8 @@ msgstr "Управление LED"
|
|||||||
msgid "Loading"
|
msgid "Loading"
|
||||||
msgstr "Загрузка"
|
msgstr "Загрузка"
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Longest period of time after connecting to Internet before \"up-script\" "
|
|
||||||
"runs."
|
|
||||||
msgstr ""
|
|
||||||
"Максимальный промежуток времени после подключения к Интернет перед запуском "
|
|
||||||
"\"up-script\"."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Longest period of time after connecting to the Internet before sending a "
|
|
||||||
"message."
|
|
||||||
msgstr "Максимальный промежуток времени после подключения Интернет перед отправкой сообщения."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Longest period of time after disconnecting from Internet before \"down-script"
|
|
||||||
"\" runs."
|
|
||||||
msgstr ""
|
|
||||||
"Максимальный промежуток времени после отключения Интернет перед запуском "
|
|
||||||
"\"down-script\"."
|
|
||||||
|
|
||||||
msgid "Longest period of time without Internet access before modem restart."
|
|
||||||
msgstr ""
|
|
||||||
"Максимальное время отсутствия доступа в Интренет перед перезапуском модема."
|
|
||||||
|
|
||||||
msgid "Longest period of time without Internet access before network restart."
|
|
||||||
msgstr ""
|
|
||||||
"Максимальное время отсутствия доступа в Интренет перед перезапуском сети."
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Longest period of time without Internet access until the device is rebooted."
|
|
||||||
msgstr ""
|
|
||||||
"Максимальное время отсутствия доступа в Интренет перед перезагрузкой "
|
|
||||||
"устройства."
|
|
||||||
|
|
||||||
msgid "Mailsend is not available..."
|
msgid "Mailsend is not available..."
|
||||||
msgstr "Mailsend недоступен..."
|
msgstr "Mailsend не доступен..."
|
||||||
|
|
||||||
msgid "Main settings"
|
msgid "Main settings"
|
||||||
msgstr "Основные настройки"
|
msgstr "Основные настройки"
|
||||||
@@ -273,12 +238,12 @@ msgstr ""
|
|||||||
msgid "Maximum timeout for waiting for a response from the host."
|
msgid "Maximum timeout for waiting for a response from the host."
|
||||||
msgstr "Максимальный таймаут ожидания ответа от хоста."
|
msgstr "Максимальный таймаут ожидания ответа от хоста."
|
||||||
|
|
||||||
|
msgid "ModemManager is not available..."
|
||||||
|
msgstr "ModemManager не доступен..."
|
||||||
|
|
||||||
msgid "Modem will be restarted when the Internet is disconnected."
|
msgid "Modem will be restarted when the Internet is disconnected."
|
||||||
msgstr "Модем будет перезапущен при отключении Интернет."
|
msgstr "Модем будет перезапущен при отключении Интернет."
|
||||||
|
|
||||||
msgid "ModemManager is not available..."
|
|
||||||
msgstr "ModemManager недоступен..."
|
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"ModemManger interface. If specified, it will be restarted after restarting "
|
"ModemManger interface. If specified, it will be restarted after restarting "
|
||||||
"ModemManager."
|
"ModemManager."
|
||||||
@@ -320,6 +285,35 @@ msgstr "Пароль"
|
|||||||
msgid "Password for SMTP authentication."
|
msgid "Password for SMTP authentication."
|
||||||
msgstr "Пароль для SMTP-аутентификации."
|
msgstr "Пароль для SMTP-аутентификации."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time after connecting to Internet before \"up-script\" runs."
|
||||||
|
msgstr ""
|
||||||
|
"Период времени после подключения к Интернет перед запуском \"up-script\"."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time after connecting to the Internet before sending a message."
|
||||||
|
msgstr "Период времени после подключения к Интернет перед отправкой сообщения."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time after disconnecting from Internet before \"down-script\" runs."
|
||||||
|
msgstr ""
|
||||||
|
"Период времени после отключения от Интернет перед запуском \"down-script\"."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time after disconnecting from Internet before sending a message."
|
||||||
|
msgstr "Период времени отсутствия доступа в Интренет перед отправкой сообщения."
|
||||||
|
|
||||||
|
msgid "Period of time without Internet access before modem restart."
|
||||||
|
msgstr "Период времени отсутствия доступа в Интренет перед перезапуском модема."
|
||||||
|
|
||||||
|
msgid "Period of time without Internet access before network restart."
|
||||||
|
msgstr "Период времени отсутствия доступа в Интренет перед перезапуском сети."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time without Internet access until the device is rebooted."
|
||||||
|
msgstr ""
|
||||||
|
"Период времени отсутствия доступа в Интренет перед перезагрузкой устройства."
|
||||||
|
|
||||||
msgid "Polling interval"
|
msgid "Polling interval"
|
||||||
msgstr "Интервал опроса"
|
msgstr "Интервал опроса"
|
||||||
|
|
||||||
@@ -468,12 +462,24 @@ msgstr ""
|
|||||||
"Только web-интерфейс: детектор работает только в web-интерфейсе (UI "
|
"Только web-интерфейс: детектор работает только в web-интерфейсе (UI "
|
||||||
"детектор)."
|
"детектор)."
|
||||||
|
|
||||||
|
msgid "When email will be sent"
|
||||||
|
msgstr "Когда будет отправлено сообщение"
|
||||||
|
|
||||||
msgid "Windows: 32 bytes"
|
msgid "Windows: 32 bytes"
|
||||||
msgstr "Windows: 32 байта"
|
msgstr "Windows: 32 байта"
|
||||||
|
|
||||||
msgid "Write messages to the system log."
|
msgid "Write messages to the system log."
|
||||||
msgstr "Записывать сообщения в системный журнал."
|
msgstr "Записывать сообщения в системный журнал."
|
||||||
|
|
||||||
|
msgid "after connection"
|
||||||
|
msgstr "после подключения"
|
||||||
|
|
||||||
|
msgid "after disconnection"
|
||||||
|
msgstr "после отключения"
|
||||||
|
|
||||||
|
msgid "after connection or disconnection"
|
||||||
|
msgstr "после подключения или отключения"
|
||||||
|
|
||||||
msgid "down-script"
|
msgid "down-script"
|
||||||
msgstr "down-script"
|
msgstr "down-script"
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ msgstr ""
|
|||||||
msgid "Alive period"
|
msgid "Alive period"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid "An email will be sent when connected or disconnected from the Internet."
|
||||||
"An email will be sent when the internet connection is restored after being "
|
|
||||||
"disconnected."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "An error has occurred"
|
msgid "An error has occurred"
|
||||||
@@ -207,31 +205,6 @@ msgstr ""
|
|||||||
msgid "Loading"
|
msgid "Loading"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Longest period of time after connecting to Internet before \"up-script\" "
|
|
||||||
"runs."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Longest period of time after connecting to the Internet before sending a "
|
|
||||||
"message."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Longest period of time after disconnecting from Internet before \"down-script"
|
|
||||||
"\" runs."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Longest period of time without Internet access before modem restart."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Longest period of time without Internet access before network restart."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Longest period of time without Internet access until the device is rebooted."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Mailsend is not available..."
|
msgid "Mailsend is not available..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -249,10 +222,10 @@ msgstr ""
|
|||||||
msgid "Maximum timeout for waiting for a response from the host."
|
msgid "Maximum timeout for waiting for a response from the host."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Modem will be restarted when the Internet is disconnected."
|
msgid "ModemManager is not available..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ModemManager is not available..."
|
msgid "Modem will be restarted when the Internet is disconnected."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
@@ -290,6 +263,32 @@ msgstr ""
|
|||||||
msgid "Password for SMTP authentication."
|
msgid "Password for SMTP authentication."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time after connecting to Internet before \"up-script\" runs."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time after connecting to the Internet before sending a message."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time after disconnecting from Internet before \"down-script\" runs."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time after disconnecting from Internet before sending a message."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Period of time without Internet access before modem restart."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Period of time without Internet access before network restart."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Period of time without Internet access until the device is rebooted."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Polling interval"
|
msgid "Polling interval"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -434,12 +433,24 @@ msgstr ""
|
|||||||
msgid "Web UI only: detector works only when the Web UI is open (UI detector)."
|
msgid "Web UI only: detector works only when the Web UI is open (UI detector)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "When email will be sent"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Windows: 32 bytes"
|
msgid "Windows: 32 bytes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Write messages to the system log."
|
msgid "Write messages to the system log."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "after connection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "after disconnection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "both"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "down-script"
|
msgid "down-script"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +1,86 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env lua
|
||||||
|
|
||||||
. /lib/functions.sh
|
local appName = "internet-detector"
|
||||||
. /usr/share/libubox/jshn.sh
|
local appExec = "/usr/bin/internet-detector"
|
||||||
|
local mailsendExec = "/usr/bin/mailsend"
|
||||||
|
local modemManagerInit = "/etc/init.d/modemmanager"
|
||||||
|
|
||||||
readonly ID_EXEC="/usr/bin/internet-detector"
|
local InternetDetector = require(appName .. ".main")
|
||||||
|
local uci = require("uci")
|
||||||
|
local unistd = require("posix.unistd")
|
||||||
|
|
||||||
run_instance() {
|
local function prequire(package)
|
||||||
config_get enabled "$1" enabled "0"
|
local retVal, pkg = pcall(require, package)
|
||||||
if [ $enabled = "1" ]; then
|
return retVal and pkg
|
||||||
$ID_EXEC service "$1" > /dev/null 2>&1
|
end
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
start_ui_instances() {
|
local function init()
|
||||||
config_load internet-detector
|
local lines = {}
|
||||||
config_get mode "config" mode "0"
|
if prequire(appName .. ".mod_modem_restart") then
|
||||||
if [ $mode = "2" ]; then
|
lines[#lines + 1] = '"mm_mod":true'
|
||||||
config_foreach run_instance "instance"
|
if (unistd.access(modemManagerInit, "x") and
|
||||||
fi
|
os.execute(modemManagerInit .. " enabled") == 0) then
|
||||||
}
|
lines[#lines + 1] = '"mm_init":true'
|
||||||
|
else
|
||||||
|
lines[#lines + 1] = '"mm_init":false'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
lines[#lines + 1] = '"mm_mod":false'
|
||||||
|
end
|
||||||
|
if prequire(appName .. ".mod_email") then
|
||||||
|
lines[#lines + 1] = '"email_mod":true'
|
||||||
|
if unistd.access(mailsendExec, "x") then
|
||||||
|
lines[#lines + 1] = '"email_exec":true'
|
||||||
|
else
|
||||||
|
lines[#lines + 1] = '"email_exec":false'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
lines[#lines + 1] = '"email_mod":false'
|
||||||
|
end
|
||||||
|
return string.format("{%s}", table.concat(lines, ","))
|
||||||
|
end
|
||||||
|
|
||||||
ui_poll() {
|
local function startUiInstances()
|
||||||
$ID_EXEC uipoll
|
local uciCursor = uci.cursor()
|
||||||
if [ $? -eq 126 ]; then
|
local mode = tonumber(uciCursor:get(appName, "config", "mode"))
|
||||||
start_ui_instances
|
if mode == 2 then
|
||||||
$ID_EXEC inet-status
|
uciCursor:foreach(
|
||||||
fi
|
appName,
|
||||||
}
|
"instance",
|
||||||
|
function(s)
|
||||||
|
if s.enabled == "1" then
|
||||||
|
os.execute(string.format("%s service %s", appExec, s[".name"]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
case "$1" in
|
local function uiPoll()
|
||||||
list)
|
if InternetDetector:status() == "stoped" then
|
||||||
json_init
|
startUiInstances()
|
||||||
json_add_object "UIPoll"
|
else
|
||||||
json_close_object
|
InternetDetector:setSIGUSR()
|
||||||
json_dump
|
end
|
||||||
json_cleanup
|
return InternetDetector:inetStatus()
|
||||||
;;
|
end
|
||||||
call)
|
|
||||||
case "$2" in
|
local function list()
|
||||||
UIPoll)
|
io.write('{"Init":{},"Status":{},"InetStatus":{},"UIPoll":{}}')
|
||||||
ui_poll
|
end
|
||||||
;;
|
|
||||||
esac
|
if arg[1] == "list" then
|
||||||
;;
|
list()
|
||||||
esac
|
elseif arg[1] == "call" then
|
||||||
|
if arg[2] == "Init" then
|
||||||
|
io.write(init())
|
||||||
|
elseif arg[2] == "Status" then
|
||||||
|
io.write(string.format('{"status":"%s"}', tostring(InternetDetector:status())))
|
||||||
|
elseif arg[2] == "InetStatus" then
|
||||||
|
io.write(InternetDetector:inetStatus())
|
||||||
|
elseif arg[2] == "UIPoll" then
|
||||||
|
io.write(uiPoll())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
os.exit(0)
|
||||||
|
|||||||
@@ -6,14 +6,12 @@
|
|||||||
"/sys/class/leds": [ "list" ],
|
"/sys/class/leds": [ "list" ],
|
||||||
"/etc/internet-detector/up-script*": [ "read" ],
|
"/etc/internet-detector/up-script*": [ "read" ],
|
||||||
"/etc/internet-detector/down-script*": [ "read" ],
|
"/etc/internet-detector/down-script*": [ "read" ],
|
||||||
"/etc/internet-detector/public-ip-script*": [ "read" ],
|
"/etc/internet-detector/public-ip-script*": [ "read" ]
|
||||||
"/usr/bin/internet-detector*": [ "exec" ],
|
|
||||||
"/usr/bin/mailsend": [ "exec" ]
|
|
||||||
},
|
},
|
||||||
"uci": [ "internet-detector" ],
|
"uci": [ "internet-detector" ],
|
||||||
"ubus": {
|
"ubus": {
|
||||||
"luci": [ "getInitList", "setInitAction" ],
|
"luci": [ "getInitList", "setInitAction" ],
|
||||||
"luci.internet-detector": [ "UIPoll" ]
|
"luci.internet-detector": [ "Init", "Status", "InetStatus", "UIPoll" ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"write": {
|
"write": {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 129 KiB |
BIN
screenshots/04.jpg
Normal file
BIN
screenshots/04.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
screenshots/05.jpg
Normal file
BIN
screenshots/05.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
Reference in New Issue
Block a user