mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-10-30 01:15:13 +08:00
update 2026-05-27 23:25:57
This commit is contained in:
1 parent
84f707b52b
commit
d0159b49a5
759 files changed
+362365
No files matched your search
@@ -0,0 +1,83 @@
|
||||
# SPDX-Identifier-License: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=UnblockNeteaseMusic-Go
|
||||
PKG_VERSION:=0.2.13
|
||||
PKG_RELEASE:=$(AUTORELEASE)
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/cnsilvan/UnblockNeteaseMusic/tar.gz/$(PKG_VERSION)?
|
||||
PKG_HASH:=92201b7f04ab1015c806c672b98a29b97d0f137d9b60e6d35d279c2064ed86a4
|
||||
|
||||
PKG_LICENSE:=GPL-3.0
|
||||
PKG_LICENSE_FILE:=LICENSE
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
CONFIG_UNBLOCKNETEASEMUSIC_GO_COMPRESS_GOPROXY \
|
||||
CONFIG_UNBLOCKNETEASEMUSIC_GO_COMPRESS_UPX
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(firstword $(subst -, ,$(PKG_NAME)))-$(PKG_VERSION)
|
||||
PKG_BUILD_DEPENDS:=golang/host upx/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
|
||||
GO_PKG:=github.com/cnsilvan/UnblockNeteaseMusic
|
||||
GO_PKG_BUILD_PKG:=$$(GO_PKG)
|
||||
GO_PKG_LDFLAGS:=-s -w
|
||||
COMPILE_TIME:= $(shell TZ=UTC-8 date '+%Y-%m-%d %H:%M:%S')
|
||||
GO_PKG_LDFLAGS+= \
|
||||
-X '$(GO_PKG)/version.Version=$(PKG_VERSION)' \
|
||||
-X '$(GO_PKG)/version.BuildTime=$(COMPILE_TIME)' \
|
||||
-X '$(GO_PKG)/version.ExGoVersionInfo=$(GO_ARM) $(GO_MIPS)$(GO_MIPS64)'
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../../lang/golang/golang-package.mk
|
||||
|
||||
define Package/UnblockNeteaseMusic-Go/config
|
||||
config UNBLOCKNETEASEMUSIC_GO_COMPRESS_GOPROXY
|
||||
bool "Compiling with GOPROXY proxy"
|
||||
default n
|
||||
|
||||
config UNBLOCKNETEASEMUSIC_GO_COMPRESS_UPX
|
||||
bool "Compress executable files with UPX"
|
||||
depends on !mips64
|
||||
default n
|
||||
endef
|
||||
|
||||
ifeq ($(CONFIG_UNBLOCKNETEASEMUSIC_GO_COMPRESS_GOPROXY),y)
|
||||
export GO111MODULE=on
|
||||
export GOPROXY=https://goproxy.io
|
||||
endif
|
||||
|
||||
define Package/UnblockNeteaseMusic-Go
|
||||
SECTION:=multimedia
|
||||
CATEGORY:=Multimedia
|
||||
TITLE:=Revive Netease Cloud Music (Golang)
|
||||
URL:=https://github.com/cnsilvan/UnblockNeteaseMusic
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS)
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call GoPackage/Build/Compile)
|
||||
ifeq ($(CONFIG_UNBLOCKNETEASEMUSIC_GO_COMPRESS_UPX),y)
|
||||
$(STAGING_DIR_HOST)/bin/upx --lzma --best $(GO_PKG_BUILD_BIN_DIR)/UnblockNeteaseMusic
|
||||
endif
|
||||
endef
|
||||
|
||||
define Package/UnblockNeteaseMusic-Go/install
|
||||
$(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR))
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/UnblockNeteaseMusic $(1)/usr/bin/UnblockNeteaseMusic
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/UnblockNeteaseMusicGo
|
||||
$(CP) ./files/* $(1)/usr/share/UnblockNeteaseMusicGo/
|
||||
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,UnblockNeteaseMusic-Go))
|
||||
$(eval $(call BuildPackage,UnblockNeteaseMusic-Go))
|
||||
@@ -0,0 +1,14 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICIjCCAaigAwIBAgIUTc9HQDej5hLCQ74u436a5yE4MDcwCgYIKoZIzj0EAwMw
|
||||
SDELMAkGA1UEBhMCQ04xJDAiBgNVBAMMG1VuYmxvY2tOZXRlYXNlTXVzaWMgUm9v
|
||||
dCBDQTETMBEGA1UECgwKMTcxNTE3MzMyOTAeFw0yMTA0MzAwNzIzMDJaFw0yNjA0
|
||||
MjkwNzIzMDJaMEgxCzAJBgNVBAYTAkNOMSQwIgYDVQQDDBtVbmJsb2NrTmV0ZWFz
|
||||
ZU11c2ljIFJvb3QgQ0ExEzARBgNVBAoMCjE3MTUxNzMzMjkwdjAQBgcqhkjOPQIB
|
||||
BgUrgQQAIgNiAASIyI7lYgGlq49qWtY1O2/XNDeowYf7W/Z+l7C14bphxAJ9jSDo
|
||||
tLwbFPWy5VPENc0rB0/yeHA2z7LU67POL2gGgp+17y7scLkkBk3Q7wRMETrtP44Z
|
||||
ITBstZ0wzVyyQEKjUzBRMB0GA1UdDgQWBBQ2F7+t8cPHJaWuCD8RHTSdLugKYzAf
|
||||
BgNVHSMEGDAWgBQ2F7+t8cPHJaWuCD8RHTSdLugKYzAPBgNVHRMBAf8EBTADAQH/
|
||||
MAoGCCqGSM49BAMDA2gAMGUCMQDqaRX2e01e0U+f0As/KUKDhmG5ElkK5CjYK9jk
|
||||
kXLNGFXJLGta6CDvjtMLBPc20qkCMBvDs+JnJKVBEJNZVsRBBs+v2YxNU/u2aYJa
|
||||
dMwXuFveSDWOS7mBeRztX/geEggiSw==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,15 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICRDCCAcqgAwIBAgIUeVqRrT2mHG5Mc8JD+ErphiAmlgkwCgYIKoZIzj0EAwMw
|
||||
SDELMAkGA1UEBhMCQ04xJDAiBgNVBAMMG1VuYmxvY2tOZXRlYXNlTXVzaWMgUm9v
|
||||
dCBDQTETMBEGA1UECgwKMTcxNTE3MzMyOTAeFw0yMTA0MzAwNzIzMDJaFw0yMjA0
|
||||
MzAwNzIzMDJaMHsxCzAJBgNVBAYTAkNOMREwDwYDVQQHDAhIYW5nemhvdTEsMCoG
|
||||
A1UECgwjTmV0RWFzZSAoSGFuZ3pob3UpIE5ldHdvcmsgQ28uLCBMdGQxETAPBgNV
|
||||
BAsMCElUIERlcHQuMRgwFgYDVQQDDA8qLm11c2ljLjE2My5jb20wdjAQBgcqhkjO
|
||||
PQIBBgUrgQQAIgNiAAQTPyU9RQ1pAFMLmozi+c4pEC1rrxAlPGwO9Em+qV+a5qLW
|
||||
gQjjsJeabMqJ/UQ7hDtdKVxWuXiAjMiDcXwL63I71MZKPTAEKXdCmNQwb4kXvRUn
|
||||
oOR4r7BMxEpGlf0CULWjQjBAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMCkGA1UdEQQi
|
||||
MCCCDW11c2ljLjE2My5jb22CDyoubXVzaWMuMTYzLmNvbTAKBggqhkjOPQQDAwNo
|
||||
ADBlAjEAs5bdgnNP/DiK919RiWscC0kyuY0ugG1C8m8F2Yod4MI3oTyrkVcag21o
|
||||
NSzm802uAjBoPuKEbjjFP4ics0BQdICiVd6WCVAsE69FnlmqRteAJqxvdKGpVLi+
|
||||
Qi3arfomrrc=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,9 @@
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BgUrgQQAIg==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIGkAgEBBDBfW3twxGaQmMzP9p0/UU5EvHFVCbBw4piVFJ+pm/uFY6CKZkC5LGMa
|
||||
Uc9vn/KiewGgBwYFK4EEACKhZANiAAQTPyU9RQ1pAFMLmozi+c4pEC1rrxAlPGwO
|
||||
9Em+qV+a5qLWgQjjsJeabMqJ/UQ7hDtdKVxWuXiAjMiDcXwL63I71MZKPTAEKXdC
|
||||
mNQwb4kXvRUnoOR4r7BMxEpGlf0CULU=
|
||||
-----END EC PRIVATE KEY-----
|
||||
@@ -0,0 +1,144 @@
|
||||
From f4f5d11b578a1ab2c3d089bbe5453052b43892bb Mon Sep 17 00:00:00 2001
|
||||
From: tofuliang <tofuliang@gmail.com>
|
||||
Date: Mon, 24 Jan 2022 18:53:11 +0800
|
||||
Subject: [PATCH] fix block ad,add web traffic logs
|
||||
|
||||
---
|
||||
app.go | 1 +
|
||||
config/config.go | 1 +
|
||||
processor/processor.go | 55 +++++++++++++++++++++++++++++++++---------
|
||||
3 files changed, 45 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/app.go b/app.go
|
||||
index 73a6070..1018d75 100644
|
||||
--- a/app.go
|
||||
+++ b/app.go
|
||||
@@ -45,6 +45,7 @@ func main() {
|
||||
log.Println("EnableLocalVip=", *config.EnableLocalVip)
|
||||
log.Println("UnlockSoundEffects=", *config.UnlockSoundEffects)
|
||||
log.Println("QQCookieFile=", *config.QQCookieFile)
|
||||
+ log.Println("LogWebTraffic=", *config.LogWebTraffic)
|
||||
if host.InitHosts() == nil {
|
||||
//go func() {
|
||||
// // // terminal: $ go tool pprof -http=:8081 http://localhost:6060/debug/pprof/heap
|
||||
diff --git a/config/config.go b/config/config.go
|
||||
index 6c07873..a653cdf 100644
|
||||
--- a/config/config.go
|
||||
+++ b/config/config.go
|
||||
@@ -31,6 +31,7 @@ var (
|
||||
EnableLocalVip = flag.Bool("lv", false, "enable local vip")
|
||||
UnlockSoundEffects = flag.Bool("sef", false, "unlock SoundEffects")
|
||||
QQCookieFile = flag.String("qc", "./qq.cookie", "specify cookies file ,such as : \"qq.cookie\"")
|
||||
+ LogWebTraffic = flag.Bool("wl", false, "log request url and response")
|
||||
)
|
||||
|
||||
func ValidParams() bool {
|
||||
diff --git a/processor/processor.go b/processor/processor.go
|
||||
index 8d09dbf..d07b9d3 100644
|
||||
--- a/processor/processor.go
|
||||
+++ b/processor/processor.go
|
||||
@@ -6,14 +6,6 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
- "github.com/cnsilvan/UnblockNeteaseMusic/cache"
|
||||
- "github.com/cnsilvan/UnblockNeteaseMusic/common"
|
||||
- "github.com/cnsilvan/UnblockNeteaseMusic/config"
|
||||
- "github.com/cnsilvan/UnblockNeteaseMusic/network"
|
||||
- "github.com/cnsilvan/UnblockNeteaseMusic/processor/crypto"
|
||||
- "github.com/cnsilvan/UnblockNeteaseMusic/provider"
|
||||
- "github.com/cnsilvan/UnblockNeteaseMusic/utils"
|
||||
- "golang.org/x/text/width"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -22,6 +14,15 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
+
|
||||
+ "github.com/cnsilvan/UnblockNeteaseMusic/cache"
|
||||
+ "github.com/cnsilvan/UnblockNeteaseMusic/common"
|
||||
+ "github.com/cnsilvan/UnblockNeteaseMusic/config"
|
||||
+ "github.com/cnsilvan/UnblockNeteaseMusic/network"
|
||||
+ "github.com/cnsilvan/UnblockNeteaseMusic/processor/crypto"
|
||||
+ "github.com/cnsilvan/UnblockNeteaseMusic/provider"
|
||||
+ "github.com/cnsilvan/UnblockNeteaseMusic/utils"
|
||||
+ "golang.org/x/text/width"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -188,6 +189,9 @@ func RequestAfter(request *http.Request, response *http.Response, netease *Netea
|
||||
if ok {
|
||||
code = codeN.String()
|
||||
}
|
||||
+
|
||||
+ logResponse(netease)
|
||||
+
|
||||
if strings.EqualFold(netease.Path, "/api/osx/version") {
|
||||
modified = disableUpdate(netease)
|
||||
} else if strings.Contains(netease.Path, "/usertool/sound/") {
|
||||
@@ -197,9 +201,24 @@ func RequestAfter(request *http.Request, response *http.Response, netease *Netea
|
||||
for key, resp := range netease.JsonBody {
|
||||
if strings.Contains(key, "/usertool/sound/") {
|
||||
modified = unblockSoundEffects(resp.(map[string]interface{}))
|
||||
- } else if *config.BlockAds && strings.Contains(netease.Path, "api/ad/") {
|
||||
+ } else if *config.BlockAds && strings.Contains(key, "api/ad/") {
|
||||
+ log.Println("block Ad has been triggered(" + key + ").")
|
||||
resp = &common.MapType{}
|
||||
modified = true
|
||||
+ } else if *config.BlockAds && strings.EqualFold(key, "/api/v2/banner/get") {
|
||||
+ newInfo := make(common.SliceType, 0)
|
||||
+ info := netease.JsonBody[key]
|
||||
+ for _, data := range info.(common.MapType)["banners"].(common.SliceType) {
|
||||
+ if banner, ok := data.(common.MapType); ok {
|
||||
+ if banner["adid"] == nil {
|
||||
+ newInfo = append(newInfo, banner)
|
||||
+ } else {
|
||||
+ log.Println("block banner Ad has been triggered.")
|
||||
+ modified = true
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ info.(common.MapType)["banners"] = newInfo
|
||||
}
|
||||
}
|
||||
} else if !netease.Web && (code == "401" || code == "512") && strings.Contains(netease.Path, "manipulate") {
|
||||
@@ -220,7 +239,9 @@ func RequestAfter(request *http.Request, response *http.Response, netease *Netea
|
||||
// log.Println("NeedRepackage")
|
||||
modifiedJson, _ := json.Marshal(netease.JsonBody)
|
||||
// log.Println(netease)
|
||||
- // log.Println(string(modifiedJson))
|
||||
+ if *config.LogWebTraffic {
|
||||
+ log.Println("modified =>\n" + string(modifiedJson))
|
||||
+ }
|
||||
if netease.Encrypted {
|
||||
modifiedJson = crypto.AesEncryptECB(modifiedJson, []byte(aeskey))
|
||||
}
|
||||
@@ -258,14 +279,24 @@ func disableUpdate(netease *Netease) bool {
|
||||
if len(value.(common.SliceType)) > 0 {
|
||||
modified = true
|
||||
jsonBody["updateFiles"] = make(common.SliceType, 0)
|
||||
+ log.Println("disable update has been triggered.")
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
- // modifiedJson, _ := json.Marshal(jsonBody)
|
||||
- // log.Println(string(modifiedJson))
|
||||
return modified
|
||||
}
|
||||
+
|
||||
+func logResponse(netease *Netease) {
|
||||
+ if *config.LogWebTraffic {
|
||||
+ reqUrl := netease.Path
|
||||
+ jsonBody := netease.JsonBody
|
||||
+ modifiedJson, _ := json.Marshal(jsonBody)
|
||||
+ sep := "===================================\n"
|
||||
+ log.Println(sep + reqUrl + " => \n" + string(modifiedJson) + "\n")
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
func localVIP(netease *Netease) bool {
|
||||
if !*config.EnableLocalVip {
|
||||
return false
|
||||
@@ -0,0 +1,29 @@
|
||||
From 6c009953d357d1cc03478cf65fc05701fb1966d6 Mon Sep 17 00:00:00 2001
|
||||
From: ameansone <ameansone@outlook.com>
|
||||
Date: Sun, 5 Dec 2021 19:18:20 +0800
|
||||
Subject: [PATCH] fix(processor): avoid unnecessary decryption
|
||||
|
||||
---
|
||||
processor/processor.go | 8 ++++++--
|
||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/processor/processor.go b/processor/processor.go
|
||||
index 8d09dbf..011571b 100644
|
||||
--- a/processor/processor.go
|
||||
+++ b/processor/processor.go
|
||||
@@ -177,9 +177,13 @@ func RequestAfter(request *http.Request, response *http.Response, netease *Netea
|
||||
if netease.Forward {
|
||||
aeskey = linuxApiKey
|
||||
}
|
||||
- decryptECBBytes, encrypted := crypto.AesDecryptECB(decryptECBBytes, []byte(aeskey))
|
||||
- netease.Encrypted = encrypted
|
||||
result := utils.ParseJson(decryptECBBytes)
|
||||
+ netease.Encrypted = false;
|
||||
+ if result == nil {
|
||||
+ decryptECBBytes, encrypted := crypto.AesDecryptECB(decryptECBBytes, []byte(aeskey))
|
||||
+ netease.Encrypted = encrypted
|
||||
+ result = utils.ParseJson(decryptECBBytes)
|
||||
+ }
|
||||
netease.JsonBody = result
|
||||
|
||||
modified := false
|
||||
@@ -0,0 +1,47 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=UnblockNeteaseMusic
|
||||
PKG_VERSION:=0.28.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/UnblockNeteaseMusic/server/tar.gz/v${PKG_VERSION}?
|
||||
PKG_HASH:=0a1e843ffaa7b8ce65cc221daec07b63754473f0430019acb228f5b254602318
|
||||
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/UnblockNeteaseMusic
|
||||
SECTION:=multimedia
|
||||
CATEGORY:=Multimedia
|
||||
TITLE:=Revive Netease Cloud Music (NodeJS)
|
||||
URL:=https://github.com/nondanee/UnblockNeteaseMusic
|
||||
DEPENDS:=+node
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
Revive Netease Cloud Music (NodeJS)
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
tar -xzvf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_BUILD_DIR)
|
||||
mkdir -p $(PKG_BUILD_DIR)/$(PKG_NAME)
|
||||
echo -e $(PKG_VERSION) > $(PKG_BUILD_DIR)/$(PKG_NAME)/core_ver
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/share/$(PKG_NAME)
|
||||
$(CP) $(PKG_BUILD_DIR)/$(PKG_NAME)/* $(1)/usr/share/$(PKG_NAME)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
@@ -0,0 +1,96 @@
|
||||
#
|
||||
# Copyright (C) 2015-2016 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v3.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=alist
|
||||
PKG_VERSION:=3.60.0
|
||||
PKG_WEB_VERSION:=3.60.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/AlistGo/alist/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=beccffdfba65a168e796a1789b6bea11ca225101cbc4276451e02b9ad8751a82
|
||||
|
||||
PKG_LICENSE:=GPL-3.0
|
||||
PKG_LICENSE_FILE:=LICENSE
|
||||
PKG_MAINTAINER:=sbwml <admin@cooluc.com>
|
||||
|
||||
define Download/$(PKG_NAME)-web
|
||||
FILE:=$(PKG_NAME)-web-$(PKG_WEB_VERSION).tar.gz
|
||||
URL_FILE:=dist.tar.gz
|
||||
URL:=https://github.com/AlistGo/alist-web/releases/download/$(PKG_WEB_VERSION)/
|
||||
HASH:=35c4a218fce07c9ef5113b70ec93e385c57c2910de8852b2d0973129c12b0843
|
||||
endef
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
GO_PKG:=github.com/AlistGo/alist
|
||||
GO_PKG_LDFLAGS:= \
|
||||
-X '$(GO_PKG)/v3/internal/conf.BuiltAt=$(shell date '+%Y-%m-%d %H:%M:%S %z')' \
|
||||
-X '$(GO_PKG)/v3/internal/conf.GoVersion=$(shell $(STAGING_DIR_HOSTPKG)/bin/go version | sed 's/go version //')' \
|
||||
-X '$(GO_PKG)/v3/internal/conf.GitAuthor=Xhofe <i@nn.ci>' \
|
||||
-X '$(GO_PKG)/v3/internal/conf.GitCommit=tarball/$(shell echo $(PKG_HASH) | cut -c 1-7)' \
|
||||
-X '$(GO_PKG)/v3/internal/conf.Version=v$(PKG_VERSION) (OpenWrt $(ARCH_PACKAGES))' \
|
||||
-X '$(GO_PKG)/v3/internal/conf.WebVersion=$(PKG_WEB_VERSION)'
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=Web Servers/Proxies
|
||||
TITLE:=A file list program that supports multiple storage
|
||||
URL:=https://alist.nn.ci/
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/alist
|
||||
/etc/config/alist
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
A file list program that supports multiple storage, powered by Gin and Solidjs.
|
||||
endef
|
||||
|
||||
ifeq ($(ARCH),arm)
|
||||
ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE))))
|
||||
ifeq ($(ARM_CPU_FEATURES),)
|
||||
TARGET_CFLAGS:=
|
||||
TARGET_LDFLAGS:=
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_USE_MUSL),)
|
||||
TARGET_CFLAGS += -D_LARGEFILE64_SOURCE
|
||||
endif
|
||||
|
||||
define Build/Prepare
|
||||
$(call Build/Prepare/Default)
|
||||
$(TAR) --strip-components=1 -C $(PKG_BUILD_DIR)/public/dist -xzf $(DL_DIR)/$(PKG_NAME)-web-$(PKG_WEB_VERSION).tar.gz
|
||||
$(SED) 's_https://jsd.nn.ci/gh/alist-org/logo@main/logo.png_/assets/logo.png_g' $(PKG_BUILD_DIR)/public/dist/index.html
|
||||
ifneq ($(CONFIG_ARCH_64BIT),y)
|
||||
$(RM) -rf $(PKG_BUILD_DIR)/drivers/{lark,lark.go}
|
||||
endif
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR))
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/alist $(1)/usr/bin
|
||||
$(INSTALL_DIR) $(1)/etc/config $(1)/etc/init.d $(1)/etc/alist
|
||||
$(INSTALL_CONF) $(CURDIR)/files/alist.config $(1)/etc/config/alist
|
||||
$(INSTALL_BIN) $(CURDIR)/files/alist.init $(1)/etc/init.d/alist
|
||||
$(INSTALL_DATA) $(CURDIR)/files/data.db $(1)/etc/alist/data.db
|
||||
endef
|
||||
|
||||
$(eval $(call Download,$(PKG_NAME)-web))
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
config alist
|
||||
option enabled '0'
|
||||
option port '5244'
|
||||
option delayed_start '0'
|
||||
option allow_wan '0'
|
||||
option force '1'
|
||||
option token_expires_in '48'
|
||||
option max_connections '0'
|
||||
option tls_insecure_skip_verify '1'
|
||||
option data_dir '/etc/alist'
|
||||
option temp_dir '/tmp/alist'
|
||||
option log '1'
|
||||
option log_max_size '10'
|
||||
option log_max_backups '5'
|
||||
option log_max_age '28'
|
||||
option log_compress '0'
|
||||
option database_type 'sqlite3'
|
||||
option ssl '0'
|
||||
option download_workers '5'
|
||||
option download_max_retry '1'
|
||||
option transfer_workers '5'
|
||||
option transfer_max_retry '2'
|
||||
option upload_workers '5'
|
||||
option upload_max_retry '0'
|
||||
option copy_workers '5'
|
||||
option copy_max_retry '2'
|
||||
option cors_allow_origins '*'
|
||||
option cors_allow_methods '*'
|
||||
option cors_allow_headers '*'
|
||||
option s3 '0'
|
||||
Executable
+246
@@ -0,0 +1,246 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
START=99
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/alist
|
||||
LOG_FILE=/var/log/alist.log
|
||||
|
||||
get_config() {
|
||||
config_get_bool enabled $1 enabled 1
|
||||
config_get port $1 port 5244
|
||||
config_get allow_wan $1 allow_wan 0
|
||||
config_get delayed_start $1 delayed_start 0
|
||||
|
||||
config_get force $1 force 1
|
||||
config_get site_url $1 site_url ""
|
||||
config_get cdn $1 cdn ""
|
||||
config_get jwt_secret $1 jwt_secret ""
|
||||
config_get data_dir $1 data_dir "/etc/alist"
|
||||
config_get temp_dir $1 temp_dir "/tmp/alist"
|
||||
config_get token_expires_in $1 token_expires_in 48
|
||||
config_get max_connections $1 max_connections 0
|
||||
config_get tls_insecure_skip_verify $1 tls_insecure_skip_verify 1
|
||||
|
||||
# log
|
||||
config_get log $1 log 1
|
||||
config_get log_max_size $1 log_max_size 10
|
||||
config_get log_max_backups $1 log_max_backups 5
|
||||
config_get log_max_age $1 log_max_age 28
|
||||
config_get log_compress $1 log_compress 0
|
||||
|
||||
# scheme
|
||||
config_get ssl $1 ssl 0
|
||||
config_get force_https $1 force_https 0
|
||||
config_get ssl_cert $1 ssl_cert ""
|
||||
config_get ssl_key $1 ssl_key ""
|
||||
|
||||
# database
|
||||
config_get database_type $1 database_type "sqlite3"
|
||||
config_get mysql_host $1 mysql_host ""
|
||||
config_get mysql_port $1 mysql_port "3306"
|
||||
config_get mysql_username $1 mysql_username ""
|
||||
config_get mysql_password $1 mysql_password ""
|
||||
config_get mysql_database $1 mysql_database ""
|
||||
config_get mysql_table_prefix $1 mysql_table_prefix "x_"
|
||||
config_get mysql_ssl_mode $1 mysql_ssl_mode ""
|
||||
config_get mysql_dsn $1 mysql_dsn ""
|
||||
|
||||
# tasks
|
||||
config_get download_workers $1 download_workers 5
|
||||
config_get download_max_retry $1 download_max_retry 1
|
||||
config_get transfer_workers $1 transfer_workers 5
|
||||
config_get transfer_max_retry $1 transfer_max_retry 2
|
||||
config_get upload_workers $1 upload_workers 5
|
||||
config_get upload_max_retry $1 upload_max_retry 0
|
||||
config_get copy_workers $1 copy_workers 5
|
||||
config_get copy_max_retry $1 copy_max_retry 2
|
||||
|
||||
# cors
|
||||
config_get cors_allow_origins $1 cors_allow_origins '*'
|
||||
config_get cors_allow_methods $1 cors_allow_methods '*'
|
||||
config_get cors_allow_headers $1 cors_allow_headers '*'
|
||||
|
||||
# s3
|
||||
config_get s3 $1 s3 0
|
||||
config_get s3_port $1 s3_port 5246
|
||||
config_get s3_ssl $1 s3_ssl 0
|
||||
|
||||
config_load network
|
||||
config_get lan_addr lan ipaddr "0.0.0.0"
|
||||
if echo "${lan_addr}" | grep -Fq ' '; then
|
||||
lan_addr="0.0.0.0"
|
||||
else
|
||||
lan_addr=${lan_addr%%/*}
|
||||
fi
|
||||
|
||||
# init jwt_secret
|
||||
[ -z "$jwt_secret" ] && jwt_secret=$(tr -cd "a-zA-Z0-9" < "/dev/urandom" | head -c16)
|
||||
uci -q set alist.@alist[0].jwt_secret="$jwt_secret"
|
||||
uci commit alist
|
||||
}
|
||||
|
||||
set_firewall() {
|
||||
if [ "$external_access" = "allow" ]; then
|
||||
uci -q delete firewall.alist
|
||||
uci set firewall.alist=rule
|
||||
uci set firewall.alist.name="alist"
|
||||
uci set firewall.alist.target="ACCEPT"
|
||||
uci set firewall.alist.src="wan"
|
||||
uci set firewall.alist.proto="tcp"
|
||||
uci set firewall.alist.dest_port="$port"
|
||||
uci set firewall.alist.enabled="1"
|
||||
uci commit firewall
|
||||
/etc/init.d/firewall reload >/dev/null 2>&1
|
||||
elif [ "$external_access" = "deny" ]; then
|
||||
uci -q delete firewall.alist
|
||||
uci commit firewall
|
||||
/etc/init.d/firewall reload >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load alist
|
||||
config_foreach get_config alist
|
||||
[ $enabled -ne 1 ] && return 1
|
||||
mkdir -p "$temp_dir" "$data_dir"
|
||||
[ "$ssl" -eq 1 ] && https_port=$port http_port="-1" || https_port="-1" http_port=$port
|
||||
if [ -e /proc/uptime ]; then
|
||||
[ $(awk -F. '{print $1}' /proc/uptime) -lt "120" ] && delayed_start=$delayed_start || delayed_start=0
|
||||
else
|
||||
delayed_start=$delayed_start
|
||||
fi
|
||||
if [ "$allow_wan" -eq "1" ]; then
|
||||
listen_addr="0.0.0.0"
|
||||
external_access="allow"
|
||||
else
|
||||
listen_addr=$lan_addr
|
||||
external_access="deny"
|
||||
fi
|
||||
|
||||
set_firewall
|
||||
> $LOG_FILE
|
||||
|
||||
# init config
|
||||
json_init
|
||||
json_add_boolean "force" "$force"
|
||||
json_add_string "site_url" "$site_url"
|
||||
json_add_string "cdn" "$cdn"
|
||||
json_add_string "jwt_secret" "$jwt_secret"
|
||||
json_add_int "token_expires_in" "$token_expires_in"
|
||||
|
||||
# database
|
||||
json_add_object 'database'
|
||||
json_add_string "type" "$database_type"
|
||||
json_add_string "host" "$mysql_host"
|
||||
json_add_int "port" "$mysql_port"
|
||||
json_add_string "user" "$mysql_username"
|
||||
json_add_string "password" "$mysql_password"
|
||||
json_add_string "name" "$mysql_database"
|
||||
json_add_string "db_file" "$data_dir/data.db"
|
||||
json_add_string "table_prefix" "$mysql_table_prefix"
|
||||
json_add_string "ssl_mode" "$mysql_ssl_mode"
|
||||
json_add_string "dsn" "$mysql_dsn"
|
||||
json_close_object
|
||||
|
||||
# meilisearch
|
||||
json_add_object "meilisearch"
|
||||
json_add_string "host" "http://localhost:7700"
|
||||
json_add_string "api_key" ""
|
||||
json_add_string "index_prefix" ""
|
||||
json_close_object
|
||||
|
||||
# scheme
|
||||
json_add_object "scheme"
|
||||
json_add_string "address" "$listen_addr"
|
||||
json_add_int "http_port" "$http_port"
|
||||
json_add_int "https_port" "$https_port"
|
||||
json_add_boolean "force_https" "$force_https"
|
||||
json_add_string "cert_file" "$ssl_cert"
|
||||
json_add_string "key_file" "$ssl_key"
|
||||
json_add_string "unix_file" ""
|
||||
json_add_string "unix_file_perm" ""
|
||||
json_close_object
|
||||
|
||||
json_add_string "temp_dir" "$temp_dir"
|
||||
json_add_string "bleve_dir" "$data_dir/bleve"
|
||||
json_add_string "dist_dir" ""
|
||||
|
||||
# log
|
||||
json_add_object "log"
|
||||
json_add_boolean "enable" "$log"
|
||||
json_add_string "name" "$LOG_FILE"
|
||||
json_add_int "max_size" "$log_max_size"
|
||||
json_add_int "max_backups" "$log_max_backups"
|
||||
json_add_int "max_age" "$log_max_age"
|
||||
json_add_boolean "compress" "$log_compress"
|
||||
json_close_object
|
||||
|
||||
json_add_int "delayed_start" "$delayed_start"
|
||||
json_add_int "max_connections" "$max_connections"
|
||||
json_add_boolean "tls_insecure_skip_verify" "$tls_insecure_skip_verify"
|
||||
|
||||
# tasks
|
||||
json_add_object "tasks"
|
||||
json_add_object "download"
|
||||
json_add_int "workers" "$download_workers"
|
||||
json_add_int "max_retry" "$download_max_retry"
|
||||
json_close_object
|
||||
json_add_object "transfer"
|
||||
json_add_int "workers" "$transfer_workers"
|
||||
json_add_int "max_retry" "$transfer_max_retry"
|
||||
json_close_object
|
||||
json_add_object "upload"
|
||||
json_add_int "workers" "$upload_workers"
|
||||
json_add_int "max_retry" "$upload_max_retry"
|
||||
json_close_object
|
||||
json_add_object "copy"
|
||||
json_add_int "workers" "$copy_workers"
|
||||
json_add_int "max_retry" "$copy_max_retry"
|
||||
json_close_object
|
||||
json_close_object
|
||||
|
||||
# cors
|
||||
json_add_object "cors"
|
||||
json_add_array "allow_origins"
|
||||
json_add_string "" "$cors_allow_origins"
|
||||
json_close_array
|
||||
json_add_array "allow_methods"
|
||||
json_add_string "" "$cors_allow_methods"
|
||||
json_close_array
|
||||
json_add_array "allow_headers"
|
||||
json_add_string "" "$cors_allow_headers"
|
||||
json_close_array
|
||||
json_close_object
|
||||
|
||||
# s3
|
||||
json_add_object "s3"
|
||||
json_add_boolean "enable" "$s3"
|
||||
json_add_int "port" "$s3_port"
|
||||
json_add_boolean "ssl" "$s3_ssl"
|
||||
json_close_object
|
||||
|
||||
json_dump > "$data_dir/config.json"
|
||||
|
||||
procd_open_instance alist
|
||||
procd_set_param command $PROG
|
||||
procd_append_param command server
|
||||
procd_append_param command --data "$data_dir"
|
||||
procd_set_param file "$data_dir/config.json"
|
||||
procd_set_param stdout 0
|
||||
procd_set_param stderr 0
|
||||
procd_set_param respawn
|
||||
procd_set_param limits core="unlimited"
|
||||
procd_set_param limits nofile="200000 200000"
|
||||
procd_close_instance alist
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "alist"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
external_access="deny"
|
||||
set_firewall
|
||||
}
|
||||
Binary file not shown.
+10
@@ -0,0 +1,10 @@
|
||||
<svg width="1252" height="1252" xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
<g>
|
||||
<g id="#70c6beff">
|
||||
<path id="svg_2" d="m634.37,138.38c11.88,-1.36 24.25,1.3 34.18,8.09c14.96,9.66 25.55,24.41 34.49,39.51c40.59,68.03 81.45,135.91 122.02,203.96c54.02,90.99 108.06,181.97 161.94,273.06c37.28,63 74.65,125.96 112.18,188.82c24.72,41.99 50.21,83.54 73.84,126.16c10.18,17.84 15.77,38.44 14.93,59.03c-0.59,15.92 -3.48,32.28 -11.84,46.08c-11.73,19.46 -31.39,33.2 -52.71,40.36c-11.37,4.09 -23.3,6.87 -35.43,6.89c-132.32,-0.05 -264.64,0.04 -396.95,0.03c-11.38,-0.29 -22.95,-1.6 -33.63,-5.72c-7.81,-3.33 -15.5,-7.43 -21.61,-13.42c-10.43,-10.32 -17.19,-24.96 -15.38,-39.83c0.94,-10.39 3.48,-20.64 7.76,-30.16c4.15,-9.77 9.99,-18.67 15.06,-27.97c22.13,-39.47 45.31,-78.35 69.42,-116.65c7.72,-12.05 14.44,-25.07 25.12,-34.87c11.35,-10.39 25.6,-18.54 41.21,-19.6c12.55,-0.52 24.89,3.82 35.35,10.55c11.8,6.92 21.09,18.44 24.2,31.88c4.49,17.01 -0.34,34.88 -7.55,50.42c-8.09,17.65 -19.62,33.67 -25.81,52.18c-1.13,4.21 -2.66,9.52 0.48,13.23c3.19,3 7.62,4.18 11.77,5.22c12,2.67 24.38,1.98 36.59,2.06c45,-0.01 90,0 135,0c8.91,-0.15 17.83,0.3 26.74,-0.22c6.43,-0.74 13.44,-1.79 18.44,-6.28c3.3,-2.92 3.71,-7.85 2.46,-11.85c-2.74,-8.86 -7.46,-16.93 -12.12,-24.89c-119.99,-204.91 -239.31,-410.22 -360.56,-614.4c-3.96,-6.56 -7.36,-13.68 -13.03,-18.98c-2.8,-2.69 -6.95,-4.22 -10.77,-3.11c-3.25,1.17 -5.45,4.03 -7.61,6.57c-5.34,6.81 -10.12,14.06 -14.51,21.52c-20.89,33.95 -40.88,68.44 -61.35,102.64c-117.9,198.43 -235.82,396.85 -353.71,595.29c-7.31,13.46 -15.09,26.67 -23.57,39.43c-7.45,10.96 -16.49,21.23 -28.14,27.83c-13.73,7.94 -30.69,11.09 -46.08,6.54c-11.23,-3.47 -22.09,-9.12 -30.13,-17.84c-10.18,-10.08 -14.69,-24.83 -14.17,-38.94c0.52,-14.86 5.49,-29.34 12.98,-42.1c71.58,-121.59 143.62,-242.92 215.93,-364.09c37.2,-62.8 74.23,-125.69 111.64,-188.36c37.84,-63.5 75.77,-126.94 113.44,-190.54c21.02,-35.82 42.19,-71.56 64.28,-106.74c6.79,-11.15 15.58,-21.15 26.16,-28.85c8.68,-5.92 18.42,-11 29.05,-11.94z" fill="#70c6be"/>
|
||||
</g>
|
||||
<g id="#1ba0d8ff">
|
||||
<path id="svg_3" d="m628.35,608.38c17.83,-2.87 36.72,1.39 51.5,11.78c11.22,8.66 19.01,21.64 21.26,35.65c1.53,10.68 0.49,21.75 -3.44,31.84c-3.02,8.73 -7.35,16.94 -12.17,24.81c-68.76,115.58 -137.5,231.17 -206.27,346.75c-8.8,14.47 -16.82,29.47 -26.96,43.07c-7.37,9.11 -16.58,16.85 -27.21,21.89c-22.47,11.97 -51.79,4.67 -68.88,-13.33c-8.66,-8.69 -13.74,-20.63 -14.4,-32.84c-0.98,-12.64 1.81,-25.42 7.53,-36.69c5.03,-10.96 10.98,-21.45 17.19,-31.77c30.22,-50.84 60.17,-101.84 90.3,-152.73c41.24,-69.98 83.16,-139.55 124.66,-209.37c4.41,-7.94 9.91,-15.26 16.09,-21.9c8.33,-8.46 18.9,-15.3 30.8,-17.16z" fill="#1ba0d8"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,46 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Copyright (C) 2009-2013 OpenWrt.org
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=BaiduPCS-Go
|
||||
PKG_VERSION:=3.9.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/qjfoidnh/BaiduPCS-Go/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=de71666e961a3eeb90f8aca7b11934c3f0296d1870e036a7c086ba1decfca8ab
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
|
||||
GO_PKG:=github.com/qjfoidnh/BaiduPCS-Go
|
||||
GO_PKG_LDFLAGS_X:=main.Version=v$(PKG_VERSION)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../../lang/golang/golang-package.mk
|
||||
|
||||
define Package/BaiduPCS-Go
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=Download Manager
|
||||
TITLE:=Terminal utility for Baidu Network Disk
|
||||
URL:=https://github.com/qjfoidnh/BaiduPCS-Go
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
endef
|
||||
|
||||
define Package/BaiduPCS-Go/install
|
||||
$(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR))
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin/
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/bin/BaiduPCS-Go $(1)/usr/bin/
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,BaiduPCS-Go))
|
||||
$(eval $(call BuildPackage,BaiduPCS-Go))
|
||||
@@ -0,0 +1,57 @@
|
||||
#
|
||||
# Copyright (C) 2006-2009 OpenWrt.org
|
||||
# Copyright (C) 2005-2016 Weijie Gao <hackpascal@gmail.com>
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=brlaser
|
||||
PKG_VERSION:=v3
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_URL:=https://github.com/pdewacht/brlaser.git
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_SOURCE_VERSION:=2a49e3287c70c254e7e3ac9dabe9d6a07218c3fa
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
|
||||
PKG_FIXUP:=autoreconf
|
||||
|
||||
PKG_INSTALL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
define Package/brlaser
|
||||
SECTION:=print
|
||||
CATEGORY:=Printing and Imaging
|
||||
TITLE:=Driver for (some) Brother laster printers
|
||||
DEPENDS:=+libcupsimage +libstdcpp
|
||||
MAINTAINER:=Weijie Gao <hackpascal@gmail.com>
|
||||
URL:=https://github.com/pdewacht/brlaser
|
||||
endef
|
||||
|
||||
define Package/brlaser/description
|
||||
Driver for Brother DCP-7030 / Brother DCP-7055 / Brother DCP-7065DN
|
||||
endef
|
||||
|
||||
define Package/brlaser/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/cups/filter
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/cups/filter/rastertobrlaser $(1)/usr/lib/cups/filter/
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/cups/drv
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/cups/drv/brlaser.drv $(1)/usr/share/cups/drv/
|
||||
|
||||
(cd $(PKG_BUILD_DIR) && ppdc brlaser.drv)
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/ppd/Brother
|
||||
$(CP) $(PKG_BUILD_DIR)/ppd/*.ppd $(1)/usr/share/ppd/Brother
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) ./files/brlaser-set-cups-filter-permission $(1)/etc/uci-defaults/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,brlaser))
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -d /usr/lib/cups/filter ] && chmod 0754 /usr/lib/cups/filter/*
|
||||
@@ -0,0 +1,45 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021-2023 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=brook
|
||||
PKG_VERSION:=20260101.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=8ddba4ed9ae9d10928e169f8121c6791e0e3c2907fa27d6e0055fe434f6e700e
|
||||
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
PKG_LICENSE:=GPL-3.0
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
GO_PKG:=github.com/txthinking/brook
|
||||
GO_PKG_BUILD_PKG:=$(GO_PKG)/cli/brook
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/brook
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=Web Servers/Proxies
|
||||
TITLE:=A cross-platform proxy software
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
URL:=https://github.com/txthinking/brook
|
||||
endef
|
||||
|
||||
define Package/brook/description
|
||||
Brook is a cross-platform strong encryption and not detectable proxy.
|
||||
Zero-Configuration.
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,brook))
|
||||
$(eval $(call BuildPackage,brook))
|
||||
@@ -0,0 +1,72 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=chinadns-ng
|
||||
PKG_VERSION:=2025.08.09
|
||||
PKG_RELEASE:=1
|
||||
|
||||
ifeq ($(ARCH),aarch64)
|
||||
ifeq ($(BOARD),rockchip)
|
||||
PKG_ARCH:=chinadns-ng+wolfssl@aarch64-linux-musl@generic+v8a@fast+lto
|
||||
PKG_HASH:=3fe0217615dd7060b7287d2b6b31d2a0b364137398bfb335a03bead322eac716
|
||||
else
|
||||
PKG_ARCH:=chinadns-ng+wolfssl_noasm@aarch64-linux-musl@generic+v8a@fast+lto
|
||||
PKG_HASH:=42ddd494200ec6d88b35902927688d316bc23e06e6c08d9e01eb2412196ab845
|
||||
endif
|
||||
else ifeq ($(ARCH),arm)
|
||||
ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE))))
|
||||
ifeq ($(ARM_CPU_FEATURES),)
|
||||
PKG_ARCH:=chinadns-ng+wolfssl@arm-linux-musleabi@generic+v6+soft_float@fast+lto
|
||||
PKG_HASH:=0a401d1dc11129481b2baf86f847d55d66bd7e725cba4bf57875fdad27ef0052
|
||||
else
|
||||
PKG_ARCH:=chinadns-ng+wolfssl@arm-linux-musleabihf@generic+v7a@fast+lto
|
||||
PKG_HASH:=dfa1f6ba80fb0925613822f4c4e00df8da68e7b8b772048d26a0d1a9d07d346b
|
||||
endif
|
||||
else ifeq ($(ARCH),mips)
|
||||
PKG_ARCH:=chinadns-ng+wolfssl@mips-linux-musl@mips32+soft_float@fast+lto
|
||||
PKG_HASH:=b610821a8f61b0ed3c8c7e82e10d401348a9de17f900988589024a37c4099c8e
|
||||
else ifeq ($(ARCH),mipsel)
|
||||
PKG_ARCH:=chinadns-ng+wolfssl@mipsel-linux-musl@mips32+soft_float@fast+lto
|
||||
PKG_HASH:=760544a88724e3b1b9eac79c9400231e81aa8786f8f00a979229e175811ffe6d
|
||||
else ifeq ($(ARCH),mips64)
|
||||
PKG_ARCH:=chinadns-ng+wolfssl@mips64-linux-musl@mips64+soft_float@fast+lto
|
||||
PKG_HASH:=2d0fce18a7ef1d74fdc12738767e66998a52c2b30d8790da760933853fe8726e
|
||||
else ifeq ($(ARCH),i386)
|
||||
PKG_ARCH:=chinadns-ng+wolfssl@i386-linux-musl@i686@fast+lto
|
||||
PKG_HASH:=85e057dd0a0e8913b30471737436ab8b71834c494ed9f9e53544261b1ffdc8d6
|
||||
else ifeq ($(ARCH),x86_64)
|
||||
PKG_ARCH:=chinadns-ng+wolfssl@x86_64-linux-musl@x86_64@fast+lto
|
||||
PKG_HASH:=842ea4e9816efd91d39bc76ead5c4a42e79011757e37c521b4270b675cfcb30c
|
||||
else
|
||||
PKG_HASH:=dummy
|
||||
endif
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_ARCH)
|
||||
PKG_SOURCE_URL:=https://github.com/zfl9/chinadns-ng/releases/download/$(PKG_VERSION)/$(PKG_ARCH)?
|
||||
UNPACK_CMD=$(CP) $(DL_DIR)/$(PKG_SOURCE) $(PKG_BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
PKG_LICENSE:=AGPL-3.0-only
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=sbwml <admin@cooluc.com>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/chinadns-ng
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
TITLE:=ChinaDNS next generation, refactoring with epoll and ipset.
|
||||
URL:=https://github.com/zfl9/chinadns-ng
|
||||
DEPENDS:=@(aarch64||arm||i386||mips||mipsel||mips64||x86_64) +ipset
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/chinadns-ng/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/chinadns-ng $(1)/usr/bin
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,chinadns-ng))
|
||||
@@ -0,0 +1,46 @@
|
||||
#
|
||||
# Copyright (C) 2007-2011 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=daemonlogger
|
||||
PKG_VERSION:=1.2.1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=@SF/daemonlogger
|
||||
PKG_HASH:=79fcd34d815e9c671ffa1ea3c7d7d50f895bb7a79b4448c4fd1c37857cf44a0b
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
PKG_MAINTAINER:=Mirko Vogt <mirko@openwrt.org>
|
||||
|
||||
PKG_INSTALL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/daemonlogger
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
DEPENDS:=+libpcap +libdnet
|
||||
TITLE:=Software Network Tap
|
||||
URL:=http://www.snort.org/snort-downloads/additional-downloads
|
||||
endef
|
||||
|
||||
CONFIGURE_VARS += \
|
||||
BUILD_CC="$(TARGET_CC)" \
|
||||
HOSTCC="$(HOSTCC)"
|
||||
|
||||
MAKE_FLAGS := CCOPT="$(TARGET_CFLAGS)" INCLS="-I. $(TARGET_CPPFLAGS)"
|
||||
|
||||
define Package/daemonlogger/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/daemonlogger $(1)/usr/bin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,daemonlogger))
|
||||
@@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Copyright (C) 2017-2024 Zxlhhyccc <zxlhhyccc@gmail.com>
|
||||
# Copyright (C) 2021-2024 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dns2socks-rust
|
||||
PKG_VERSION:=0.2.5
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/tun2proxy/dns2socks/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=53f62c38df51bc5f11c519549213a7fd0dcb33b595410b662ea63d480faced9a
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/dns2socks-$(PKG_VERSION)
|
||||
|
||||
PKG_MAINTAINER:=Zxlhhyccc <zxlhhyccc@gmail.com>
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
PKG_BUILD_DEPENDS:=rust/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
#RUST_PKG:=dns2socks
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk
|
||||
|
||||
define Package/dns2socks-rust
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
TITLE:=DNS forwards to SOCKS5 server
|
||||
URL:=https://github.com/tun2proxy/dns2socks.git
|
||||
DEPENDS:=$$(RUST_ARCH_DEPENDS)
|
||||
endef
|
||||
|
||||
define Package/dns2socks-rust/description
|
||||
This is a DNS server that forwards DNS requests to a SOCKS5 server.
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call Build/Compile/Cargo,,--all-features)
|
||||
endef
|
||||
|
||||
define Package/dns2socks-rust/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/target/$(RUSTC_TARGET_ARCH)/release/dns2socks $(1)/usr/bin/dns2socks-rust
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,dns2socks-rust))
|
||||
@@ -0,0 +1,51 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021-2026 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dns2socks
|
||||
PKG_VERSION:=2.2
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/dns2socks/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=790f7ca5ec07677a699121eb071c9e66cdafe65a22bb229eed7fd3cf215631d2
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_LICENSE_FILE:=LICENSE
|
||||
PKG_MAINTAINER:=ghostmaker
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/dns2socks
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
TITLE:=DNS to SOCKS or HTTP proxy
|
||||
URL:=https://github.com/kenzok8/dns2socks
|
||||
DEPENDS:=+libpthread
|
||||
endef
|
||||
|
||||
define Package/dns2socks/description
|
||||
This is a command line utility to resolve DNS requests via
|
||||
a SOCKS tunnel like Tor or a HTTP proxy.
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(TARGET_CC) \
|
||||
$(TARGET_CFLAGS) \
|
||||
$(TARGET_CPPFLAGS) \
|
||||
$(FPIC) \
|
||||
-o $(PKG_BUILD_DIR)/dns2socks \
|
||||
$(PKG_BUILD_DIR)/src/dns2socks.c \
|
||||
$(TARGET_LDFLAGS) -pthread
|
||||
endef
|
||||
|
||||
define Package/dns2socks/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/dns2socks $(1)/usr/bin/dns2socks
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,dns2socks))
|
||||
@@ -0,0 +1,42 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2022 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dns2tcp
|
||||
PKG_VERSION:=1.1.2
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/zfl9/dns2tcp/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=5e8c6302a1d32c16ae7d4b8e39cd9aad1f2d7e68fe18813e76cb1e48ec5940d2
|
||||
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
PKG_LICENSE:=AGPL-3.0-only
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=no-mips16 gc-sections lto
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/dns2tcp
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
TITLE:=utility to convert dns query from udp to tcp
|
||||
URL:=https://github.com/zfl9/dns2tcp
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS+= $(FPIC)
|
||||
MAKE_FLAGS+= \
|
||||
CFLAGS="-std=c99 $(TARGET_CFLAGS)" \
|
||||
EVCFLAGS="$(TARGET_CFLAGS)"
|
||||
|
||||
define Package/dns2tcp/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/dns2tcp $(1)/usr/bin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,dns2tcp))
|
||||
@@ -0,0 +1,74 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=docker
|
||||
PKG_VERSION:=29.5.2
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/docker/cli/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=e5cf80d9e0b6630bb2e97decf664ad5f0c732b296b7c47030044c3a7287bbddb
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/cli-$(PKG_VERSION)
|
||||
PKG_GIT_SHORT_COMMIT:=$(shell $(CURDIR)/git-short-commit.sh 'github.com/docker/cli' 'v$(PKG_VERSION)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)')
|
||||
|
||||
PKG_MAINTAINER:=Gerard Ryan <G.M0N3Y.2503@gmail.com>
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
GO_PKG:=github.com/docker/cli
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../../lang/golang/golang-package.mk
|
||||
|
||||
define Package/docker
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Docker Community Edition CLI
|
||||
URL:=https://www.docker.com/
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS)
|
||||
endef
|
||||
|
||||
define Package/docker/description
|
||||
The CLI used in the Docker CE and Docker EE products.
|
||||
endef
|
||||
|
||||
GO_PKG_INSTALL_EXTRA:=\
|
||||
cli/compose/schema/data \
|
||||
vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb
|
||||
|
||||
TAR_OPTIONS:=--strip-components 1 $(TAR_OPTIONS)
|
||||
TAR_CMD=$(HOST_TAR) -C $(1) $(TAR_OPTIONS)
|
||||
TARGET_LDFLAGS += $(if $(CONFIG_USE_GLIBC),-lc -lgcc_eh)
|
||||
|
||||
define Build/Prepare
|
||||
$(Build/Prepare/Default)
|
||||
|
||||
# Verify PKG_GIT_SHORT_COMMIT
|
||||
( \
|
||||
EXPECTED_PKG_GIT_SHORT_COMMIT=$$$$( $(CURDIR)/git-short-commit.sh 'github.com/docker/cli' 'v$(PKG_VERSION)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)' ); \
|
||||
if [ "$$$${EXPECTED_PKG_GIT_SHORT_COMMIT}" != "$(strip $(PKG_GIT_SHORT_COMMIT))" ]; then \
|
||||
echo "ERROR: Expected 'PKG_GIT_SHORT_COMMIT:=$$$${EXPECTED_PKG_GIT_SHORT_COMMIT}', found 'PKG_GIT_SHORT_COMMIT:=$(strip $(PKG_GIT_SHORT_COMMIT))'"; \
|
||||
exit 1; \
|
||||
fi \
|
||||
)
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
( \
|
||||
cd $(PKG_BUILD_DIR); \
|
||||
$(GO_PKG_VARS) \
|
||||
GITCOMMIT=$(PKG_GIT_SHORT_COMMIT) \
|
||||
VERSION=$(PKG_VERSION) \
|
||||
./scripts/build/binary; \
|
||||
)
|
||||
endef
|
||||
|
||||
define Package/docker/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin/
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/build/docker $(1)/usr/bin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,docker))
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
#!/bin/sh
|
||||
#
|
||||
# USAGE: git-short-commit.sh <GIT_URL> <GIT_REF> <GIT_DIR>
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
error() {
|
||||
echo "ERROR: ${*}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
GIT_URL="${1}"
|
||||
if [ -z "${GIT_URL}" ]; then
|
||||
error "Git URL not specified"
|
||||
fi
|
||||
|
||||
GIT_REF="${2}"
|
||||
if [ -z "${GIT_REF}" ]; then
|
||||
error "Git reference not specified"
|
||||
fi
|
||||
|
||||
GIT_DIR="${3}"
|
||||
if [ -z "${GIT_DIR}" ]; then
|
||||
error "Git clone directory not specified"
|
||||
fi
|
||||
|
||||
clean_up() {
|
||||
rm -rf "${GIT_DIR}"
|
||||
}
|
||||
trap clean_up EXIT
|
||||
|
||||
git init --quiet "${GIT_DIR}"
|
||||
(
|
||||
cd "${GIT_DIR}"
|
||||
for PREFIX in "" "https://" "http://" "git@"; do
|
||||
echo "Trying remote '${PREFIX}${GIT_URL}'" >&2
|
||||
git remote add origin "${PREFIX}${GIT_URL}"
|
||||
|
||||
if git fetch --depth 1 origin "${GIT_REF}"; then
|
||||
git checkout --detach FETCH_HEAD --
|
||||
git rev-parse --short HEAD
|
||||
break
|
||||
fi
|
||||
|
||||
git remote remove origin
|
||||
done
|
||||
)
|
||||
@@ -0,0 +1,87 @@
|
||||
config DOCKER_CHECK_CONFIG
|
||||
bool "Installs check-config.sh with dependencies"
|
||||
default n
|
||||
depends on PACKAGE_dockerd
|
||||
select PACKAGE_bash
|
||||
select PACKAGE_kmod-ikconfig
|
||||
|
||||
# These options are mostly specified by https://github.com/moby/moby/blob/master/contrib/check-config.sh
|
||||
|
||||
config DOCKER_CGROUP_OPTIONS
|
||||
bool "Enable available kernel support for CGroupsV1"
|
||||
default y
|
||||
depends on PACKAGE_dockerd
|
||||
select KERNEL_CGROUP_DEVICE
|
||||
select KERNEL_CGROUP_FREEZER
|
||||
select KERNEL_NET_CLS_CGROUP
|
||||
select KERNEL_CGROUP_NET_PRIO
|
||||
select PACKAGE_cgroupfs-mount
|
||||
help
|
||||
Selects kernel options to enable CGroups V1.
|
||||
|
||||
config DOCKER_OPTIONAL_FEATURES
|
||||
bool "Enable optional kernel support for Docker"
|
||||
default n
|
||||
depends on PACKAGE_dockerd
|
||||
select KERNEL_MEMCG_SWAP_ENABLED
|
||||
select KERNEL_CFQ_GROUP_IOSCHED
|
||||
select KERNEL_CGROUP_PERF
|
||||
select KERNEL_CGROUP_HUGETLB
|
||||
help
|
||||
Select 'Optional Features' kernel options for Docker that are unselected.
|
||||
See https://github.com/moby/moby/blob/master/contrib/check-config.sh
|
||||
|
||||
menu "Network"
|
||||
depends on PACKAGE_dockerd
|
||||
|
||||
config DOCKER_NET_OVERLAY
|
||||
bool "Includes the Overlay network feature"
|
||||
default n
|
||||
select PACKAGE_kmod-vxlan
|
||||
help
|
||||
Selects kmod-vxlan for the Overlay network feature.
|
||||
|
||||
config DOCKER_NET_ENCRYPT
|
||||
bool "Includes encrypted networking kernel modules"
|
||||
depends on DOCKER_NET_OVERLAY
|
||||
default n
|
||||
select PACKAGE_kmod-crypto-gcm
|
||||
select PACKAGE_kmod-crypto-seqiv
|
||||
select PACKAGE_kmod-crypto-ghash
|
||||
select PACKAGE_kmod-ipsec
|
||||
help
|
||||
Select needed kernel modules for encrypted networking support.
|
||||
|
||||
config DOCKER_NET_MACVLAN
|
||||
bool "Includes macvlan kernel modules"
|
||||
default y
|
||||
select PACKAGE_kmod-macvlan
|
||||
select PACKAGE_kmod-dummy
|
||||
|
||||
config DOCKER_NET_TFTP
|
||||
bool "Includes ftp/tftp client kernel modules"
|
||||
default n
|
||||
select PACKAGE_kmod-nf-nathelper
|
||||
select PACKAGE_kmod-nf-nathelper-extra
|
||||
endmenu
|
||||
|
||||
menu "Storage"
|
||||
depends on PACKAGE_dockerd
|
||||
|
||||
config DOCKER_STO_DEVMAPPER
|
||||
bool "Enables support for devmapper snapshotting"
|
||||
default n
|
||||
select PACKAGE_libdevmapper
|
||||
|
||||
config DOCKER_STO_EXT4
|
||||
bool "Enables support for ext3 or ext4 as the backing filesystem"
|
||||
default y
|
||||
select KERNEL_EXT4_FS_POSIX_ACL
|
||||
select KERNEL_EXT4_FS_SECURITY
|
||||
|
||||
config DOCKER_STO_BTRFS
|
||||
bool "Enables support for btrfs as the backing filesystem"
|
||||
default n
|
||||
select KERNEL_BTRFS_FS_POSIX_ACL
|
||||
select PACKAGE_btrfs-progs
|
||||
endmenu
|
||||
@@ -0,0 +1,182 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dockerd
|
||||
PKG_VERSION:=29.5.2
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_GIT_URL:=github.com/moby/moby
|
||||
PKG_GIT_REF:=docker-v$(PKG_VERSION)
|
||||
PKG_SOURCE_URL:=https://codeload.$(PKG_GIT_URL)/tar.gz/$(PKG_GIT_REF)?
|
||||
PKG_HASH:=1235ed325d324c76f52e52beaa1dac85c92a073bf2f9fcc5bb9f67e35a668028
|
||||
PKG_GIT_SHORT_COMMIT:=$(shell $(CURDIR)/git-short-commit.sh '$(PKG_GIT_URL)' '$(PKG_GIT_REF)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)')
|
||||
|
||||
PKG_MAINTAINER:=Gerard Ryan <G.M0N3Y.2503@gmail.com>
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
GO_PKG:=github.com/docker/docker
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../../lang/golang/golang-package.mk
|
||||
|
||||
define Package/dockerd/config
|
||||
source "$(SOURCE)/Config.in"
|
||||
endef
|
||||
|
||||
define Package/dockerd
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Docker Community Edition Daemon
|
||||
URL:=https://www.docker.com/
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) \
|
||||
+ca-certificates \
|
||||
+containerd \
|
||||
+iptables \
|
||||
+iptables-mod-extra \
|
||||
+IPV6:ip6tables \
|
||||
+IPV6:kmod-ipt-nat6 \
|
||||
+KERNEL_SECCOMP:libseccomp \
|
||||
+kmod-ipt-nat \
|
||||
+kmod-ipt-physdev \
|
||||
+kmod-nf-ipvs \
|
||||
+kmod-veth \
|
||||
+tini \
|
||||
+uci-firewall \
|
||||
@!(mips||mips64||mipsel)
|
||||
USERID:=docker:docker
|
||||
MENU:=1
|
||||
endef
|
||||
|
||||
define Package/dockerd/conffiles
|
||||
/etc/config/dockerd
|
||||
endef
|
||||
|
||||
define Package/dockerd/description
|
||||
The Docker CE Engine.
|
||||
endef
|
||||
|
||||
TAR_OPTIONS:=--strip-components 1 $(TAR_OPTIONS)
|
||||
TAR_CMD=$(HOST_TAR) -C $(1) $(TAR_OPTIONS)
|
||||
TARGET_LDFLAGS += $(if $(CONFIG_USE_GLIBC),-lc -lgcc_eh)
|
||||
|
||||
# $(1) = path to dependent package 'Makefile'
|
||||
# $(2) = relevant dependency '.installer' file
|
||||
define EnsureVendoredVersion
|
||||
( \
|
||||
DEP_VER=$$$$( grep --only-matching --perl-regexp '(?<=PKG_VERSION:=)(.*)' "$(1)" ); \
|
||||
VEN_VER=$$$$( grep --only-matching --perl-regexp '(?<=_VERSION:=v)(.*)(?=})' "$(PKG_BUILD_DIR)/hack/dockerfile/install/$(2)" ); \
|
||||
if [ "$$$${VEN_VER}" != "$$$${DEP_VER}" ]; then \
|
||||
echo "ERROR: Expected 'PKG_VERSION:=$$$${VEN_VER}' in '$(1)', found 'PKG_VERSION:=$$$${DEP_VER}'"; \
|
||||
exit 1; \
|
||||
fi \
|
||||
)
|
||||
endef
|
||||
|
||||
# $(1) = path to dependent package 'Makefile'
|
||||
# $(2) = relevant dependency '.installer' file
|
||||
define EnsureVendoredCommit
|
||||
( \
|
||||
DEP_VER=$$$$( grep --only-matching --perl-regexp '(?<=PKG_SOURCE_VERSION:=)(.*)' "$(1)" ); \
|
||||
VEN_VER=$$$$( grep --only-matching --perl-regexp '(?<=_COMMIT:=)(.*)(?=})' "$(PKG_BUILD_DIR)/hack/dockerfile/install/$(2)" ); \
|
||||
if [ "$$$${VEN_VER}" != "$$$${DEP_VER}" ]; then \
|
||||
echo "ERROR: Expected 'PKG_SOURCE_VERSION:=$$$${VEN_VER}' in '$(1)', found 'PKG_SOURCE_VERSION:=$$$${DEP_VER}'"; \
|
||||
exit 1; \
|
||||
fi \
|
||||
)
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
$(Build/Prepare/Default)
|
||||
|
||||
# Verify dependencies are the vendored version
|
||||
$(call EnsureVendoredVersion,../containerd/Makefile,containerd.installer)
|
||||
$(call EnsureVendoredVersion,../runc/Makefile,runc.installer)
|
||||
$(call EnsureVendoredVersion,../tini/Makefile,tini.installer)
|
||||
|
||||
# Verify CLI is the same version
|
||||
( \
|
||||
CLI_MAKEFILE="../docker/Makefile"; \
|
||||
CLI_VERSION=$$$$( grep --only-matching --perl-regexp '(?<=PKG_VERSION:=)(.*)' "$$$${CLI_MAKEFILE}" ); \
|
||||
if [ "$$$${CLI_VERSION}" != "$(PKG_VERSION)" ]; then \
|
||||
echo "ERROR: Expected 'PKG_VERSION:=$(PKG_VERSION)' in '$$$${CLI_MAKEFILE}', found 'PKG_VERSION:=$$$${CLI_VERSION}'"; \
|
||||
exit 1; \
|
||||
fi \
|
||||
)
|
||||
|
||||
# Verify PKG_GIT_SHORT_COMMIT
|
||||
( \
|
||||
EXPECTED_PKG_GIT_SHORT_COMMIT=$$$$( $(CURDIR)/git-short-commit.sh '$(PKG_GIT_URL)' '$(PKG_GIT_REF)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)' ); \
|
||||
if [ "$$$${EXPECTED_PKG_GIT_SHORT_COMMIT}" != "$(strip $(PKG_GIT_SHORT_COMMIT))" ]; then \
|
||||
echo "ERROR: Expected 'PKG_GIT_SHORT_COMMIT:=$$$${EXPECTED_PKG_GIT_SHORT_COMMIT}', found 'PKG_GIT_SHORT_COMMIT:=$(strip $(PKG_GIT_SHORT_COMMIT))'"; \
|
||||
exit 1; \
|
||||
fi \
|
||||
)
|
||||
endef
|
||||
|
||||
BUILDTAGS:=
|
||||
ifeq ($(KERNEL_SECCOMP),y)
|
||||
BUILDTAGS += seccomp
|
||||
endif
|
||||
ifeq ($(CONFIG_SELINUX),y)
|
||||
BUILDTAGS += selinux
|
||||
endif
|
||||
|
||||
define Build/Compile
|
||||
( \
|
||||
cd $(PKG_BUILD_DIR); \
|
||||
$(GO_PKG_VARS) \
|
||||
DOCKER_GITCOMMIT=$(PKG_GIT_SHORT_COMMIT) \
|
||||
DOCKER_BUILDTAGS='$(BUILDTAGS)' \
|
||||
VERSION=$(PKG_VERSION) \
|
||||
./hack/make.sh binary; \
|
||||
)
|
||||
endef
|
||||
|
||||
define Package/dockerd/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin/
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bundles/binary-daemon/dockerd $(1)/usr/bin/
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bundles/binary-daemon/docker-proxy $(1)/usr/bin/
|
||||
|
||||
$(INSTALL_DIR) $(1)/opt/docker/
|
||||
|
||||
ifeq ($(CONFIG_DOCKER_CHECK_CONFIG),y)
|
||||
$(INSTALL_DIR) $(1)/usr/share/docker/
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/contrib/check-config.sh $(1)/usr/share/docker/
|
||||
endif
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/dockerd.init $(1)/etc/init.d/dockerd
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/etc/config/dockerd $(1)/etc/config/dockerd
|
||||
|
||||
# Must be after systcl 11-br-netfilter.conf from kmod-br-netfilter
|
||||
$(INSTALL_DIR) $(1)/etc/sysctl.d
|
||||
$(INSTALL_DATA) ./files/etc/sysctl.d/sysctl-br-netfilter-ip.conf \
|
||||
$(1)/etc/sysctl.d/12-br-netfilter-ip.conf
|
||||
endef
|
||||
|
||||
define Package/dockerd/postinst
|
||||
#!/bin/sh
|
||||
[ -n "$$IPKG_INSTROOT" ] || {
|
||||
/etc/init.d/dockerd enable
|
||||
/etc/init.d/dockerd uciadd
|
||||
/etc/init.d/dockerd start
|
||||
}
|
||||
endef
|
||||
|
||||
define Package/dockerd/prerm
|
||||
#!/bin/sh
|
||||
[ -n "$$IPKG_INSTROOT" ] || {
|
||||
/etc/init.d/dockerd disable
|
||||
/etc/init.d/dockerd stop
|
||||
/etc/init.d/dockerd ucidel
|
||||
}
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,dockerd))
|
||||
Executable
+285
@@ -0,0 +1,285 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
USE_PROCD=1
|
||||
START=99
|
||||
|
||||
extra_command "uciadd" "<interface> <device> <zone> Add docker bridge configuration to network and firewall uci config"
|
||||
extra_command "ucidel" "<interface> <device> <zone> Delete docker bridge configuration from network and firewall uci config"
|
||||
|
||||
DOCKER_CONF_DIR="/tmp/dockerd"
|
||||
DOCKERD_CONF="${DOCKER_CONF_DIR}/daemon.json"
|
||||
|
||||
uci_quiet() {
|
||||
uci -q "${@}" >/dev/null
|
||||
}
|
||||
|
||||
json_add_array_string() {
|
||||
json_add_string "" "${1}"
|
||||
}
|
||||
|
||||
find_network_device() {
|
||||
local device="${1}"
|
||||
local device_section=""
|
||||
|
||||
check_device() {
|
||||
local cfg="${1}"
|
||||
local device="${2}"
|
||||
|
||||
local type name
|
||||
config_get type "${cfg}" type
|
||||
config_get name "${cfg}" name
|
||||
|
||||
[ "${type}" = "bridge" ] && [ "${name}" = "${device}" ] \
|
||||
&& device_section="${cfg}"
|
||||
}
|
||||
|
||||
config_load network
|
||||
config_foreach check_device device "${device}"
|
||||
|
||||
echo "${device_section}"
|
||||
}
|
||||
|
||||
boot() {
|
||||
uciadd
|
||||
rc_procd start_service
|
||||
}
|
||||
|
||||
uciadd() {
|
||||
local iface="${1}"
|
||||
local device="${2}"
|
||||
local zone="${3}"
|
||||
|
||||
[ -z "${iface}" ] && {
|
||||
iface="docker"
|
||||
device="docker0"
|
||||
zone="docker"
|
||||
}
|
||||
|
||||
/etc/init.d/dockerd running && {
|
||||
echo "Please stop dockerd service first"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Add network interface
|
||||
if ! uci_quiet get network.${iface}; then
|
||||
logger -t "dockerd-init" -p notice "Adding interface '${iface}' to network config"
|
||||
uci_quiet add network interface
|
||||
uci_quiet rename network.@interface[-1]="${iface}"
|
||||
uci_quiet set network.@interface[-1].device="${device}"
|
||||
uci_quiet set network.@interface[-1].proto="none"
|
||||
uci_quiet set network.@interface[-1].auto="0"
|
||||
uci_quiet commit network
|
||||
fi
|
||||
|
||||
# Add docker bridge device
|
||||
if [ "$(find_network_device "$device")" = "" ]; then
|
||||
logger -t "dockerd-init" -p notice "Adding bridge device '${device}' to network config"
|
||||
uci_quiet add network device
|
||||
uci_quiet set network.@device[-1].type="bridge"
|
||||
uci_quiet set network.@device[-1].name="${device}"
|
||||
uci_quiet commit network
|
||||
else
|
||||
logger -t "dockerd-init" -p notice "Bridge device '${device}' already defined in network config"
|
||||
fi
|
||||
|
||||
# Add firewall zone
|
||||
if ! uci_quiet get firewall.${zone}; then
|
||||
logger -t "dockerd-init" -p notice "Adding firewall zone '${zone}' to firewall config"
|
||||
uci_quiet add firewall zone
|
||||
uci_quiet rename firewall.@zone[-1]="${zone}"
|
||||
uci_quiet set firewall.@zone[-1].input="ACCEPT"
|
||||
uci_quiet set firewall.@zone[-1].output="ACCEPT"
|
||||
uci_quiet set firewall.@zone[-1].forward="ACCEPT"
|
||||
uci_quiet set firewall.@zone[-1].name="${zone}"
|
||||
uci_quiet commit firewall
|
||||
fi
|
||||
|
||||
# Add interface to firewall zone
|
||||
if uci_quiet get firewall.${zone}; then
|
||||
uci_quiet del_list firewall.${zone}.network="${iface}"
|
||||
uci_quiet add_list firewall.${zone}.network="${iface}"
|
||||
uci_quiet commit firewall
|
||||
fi
|
||||
|
||||
reload_config
|
||||
}
|
||||
|
||||
ucidel() {
|
||||
local iface="${1}"
|
||||
local device="${2}"
|
||||
local zone="${3}"
|
||||
|
||||
[ -z "${iface}" ] && {
|
||||
iface="docker"
|
||||
device="docker0"
|
||||
zone="docker"
|
||||
}
|
||||
|
||||
/etc/init.d/dockerd running && {
|
||||
echo "Please stop dockerd service first"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Remove network device
|
||||
if uci_quiet delete network.$(find_network_device "${device}"); then
|
||||
logger -t "dockerd-init" -p notice "Deleting bridge device '${device}' from network config"
|
||||
uci_quiet commit network
|
||||
fi
|
||||
|
||||
# Remove network interface
|
||||
if uci_quiet get network.${iface}; then
|
||||
logger -t "dockerd-init" -p notice "Deleting interface '${iface}' from network config"
|
||||
uci_quiet delete network.${iface}
|
||||
uci_quiet commit network
|
||||
fi
|
||||
|
||||
# Remove interface from firewall zone
|
||||
if uci_quiet get firewall.${zone}; then
|
||||
logger -t "dockerd-init" -p notice "Deleting network interface '${iface}' in zone '${zone}' from firewall config"
|
||||
uci_quiet del_list firewall.${zone}.network="${iface}"
|
||||
uci_quiet commit firewall
|
||||
# Remove Firewall zone if network is empty
|
||||
if ! uci_quiet get firewall.${zone}.network; then
|
||||
logger -t "dockerd-init" -p notice "Deleting firewall zone '${zone}' from firewall config"
|
||||
uci_quiet delete firewall.${zone}
|
||||
fi
|
||||
uci_quiet commit firewall
|
||||
fi
|
||||
|
||||
reload_config
|
||||
}
|
||||
|
||||
process_config() {
|
||||
local alt_config_file data_root log_level iptables bip
|
||||
|
||||
[ -f /etc/config/dockerd ] || {
|
||||
# Use the daemon default configuration
|
||||
DOCKERD_CONF=""
|
||||
return 0
|
||||
}
|
||||
|
||||
# reset configuration
|
||||
rm -fr "${DOCKER_CONF_DIR}"
|
||||
mkdir -p "${DOCKER_CONF_DIR}"
|
||||
|
||||
config_load 'dockerd'
|
||||
config_get alt_config_file globals alt_config_file
|
||||
[ -n "${alt_config_file}" ] && [ -f "${alt_config_file}" ] && {
|
||||
ln -s "${alt_config_file}" "${DOCKERD_CONF}"
|
||||
return 0
|
||||
}
|
||||
|
||||
config_get data_root globals data_root "/opt/docker/"
|
||||
config_get log_level globals log_level "warn"
|
||||
config_get_bool iptables globals iptables "1"
|
||||
|
||||
# Don't add these options by default
|
||||
# omission == docker defaults
|
||||
config_get log_driver globals log_driver ""
|
||||
config_get bip globals bip ""
|
||||
config_get registry_mirrors globals registry_mirrors ""
|
||||
config_get hosts globals hosts ""
|
||||
config_get dns globals dns ""
|
||||
config_get_bool ipv6 globals ipv6 ""
|
||||
config_get ip globals ip ""
|
||||
config_get fixed_cidr globals fixed_cidr ""
|
||||
config_get fixed_cidr_v6 globals fixed_cidr_v6 ""
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
json_init
|
||||
json_add_string "data-root" "${data_root}"
|
||||
json_add_string "log-level" "${log_level}"
|
||||
json_add_boolean "iptables" "${iptables}"
|
||||
[ -z "${log_driver}" ] || json_add_string "log-driver" "${log_driver}"
|
||||
[ -z "${bip}" ] || json_add_string "bip" "${bip}"
|
||||
[ -z "${registry_mirrors}" ] || json_add_array "registry-mirrors"
|
||||
[ -z "${registry_mirrors}" ] || config_list_foreach globals registry_mirrors json_add_array_string
|
||||
[ -z "${registry_mirrors}" ] || json_close_array
|
||||
[ -z "${hosts}" ] || json_add_array "hosts"
|
||||
[ -z "${hosts}" ] || config_list_foreach globals hosts json_add_array_string
|
||||
[ -z "${hosts}" ] || json_close_array
|
||||
[ -z "${dns}" ] || json_add_array "dns"
|
||||
[ -z "${dns}" ] || config_list_foreach globals dns json_add_array_string
|
||||
[ -z "${dns}" ] || json_close_array
|
||||
[ -z "${ipv6}" ] || json_add_boolean "ipv6" "${ipv6}"
|
||||
[ -z "${ip}" ] || json_add_string "ip" "${ip}"
|
||||
[ -z "${fixed_cidr}" ] || json_add_string "fixed-cidr" "${fixed_cidr}"
|
||||
[ -z "${fixed_cidr_v6}" ] || json_add_string "fixed-cidr-v6" "${fixed_cidr_v6}"
|
||||
json_dump > "${DOCKERD_CONF}"
|
||||
|
||||
[ "${iptables}" -eq "1" ] && config_foreach iptables_add_blocking_rule firewall
|
||||
}
|
||||
|
||||
start_service() {
|
||||
local nofile=$(cat /proc/sys/fs/nr_open)
|
||||
|
||||
process_config
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param stderr 1
|
||||
if [ -z "${DOCKERD_CONF}" ]; then
|
||||
procd_set_param command /usr/bin/dockerd
|
||||
else
|
||||
procd_set_param command /usr/bin/dockerd --config-file="${DOCKERD_CONF}"
|
||||
fi
|
||||
procd_set_param limits nofile="${nofile} ${nofile}"
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
process_config
|
||||
procd_send_signal dockerd
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger 'dockerd'
|
||||
}
|
||||
|
||||
iptables_add_blocking_rule() {
|
||||
local cfg="${1}"
|
||||
|
||||
local device=""
|
||||
local extra_iptables_args=""
|
||||
|
||||
handle_iptables_rule() {
|
||||
local interface="${1}"
|
||||
local outbound="${2}"
|
||||
local extra_iptables_args="${3}"
|
||||
|
||||
local inbound=""
|
||||
|
||||
. /lib/functions/network.sh
|
||||
network_get_physdev inbound "${interface}"
|
||||
|
||||
[ -z "${inbound}" ] && {
|
||||
logger -t "dockerd-init" -p notice "Unable to get physical device for interface ${interface}"
|
||||
return
|
||||
}
|
||||
|
||||
# Wait for a maximum of 10 second per command, retrying every millisecond
|
||||
local iptables_wait_args="--wait 10 --wait-interval 1000"
|
||||
|
||||
# Ignore errors as it might already be present
|
||||
iptables ${iptables_wait_args} --table filter --new DOCKER-USER 2>/dev/null
|
||||
if ! iptables ${iptables_wait_args} --table filter --check DOCKER-USER --in-interface "${inbound}" --out-interface "${outbound}" ${extra_iptables_args} --jump REJECT 2>/dev/null; then
|
||||
logger -t "dockerd-init" -p notice "Drop traffic from ${inbound} to ${outbound}"
|
||||
iptables ${iptables_wait_args} --table filter --insert DOCKER-USER --in-interface "${inbound}" --out-interface "${outbound}" ${extra_iptables_args} --jump REJECT
|
||||
fi
|
||||
}
|
||||
|
||||
config_get device "${cfg}" device
|
||||
|
||||
[ -z "${device}" ] && {
|
||||
logger -t "dockerd-init" -p notice "No device configured for ${cfg}"
|
||||
return
|
||||
}
|
||||
|
||||
config_get extra_iptables_args "${cfg}" extra_iptables_args
|
||||
config_list_foreach "${cfg}" blocked_interfaces handle_iptables_rule "${device}" "${extra_iptables_args}"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
if /etc/init.d/dockerd running; then
|
||||
service_stop "/usr/bin/dockerd"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# The following settings require a restart of docker to take full effect, A reload will only have partial or no effect:
|
||||
# log_driver
|
||||
# bip
|
||||
# blocked_interfaces
|
||||
# extra_iptables_args
|
||||
# device
|
||||
|
||||
config globals 'globals'
|
||||
# option alt_config_file '/etc/docker/daemon.json'
|
||||
option data_root '/opt/docker/'
|
||||
# option log_driver 'local'
|
||||
option log_level 'warn'
|
||||
option iptables '1'
|
||||
# list hosts 'unix:///var/run/docker.sock'
|
||||
# option bip '172.18.0.1/24'
|
||||
# option fixed_cidr '172.17.0.0/16'
|
||||
# option fixed_cidr_v6 'fc00:1::/80'
|
||||
# option ipv6 '1'
|
||||
# option ip '::ffff:0.0.0.0'
|
||||
# list dns '172.17.0.1'
|
||||
# list registry_mirrors 'https://<my-docker-mirror-host>'
|
||||
# list registry_mirrors 'https://hub.docker.com'
|
||||
|
||||
# Docker doesn't work well out of the box with fw4. This is because Docker relies on a compatibility layer that
|
||||
# naively translates iptables rules. For the best compatibility replace the following dependencies:
|
||||
# `firewall4` -> `firewall`
|
||||
# `iptables-nft` -> `iptables-legacy`
|
||||
# `ip6tables-nft` -> `ip6tables-legacy`
|
||||
|
||||
# Docker undermines the fw3 rules. By default all external source IPs are allowed to connect to the Docker host.
|
||||
# See https://docs.docker.com/network/iptables/ for more details.
|
||||
|
||||
# firewall config changes are only additive i.e firewall will need to be restarted first to clear old changes,
|
||||
# then docker restarted to load in new changes.
|
||||
config firewall 'firewall'
|
||||
option device 'docker0'
|
||||
list blocked_interfaces 'wan'
|
||||
# option extra_iptables_args '--match conntrack ! --ctstate RELATED,ESTABLISHED' # allow outbound connections
|
||||
@@ -0,0 +1,7 @@
|
||||
# Do not edit, changes to this file will be lost on upgrades
|
||||
# /etc/sysctl.conf can be used to customize sysctl settings
|
||||
|
||||
# enable bridge firewalling for docker
|
||||
net.bridge.bridge-nf-call-ip6tables=1
|
||||
net.bridge.bridge-nf-call-iptables=1
|
||||
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
|
||||
#!/bin/sh
|
||||
#
|
||||
# USAGE: git-short-commit.sh <GIT_URL> <GIT_REF> <GIT_DIR>
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
error() {
|
||||
echo "ERROR: ${*}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
GIT_URL="${1}"
|
||||
if [ -z "${GIT_URL}" ]; then
|
||||
error "Git URL not specified"
|
||||
fi
|
||||
|
||||
GIT_REF="${2}"
|
||||
if [ -z "${GIT_REF}" ]; then
|
||||
error "Git reference not specified"
|
||||
fi
|
||||
|
||||
# Remove docker- prefix if present (e.g., docker-v29.2.1 -> v29.2.1)
|
||||
GIT_REF=$(echo "$GIT_REF" | sed 's/^docker-//')
|
||||
|
||||
GIT_DIR="${3}"
|
||||
if [ -z "${GIT_DIR}" ]; then
|
||||
error "Git clone directory not specified"
|
||||
fi
|
||||
|
||||
clean_up() {
|
||||
rm -rf "${GIT_DIR}"
|
||||
}
|
||||
trap clean_up EXIT
|
||||
|
||||
git init --quiet "${GIT_DIR}"
|
||||
(
|
||||
cd "${GIT_DIR}"
|
||||
for PREFIX in "" "https://" "http://" "git@"; do
|
||||
echo "Trying remote '${PREFIX}${GIT_URL}'" >&2
|
||||
git remote add origin "${PREFIX}${GIT_URL}"
|
||||
|
||||
if git fetch --depth 1 origin "${GIT_REF}"; then
|
||||
git checkout --detach FETCH_HEAD --
|
||||
git rev-parse --short HEAD
|
||||
break
|
||||
fi
|
||||
|
||||
git remote remove origin
|
||||
done
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=geoview
|
||||
PKG_VERSION:=0.2.6
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/snowie2000/geoview/tar.gz/$(PKG_VERSION)?
|
||||
PKG_HASH:=1b46d4255ab22ec433a14f804e9487885cc2693323579c1385d51919838db132
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=snowie2000
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
GO_PKG:=github.com/snowie2000/geoview
|
||||
GO_PKG_BUILD_PKG:=$(GO_PKG)
|
||||
|
||||
GO_PKG_LDFLAGS:=-s -w
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/geoview
|
||||
TITLE:=A geofile toolkit
|
||||
URL:=https://github.com/snowie2000/geoview
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
DEPENDS+= $(GO_ARCH_DEPENDS)
|
||||
endef
|
||||
|
||||
define Package/geoview/description
|
||||
geoview is a handy tool to extract useful information from geo* files.
|
||||
endef
|
||||
|
||||
define Package/geoview/install
|
||||
$(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR))
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/geoview $(1)/usr/bin/
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,geoview))
|
||||
$(eval $(call BuildPackage,geoview))
|
||||
@@ -0,0 +1,58 @@
|
||||
#
|
||||
# Copyright (C) 2018 Sartura Ltd.
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=go-ethereum
|
||||
PKG_VERSION:=1.10.20
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/ethereum/go-ethereum/tar.gz/v${PKG_VERSION}?
|
||||
PKG_HASH:=15ff54f0a4444eb9faa7c1f6219d3a1db5d547178b4eef6679bb601abc681f9d
|
||||
|
||||
PKG_MAINTAINER:=Mislav Novakovic <mislav.novakovic@sartura.hr>
|
||||
PKG_LICENSE:=GPL-3.0-or-later LGPL-3.0-or-later
|
||||
PKG_LICENSE_FILES:=COPYING COPYING.LESSER
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
PKG_CONFIG_DEPENDS:=CONFIG_BUILD_NLS
|
||||
|
||||
GO_PKG:=github.com/ethereum/go-ethereum
|
||||
GO_PKG_BUILD_PKG:=github.com/ethereum/go-ethereum/cmd/geth
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/nls.mk
|
||||
include ../../lang/golang/golang-package.mk
|
||||
|
||||
define Package/geth
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Ethereum Go client
|
||||
URL:=https://geth.ethereum.org/
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) $(ICONV_DEPENDS)
|
||||
endef
|
||||
|
||||
define Package/geth/description
|
||||
Ethereum is a decentralized platform that runs smart contracts, applications
|
||||
that run exactly as programmed without possibility of downtime, censorship,
|
||||
fraud or third party interference.
|
||||
endef
|
||||
|
||||
TARGET_LDFLAGS += $(if $(ICONV_FULL),-liconv)
|
||||
|
||||
define Package/geth/install
|
||||
$(call GoPackage/Package/Install/Bin,$(1))
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d/
|
||||
$(INSTALL_BIN) ./files/geth.init $(1)/etc/init.d/geth
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,geth))
|
||||
$(eval $(call BuildPackage,geth))
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=90
|
||||
STOP=10
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/geth
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command ${PROG}
|
||||
procd_append_param command --syncmode "light" --cache 1024
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2022 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gn
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git
|
||||
PKG_SOURCE_DATE:=2026-05-21
|
||||
PKG_SOURCE_VERSION:=e44942b445d9376451f7d519965dae036efde5bc
|
||||
PKG_MIRROR_HASH:=8d41089dd918aa94a5f8ab6ac78b056220e353755e5b97f2f2cc70ff634c1f1c
|
||||
|
||||
PKG_LICENSE:=BSD 3-Clause
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
|
||||
PKG_HOST_ONLY:=1
|
||||
HOST_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/host-build.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/gn
|
||||
SECTION:=devel
|
||||
CATEGORY:=Development
|
||||
TITLE:=A meta-build system that generates build files for Ninja
|
||||
URL:=https://gn.googlesource.com/gn/
|
||||
BUILDONLY:=1
|
||||
endef
|
||||
|
||||
define Package/gn/description
|
||||
GN can generate Ninja build files for C, C++, Rust, Objective C,
|
||||
and Swift source on most popular platforms.
|
||||
endef
|
||||
|
||||
define Host/Configure
|
||||
$(PYTHON) $(HOST_BUILD_DIR)/build/gen.py \
|
||||
--no-last-commit-position \
|
||||
--allow-warnings
|
||||
endef
|
||||
|
||||
define Host/Compile
|
||||
+$(NINJA) -C $(HOST_BUILD_DIR)/out
|
||||
endef
|
||||
|
||||
define Host/Install
|
||||
$(INSTALL_DIR) $(STAGING_DIR_HOSTPKG)/bin/
|
||||
$(INSTALL_BIN) $(HOST_BUILD_DIR)/out/gn $(STAGING_DIR_HOSTPKG)/bin/
|
||||
endef
|
||||
|
||||
define Host/Clean
|
||||
$(RM) $(STAGING_DIR_HOSTPKG)/bin/gn
|
||||
endef
|
||||
|
||||
$(eval $(call HostBuild))
|
||||
$(eval $(call BuildPackage,gn))
|
||||
@@ -0,0 +1,9 @@
|
||||
// Generated by build/gen.py.
|
||||
|
||||
#ifndef OUT_LAST_COMMIT_POSITION_H_
|
||||
#define OUT_LAST_COMMIT_POSITION_H_
|
||||
|
||||
#define LAST_COMMIT_POSITION_NUM 2406
|
||||
#define LAST_COMMIT_POSITION "2406 (e44942b445d9)"
|
||||
|
||||
#endif // OUT_LAST_COMMIT_POSITION_H_
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
#
|
||||
# Copyright (C) 2007-2015 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gnurl
|
||||
PKG_VERSION:=7.72.0
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=@GNU/gnunet
|
||||
PKG_HASH:=b1e72be03fcf9b3fdaf01ae0702c686e9de10f67ef538918447c9e81555694f8
|
||||
|
||||
PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
PKG_FIXUP:=autoreconf
|
||||
PKG_INSTALL:=1
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/gnurl/Default
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
URL:=https://gnunet.org/en/gnurl.html
|
||||
endef
|
||||
|
||||
define Package/gnurl
|
||||
$(call Package/gnurl/Default)
|
||||
SUBMENU:=File Transfer
|
||||
DEPENDS:=+libgnurl
|
||||
TITLE:=A client-side HTTP/HTTPS transfer utility
|
||||
endef
|
||||
|
||||
define Package/libgnurl
|
||||
$(call Package/gnurl/Default)
|
||||
SECTION:=libs
|
||||
CATEGORY:=Libraries
|
||||
DEPENDS:=+libgnutls +libidn2 +zlib
|
||||
TITLE:=A client-side HTTP/HTTPS transfer library
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += $(FPIC)
|
||||
|
||||
CONFIGURE_ARGS += \
|
||||
$(call autoconf_bool,CONFIG_IPV6,ipv6) \
|
||||
--with-gnutls="$(STAGING_DIR)/usr" \
|
||||
--with-libidn="$(STAGING_DIR)/usr" \
|
||||
--with-zlib="$(STAGING_DIR)/usr" \
|
||||
--with-ca-path="/etc/ssl/certs/" \
|
||||
--enable-shared \
|
||||
--enable-static \
|
||||
--without-axtls \
|
||||
--without-brotli \
|
||||
--without-libssh2 \
|
||||
--without-libmetalink \
|
||||
--without-winidn \
|
||||
--without-librtmp \
|
||||
--without-nghttp2 \
|
||||
--without-nss \
|
||||
--without-cyassl \
|
||||
--without-libpsl \
|
||||
--without-polarssl \
|
||||
--without-ssl \
|
||||
--without-winssl \
|
||||
--without-darwinssl \
|
||||
--without-zstd \
|
||||
--disable-ares \
|
||||
--disable-sspi \
|
||||
--disable-crypto-auth \
|
||||
--disable-ntlm-wb \
|
||||
--disable-tls-srp \
|
||||
--disable-ldap \
|
||||
--disable-ldaps \
|
||||
--disable-rtsp \
|
||||
--disable-dict \
|
||||
--disable-telnet \
|
||||
--disable-tftp \
|
||||
--disable-pop3 \
|
||||
--disable-imap \
|
||||
--disable-smtp \
|
||||
--disable-gopher \
|
||||
--disable-file \
|
||||
--disable-ftp \
|
||||
--disable-smb \
|
||||
--disable-debug \
|
||||
--disable-manual \
|
||||
--disable-verbose
|
||||
|
||||
define Build/InstallDev
|
||||
$(INSTALL_DIR) $(2)/bin $(1)/usr/bin $(1)/usr/include/gnurl $(1)/usr/lib $(1)/usr/lib/pkgconfig
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/gnurl-config $(1)/usr/bin/
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/include/gnurl/*.h $(1)/usr/include/gnurl
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libgnurl.{a,so*} $(1)/usr/lib/
|
||||
$(CP) $(PKG_BUILD_DIR)/libgnurl.pc $(1)/usr/lib/pkgconfig/
|
||||
$(SED) 's,^\(prefix\|exec_prefix\)=.*,\1=$(STAGING_DIR)/usr,g' $(1)/usr/bin/gnurl-config
|
||||
[ -n "$(TARGET_LDFLAGS)" ] && $(SED) 's#$(TARGET_LDFLAGS)##g' $(1)/usr/lib/pkgconfig/libgnurl.pc || true
|
||||
$(LN) $(STAGING_DIR)/usr/bin/gnurl-config $(2)/bin/
|
||||
endef
|
||||
|
||||
define Package/gnurl/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/gnurl $(1)/usr/bin/
|
||||
endef
|
||||
|
||||
define Package/libgnurl/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libgnurl.so.* $(1)/usr/lib/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gnurl))
|
||||
$(eval $(call BuildPackage,libgnurl))
|
||||
@@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gost
|
||||
PKG_VERSION:=3.2.6
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/go-gost/gost/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=79874354530b899576dd4866d3b1400651d0b17c1e7a90ad30c44686a0642600
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
GO_PKG:=github.com/go-gost/gost
|
||||
GO_PKG_BUILD_PKG:=$(GO_PKG)/cmd/gost
|
||||
GO_PKG_LDFLAGS_X:=main.version=$(PKG_VERSION)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../../lang/golang/golang-package.mk
|
||||
|
||||
define Package/gost
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=GO Simple Tunnel
|
||||
URL:=https://gost.run/
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
USERID:=gost:gost
|
||||
endef
|
||||
|
||||
define Package/gost/conffiles
|
||||
/etc/config/gost
|
||||
/etc/gost/
|
||||
endef
|
||||
|
||||
define Package/gost/install
|
||||
$(call GoPackage/Package/Install/Bin,$(1))
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) $(CURDIR)/files/gost.config $(1)/etc/config/gost
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) $(CURDIR)/files/gost.init $(1)/etc/init.d/gost
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) $(CURDIR)/files/gost.uci $(1)/etc/uci-defaults/99-gost-migrate
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,gost))
|
||||
$(eval $(call BuildPackage,gost))
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
config gost 'config'
|
||||
option enabled '0'
|
||||
option config_file ''
|
||||
#list arguments ''
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
USE_PROCD=1
|
||||
START=99
|
||||
|
||||
CONF="gost"
|
||||
PROG="/usr/bin/gost"
|
||||
|
||||
start_service() {
|
||||
local enabled
|
||||
|
||||
config_load "gost"
|
||||
config_get_bool enabled "config" "enabled" 0
|
||||
[ "$enabled" -eq 1 ] || return 1
|
||||
|
||||
local config_file arguments
|
||||
config_get config_file "config" "config_file"
|
||||
config_get arguments "config" "arguments"
|
||||
|
||||
procd_open_instance
|
||||
|
||||
procd_set_param command "$PROG"
|
||||
if [ -n "$config_file" ]; then
|
||||
procd_append_param command -C "$config_file"
|
||||
procd_set_param file "$config_file"
|
||||
fi
|
||||
if [ -n "$arguments" ]; then
|
||||
set -- $arguments
|
||||
procd_append_param command "$@"
|
||||
fi
|
||||
|
||||
procd_set_param user gost
|
||||
procd_set_param group gost
|
||||
|
||||
procd_set_param limits core="unlimited"
|
||||
procd_set_param limits nofile="1000000 1000000"
|
||||
procd_set_param stderr 1
|
||||
|
||||
procd_set_param respawn
|
||||
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "$CONF"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
grep -q "config gost$" "/etc/config/gost" || exit 0
|
||||
|
||||
sed -e "s,config gost,config gost 'config'," \
|
||||
-e "s,option enable ,option enabled ," \
|
||||
-e "s,option run_command,list arguments," \
|
||||
-i "/etc/config/gost"
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,56 @@
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gowebdav
|
||||
PKG_VERSION:=5.7.4
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=5462eaf18292727e7af66d77c1723f729c97a31d4dfe4b47343f0644a5e63454
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=ElonH <elonhhuang@gmail.com>
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/webdav-$(PKG_VERSION)
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
|
||||
GO_PKG:=github.com/hacdias/webdav
|
||||
GO_PKG_LDFLAGS:=-s -w
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../../lang/golang/golang-package.mk
|
||||
|
||||
define Package/gowebdav
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=File Transfer
|
||||
TITLE:=Simple Go WebDAV server
|
||||
URL:=https://github.com/hacdias/webdav
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS)
|
||||
endef
|
||||
|
||||
define Package/gowebdav/description
|
||||
Webdav command line interface is really easy to use so you can easily create
|
||||
a WebDAV server for your own user. By default, it runs on a random free port
|
||||
and supports JSON, YAML and TOML configuration.
|
||||
endef
|
||||
|
||||
define Package/gowebdav/install
|
||||
$(call GoPackage/Package/Install/Bin,$(1))
|
||||
|
||||
# $(INSTALL_DIR) $(1)/etc/config/ $(1)/etc/init.d/ $(1)/etc/webdav/
|
||||
# $(INSTALL_BIN) $(CURDIR)/files/webdav.init $(1)/etc/init.d/webdav
|
||||
# $(INSTALL_CONF) $(CURDIR)/files/webdav.config $(1)/etc/config/webdav
|
||||
# $(INSTALL_CONF) $(CURDIR)/files/webdav.yaml $(1)/etc/webdav/webdav.yaml
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,gowebdav))
|
||||
$(eval $(call BuildPackage,gowebdav))
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
config webdav 'config'
|
||||
option enabled '0'
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2021 ImmortalWrt
|
||||
|
||||
START=90
|
||||
STOP=10
|
||||
|
||||
WEBDAV="/usr/bin/webdav"
|
||||
|
||||
WEBDAVCONFIG="/etc/webdav/webdav.yaml"
|
||||
|
||||
start() {
|
||||
local enabled="$(uci get webdav.config.enabled)"
|
||||
stop
|
||||
[ "${enabled}" == "1" ] || exit 0
|
||||
export HOME="/root/"
|
||||
"${WEBDAV}" -c "${WEBDAVCONFIG}" >/dev/null 2>&1 &
|
||||
}
|
||||
|
||||
stop() {
|
||||
killall -9 pgrep "${WEBDAV}" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Server related settings
|
||||
address: 0.0.0.0
|
||||
port: 10880
|
||||
auth: true
|
||||
tls: false
|
||||
cert: cert.pem
|
||||
key: key.pem
|
||||
prefix: /
|
||||
|
||||
# Default user settings (will be merged)
|
||||
scope: .
|
||||
modify: true
|
||||
rules: []
|
||||
|
||||
# CORS configuration
|
||||
cors:
|
||||
enabled: true
|
||||
credentials: true
|
||||
allowed_headers:
|
||||
- Depth
|
||||
allowed_hosts:
|
||||
- http://localhost:8999
|
||||
allowed_methods:
|
||||
- GET
|
||||
exposed_headers:
|
||||
- Content-Length
|
||||
- Content-Range
|
||||
|
||||
users:
|
||||
- username: video
|
||||
password: 123
|
||||
scope: /mnt/sdb1
|
||||
modify: true
|
||||
rules:
|
||||
- regex: false
|
||||
allow: false
|
||||
path: /some/file
|
||||
@@ -0,0 +1,63 @@
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=headscale
|
||||
PKG_VERSION:=0.21.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/juanfont/headscale/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=2afbdc038508cb694be496db9ba6b63bbc611b7038e8299e60eef0f1b227f12f
|
||||
|
||||
PKG_LICENSE:=BSD-3-clause
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
|
||||
GO_PKG:=github.com/juanfont/headscale
|
||||
GO_PKG_BUILD_PKG:=$(GO_PKG)/cmd/headscale
|
||||
GO_PKG_LDFLAGS:=-s -w
|
||||
GO_PKG_LDFLAGS+= \
|
||||
-X '$(GO_PKG_BUILD_PKG)/cli.Version=v$(PKG_VERSION)'
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../../lang/golang/golang-package.mk
|
||||
|
||||
define Package/headscale
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=VPN
|
||||
TITLE:=A Tailscale control server
|
||||
URL:=https://github.com/juanfont/headscale
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS)
|
||||
endef
|
||||
|
||||
define Package/headscale/description
|
||||
An open source, self-hosted implementation of the Tailscale control server.
|
||||
endef
|
||||
|
||||
define Package/headscale/conffiles
|
||||
/etc/headscale/config.yaml
|
||||
/etc/headscale/db.sqlite
|
||||
/etc/headscale/derp.yaml
|
||||
/etc/headscale/noise_private.key
|
||||
/etc/headscale/private.key
|
||||
endef
|
||||
|
||||
define Package/headscale/install
|
||||
$(call GoPackage/Package/Install/Bin,$(1))
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/headscale
|
||||
touch $(1)/etc/headscale/db.sqlite
|
||||
$(INSTALL_CONF) $(PKG_BUILD_DIR)/config-example.yaml $(1)/etc/headscale/config.yaml
|
||||
$(INSTALL_CONF) $(PKG_BUILD_DIR)/derp-example.yaml $(1)/etc/headscale/derp.yaml
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,headscale))
|
||||
$(eval $(call BuildPackage,headscale))
|
||||
@@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=hysteria
|
||||
PKG_VERSION:=2.9.2
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/apernet/hysteria/tar.gz/app/v$(PKG_VERSION)?
|
||||
PKG_HASH:=d3d0e7b2e49f0cebb861fb215aa1625e4efa895a902d768db7ea45227d88b555
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-app-v$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
GO_PKG:=github.com/apernet/hysteria
|
||||
GO_PKG_BUILD_PKG:=$(GO_PKG)/app/v2
|
||||
GO_PKG_LDFLAGS_X = \
|
||||
$(GO_PKG)/app/v2/cmd.appVersion=v$(PKG_VERSION) \
|
||||
$(GO_PKG)/app/v2/cmd.appType=release \
|
||||
$(GO_PKG)/app/v2/cmd.appPlatform=$(GO_OS) \
|
||||
$(GO_PKG)/app/v2/cmd.appArch=$(GO_ARCH)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
|
||||
|
||||
define Package/hysteria
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=A feature-packed network utility optimized for networks of poor quality
|
||||
URL:=https://github.com/tobyxdd/hysteria
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
|
||||
endef
|
||||
|
||||
define Package/hysteria/description
|
||||
Hysteria is a feature-packed network utility optimized for networks
|
||||
of poor quality (e.g. satellite connections, congested public Wi-Fi,
|
||||
connecting from China to servers abroad) powered by a custom version
|
||||
of QUIC protocol.
|
||||
endef
|
||||
|
||||
define Package/hysteria/install
|
||||
$(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR))
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/app $(1)/usr/bin/hysteria
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,hysteria))
|
||||
$(eval $(call BuildPackage,hysteria))
|
||||
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# (с) 2026 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector-mod-email
|
||||
PKG_VERSION:=1.7.3
|
||||
PKG_RELEASE:=1
|
||||
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/modules
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_email.lua $(1)/usr/lib/lua/internet-detector/modules/mod_email.lua
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
--[[
|
||||
Dependences:
|
||||
mailsend
|
||||
--]]
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_email",
|
||||
runPrio = 60,
|
||||
config = {
|
||||
debug = false,
|
||||
},
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
deadPeriod = 0,
|
||||
alivePeriod = 0,
|
||||
mode = 0, -- 0: connected, 1: disconnected, 2: both
|
||||
hostAlias = "OpenWrt",
|
||||
mta = "/usr/bin/mailsend",
|
||||
mtaConnectTimeout = 5,
|
||||
mtaReadTimeout = 5,
|
||||
mailRecipient = nil,
|
||||
mailSender = nil,
|
||||
mailUser = nil,
|
||||
mailPassword = nil,
|
||||
mailSmtp = nil,
|
||||
mailSmtpPort = nil,
|
||||
mailSecurity = "tls",
|
||||
msgTextPattern = "[%s] (%s) @ %s", -- Message (host, instance, message)
|
||||
msgSubPattern = "%s notification", -- Subject (host)
|
||||
msgConnectPattern = "Connected: %s",
|
||||
msgDisconnectPattern = "Disconnected: %s",
|
||||
msgSeparator = " | ",
|
||||
msgMaxItems = 50,
|
||||
msgSendAttempts = 3,
|
||||
msgSendTimeout = 5,
|
||||
status = nil,
|
||||
_enabled = false,
|
||||
_deadCounter = 0,
|
||||
_aliveCounter = 0,
|
||||
_msgSentDisconnect = true,
|
||||
_disconnected = true,
|
||||
_msgSentConnect = true,
|
||||
_connected = true,
|
||||
_msgBuffer = {},
|
||||
_msgSendCounter = 3,
|
||||
_msgTimeoutCounter = 5,
|
||||
}
|
||||
|
||||
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)
|
||||
end
|
||||
if t.host_alias then
|
||||
self.hostAlias = t.host_alias
|
||||
else
|
||||
self.hostAlias = self.config.hostname
|
||||
end
|
||||
|
||||
self.mailRecipient = t.mail_recipient
|
||||
self.mailSender = t.mail_sender
|
||||
self.mailUser = t.mail_user
|
||||
self.mailPassword = t.mail_password
|
||||
self.mailSmtp = t.mail_smtp
|
||||
self.mailSmtpPort = t.mail_smtp_port
|
||||
|
||||
if t.mail_security ~= nil then
|
||||
self.mailSecurity = t.mail_security
|
||||
end
|
||||
if tonumber(t.message_at_startup) == 1 then
|
||||
self._msgSentDisconnect = false
|
||||
self._disconnected = false
|
||||
self._msgSentConnect = false
|
||||
self._connected = false
|
||||
end
|
||||
|
||||
if unistd.access(self.mta, "x") then
|
||||
self._enabled = true
|
||||
else
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format("%s: %s is not available", self.name, self.mta))
|
||||
end
|
||||
|
||||
if (not self.mailRecipient or
|
||||
not self.mailSender or
|
||||
not self.mailUser or
|
||||
not self.mailPassword or
|
||||
not self.mailSmtp or
|
||||
not self.mailSmtpPort) then
|
||||
self._enabled = false
|
||||
self.syslog("warning", string.format(
|
||||
"%s: Insufficient data to connect to the SMTP server", self.name))
|
||||
end
|
||||
|
||||
self._msgSendCounter = self.msgSendAttempts
|
||||
end
|
||||
|
||||
function Module:appendNotice(str)
|
||||
self._msgBuffer[#self._msgBuffer + 1] = str
|
||||
if #self._msgBuffer > self.msgMaxItems then
|
||||
local t = {}
|
||||
for i = #self._msgBuffer - self.msgMaxItems + 1, #self._msgBuffer do
|
||||
t[#t + 1] = self._msgBuffer[i]
|
||||
end
|
||||
self._msgBuffer = t
|
||||
end
|
||||
end
|
||||
|
||||
function Module:sendMessage(msg, textPattern)
|
||||
local retVal = 1
|
||||
local verboseArg = ""
|
||||
local emailMsg = string.format(
|
||||
textPattern, self.hostAlias, self.config.serviceConfig.instance, msg)
|
||||
|
||||
-- Debug
|
||||
if self.config.debug then
|
||||
verboseArg = " -v"
|
||||
end
|
||||
self.debugOutput(string.format("--- %s ---", self.name))
|
||||
|
||||
local securityArgs = "-starttls -auth-login"
|
||||
if self.mailSecurity == "ssl" then
|
||||
securityArgs = "-ssl -auth"
|
||||
end
|
||||
|
||||
local mtaCmd = string.format(
|
||||
'%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.mtaConnectTimeout, self.mtaReadTimeout,
|
||||
self.mailUser, self.mailPassword, self.mailSender, self.mailRecipient,
|
||||
string.format(self.msgSubPattern, self.hostAlias),
|
||||
emailMsg)
|
||||
|
||||
-- Debug
|
||||
self.debugOutput(string.format("%s: %s", self.name, mtaCmd))
|
||||
self.syslog("debug", string.format("%s: %s", self.name, mtaCmd))
|
||||
|
||||
retVal = os.execute(mtaCmd)
|
||||
if retVal == 0 then
|
||||
self.syslog("info", string.format(
|
||||
"%s: Message sent to %s", self.name, self.mailRecipient))
|
||||
self._msgBuffer = {}
|
||||
else
|
||||
self.syslog("err", string.format(
|
||||
"%s: An error occured while sending message", self.name))
|
||||
end
|
||||
|
||||
return retVal
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if not self._enabled then
|
||||
return
|
||||
end
|
||||
|
||||
if currentStatus == 1 then
|
||||
self._aliveCounter = 0
|
||||
self._msgSentConnect = false
|
||||
|
||||
if not self._disconnected then
|
||||
self._disconnected = true
|
||||
self:appendNotice(string.format(
|
||||
self.msgDisconnectPattern, os.date("%Y.%m.%d %H:%M:%S", os.time())))
|
||||
end
|
||||
|
||||
if not self._msgSentDisconnect and (self.mode == 1 or self.mode == 2) then
|
||||
if self._deadCounter >= self.deadPeriod then
|
||||
self._msgSendCounter = 0
|
||||
self._msgSentDisconnect = true
|
||||
else
|
||||
self._deadCounter = self._deadCounter + timeDiff
|
||||
end
|
||||
end
|
||||
self._connected = false
|
||||
elseif currentStatus == 0 then
|
||||
self._deadCounter = 0
|
||||
self._msgSentDisconnect = false
|
||||
|
||||
if not self._connected then
|
||||
self._connected = true
|
||||
self:appendNotice(string.format(
|
||||
self.msgConnectPattern, os.date("%Y.%m.%d %H:%M:%S", os.time())))
|
||||
end
|
||||
|
||||
if not self._msgSentConnect and (self.mode == 0 or self.mode == 2) then
|
||||
if self._aliveCounter >= self.alivePeriod then
|
||||
self._msgSendCounter = 0
|
||||
self._msgSentConnect = true
|
||||
else
|
||||
self._aliveCounter = self._aliveCounter + timeDiff
|
||||
end
|
||||
end
|
||||
self._disconnected = false
|
||||
end
|
||||
|
||||
if self._msgSendCounter < self.msgSendAttempts then
|
||||
if self._msgTimeoutCounter >= self.msgSendTimeout then
|
||||
if #self._msgBuffer > 0 then
|
||||
if self:sendMessage(table.concat(self._msgBuffer, self.msgSeparator), self.msgTextPattern) == 0 then
|
||||
self._msgSendCounter = self.msgSendAttempts
|
||||
else
|
||||
self._msgSendCounter = self._msgSendCounter + 1
|
||||
end
|
||||
end
|
||||
self._msgTimeoutCounter = 0
|
||||
else
|
||||
self._msgTimeoutCounter = self._msgTimeoutCounter + timeDiff
|
||||
end
|
||||
else
|
||||
self._msgTimeoutCounter = self.msgSendTimeout
|
||||
end
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# (с) 2026 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector-mod-modem-restart
|
||||
PKG_VERSION:=1.7.3
|
||||
PKG_RELEASE:=1
|
||||
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/modules
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_modem_restart.lua $(1)/usr/lib/lua/internet-detector/modules/mod_modem_restart.lua
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
--[[
|
||||
Dependences:
|
||||
modemmanager
|
||||
--]]
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_modem_restart",
|
||||
runPrio = 40,
|
||||
config = {},
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
mmcli = "/usr/bin/mmcli",
|
||||
mmInit = "/etc/init.d/modemmanager",
|
||||
deadPeriod = 600,
|
||||
attempts = 1,
|
||||
attemptInterval = 15,
|
||||
ifaceTimeout = 0,
|
||||
iface = nil,
|
||||
anyBand = false,
|
||||
status = nil,
|
||||
_enabled = false,
|
||||
_attemptsCounter = 0,
|
||||
_attemptIntervalCounter = 0,
|
||||
_deadCounter = 0,
|
||||
_firstAttempt = true,
|
||||
_ifaceRestarting = false,
|
||||
_ifaceRestartCounter = 0,
|
||||
_disconnectedAtStartup = false,
|
||||
}
|
||||
|
||||
function Module:toggleIface(flag)
|
||||
if not self.iface then
|
||||
return
|
||||
end
|
||||
return os.execute(
|
||||
string.format("%s %s", (flag and "/sbin/ifup" or "/sbin/ifdown"), self.iface)
|
||||
)
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
if t.dead_period ~= nil then
|
||||
self.deadPeriod = tonumber(t.dead_period)
|
||||
end
|
||||
if t.attempts ~= nil then
|
||||
self.attempts = tonumber(t.attempts)
|
||||
end
|
||||
if t.attempt_interval ~= nil then
|
||||
self.attemptInterval = tonumber(t.attempt_interval)
|
||||
end
|
||||
if t.iface ~= nil then
|
||||
self.iface = t.iface
|
||||
end
|
||||
if t.iface_timeout ~= nil then
|
||||
self.ifaceTimeout = tonumber(t.iface_timeout)
|
||||
end
|
||||
if t.any_band ~= nil then
|
||||
self.anyBand = (tonumber(t.any_band) ~= 0)
|
||||
end
|
||||
if tonumber(t.disconnected_at_startup) == 1 then
|
||||
self._disconnectedAtStartup = true
|
||||
end
|
||||
|
||||
if not unistd.access(self.mmcli, "x") then
|
||||
self.anyBand = false
|
||||
end
|
||||
|
||||
if (unistd.access(self.mmInit, "x")
|
||||
and os.execute(string.format("%s enabled", self.mmInit)) == 0) then
|
||||
self._enabled = true
|
||||
else
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format(
|
||||
"%s: modemmanager service is not available", self.name))
|
||||
end
|
||||
end
|
||||
|
||||
function Module:restartMM()
|
||||
if os.execute(string.format("%s enabled", self.mmInit)) == 0 then
|
||||
if self.anyBand then
|
||||
self.syslog("info", string.format(
|
||||
"%s: resetting current-bands to 'any'", self.name))
|
||||
os.execute(string.format("%s -m any --set-current-bands=any", self.mmcli))
|
||||
end
|
||||
|
||||
self.syslog("info", string.format("%s: reconnecting modem", self.name))
|
||||
os.execute(string.format("%s restart", self.mmInit))
|
||||
|
||||
if self.iface then
|
||||
self.syslog("info", string.format(
|
||||
"%s: restarting network interface '%s'", self.name, self.iface))
|
||||
self:toggleIface(false)
|
||||
if self.ifaceTimeout < 1 then
|
||||
self:toggleIface(true)
|
||||
else
|
||||
self._ifaceRestarting = true
|
||||
end
|
||||
end
|
||||
else
|
||||
self.syslog("warning", string.format(
|
||||
"%s: modemmanager service is disabled", self.name))
|
||||
end
|
||||
if self.attempts > 0 then
|
||||
self._attemptsCounter = self._attemptsCounter + 1
|
||||
end
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if not self._enabled then
|
||||
return
|
||||
end
|
||||
if self.iface and self._ifaceRestarting then
|
||||
if self._ifaceRestartCounter >= self.ifaceTimeout then
|
||||
self:toggleIface(true)
|
||||
self._ifaceRestarting = false
|
||||
self._ifaceRestartCounter = 0
|
||||
else
|
||||
self._ifaceRestartCounter = self._ifaceRestartCounter + timeDiff
|
||||
end
|
||||
else
|
||||
if currentStatus == 1 then
|
||||
if self._disconnectedAtStartup and self._deadCounter >= self.deadPeriod then
|
||||
if self.attempts == 0 or self._attemptsCounter < self.attempts then
|
||||
if self._firstAttempt or self._attemptIntervalCounter >= self.attemptInterval then
|
||||
self:restartMM()
|
||||
self._attemptIntervalCounter = 0
|
||||
self._firstAttempt = false
|
||||
else
|
||||
self._attemptIntervalCounter = self._attemptIntervalCounter + timeDiff
|
||||
end
|
||||
end
|
||||
else
|
||||
self._deadCounter = self._deadCounter + timeDiff
|
||||
end
|
||||
else
|
||||
self._attemptsCounter = 0
|
||||
self._attemptIntervalCounter = 0
|
||||
self._deadCounter = 0
|
||||
self._disconnectedAtStartup = true
|
||||
self._firstAttempt = true
|
||||
end
|
||||
self._ifaceRestartCounter = 0
|
||||
end
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# (с) 2026 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector-mod-telegram
|
||||
PKG_VERSION:=1.7.3
|
||||
PKG_RELEASE:=1
|
||||
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:=Telegram messenger module for internet-detector
|
||||
URL:=https://github.com/gSpotx2f/luci-app-internet-detector
|
||||
PKGARCH:=all
|
||||
DEPENDS:=+internet-detector +curl +ucode +ucode-mod-fs
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
Telegram messenger 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/modules
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_telegram.lua $(1)/usr/lib/lua/internet-detector/modules/mod_telegram.lua
|
||||
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
|
||||
$(INSTALL_DATA) ./files/usr/share/rpcd/acl.d/luci-app-internet-detector-mod-telegram.json $(1)/usr/share/rpcd/acl.d/luci-app-internet-detector-mod-telegram.json
|
||||
$(INSTALL_DIR) $(1)/usr/share/rpcd/ucode
|
||||
$(INSTALL_DATA) ./files/usr/share/rpcd/ucode/luci.internet-detector-mod-telegram $(1)/usr/share/rpcd/ucode/luci.internet-detector-mod-telegram
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
--[[
|
||||
Dependences:
|
||||
curl
|
||||
--]]
|
||||
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_telegram",
|
||||
runPrio = 70,
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
deadPeriod = 0,
|
||||
alivePeriod = 0,
|
||||
mode = 0, -- 0: connected, 1: disconnected, 2: both
|
||||
hostAlias = "OpenWrt",
|
||||
connectTimeout = 5,
|
||||
tgAPIToken = nil,
|
||||
tgChatId = nil,
|
||||
tgMsgURLpattern = "https://api.telegram.org/bot%s/sendMessage?chat_id=%s&parse_mode=html&text=%s",
|
||||
msgTextPattern = "<strong>[%s] (%s)</strong> @ %s", -- Message (host, instance, message)
|
||||
msgConnectPattern = "Connected: %s",
|
||||
msgDisconnectPattern = "Disconnected: %s",
|
||||
msgSeparator = " | ",
|
||||
msgMaxItems = 50,
|
||||
msgSendAttempts = 3,
|
||||
msgSendTimeout = 5,
|
||||
curlExec = "/usr/bin/curl",
|
||||
curlParams = "-s -g --no-keepalive",
|
||||
proxyAuthString = "",
|
||||
proxyString = "",
|
||||
ifaceString = "",
|
||||
status = nil,
|
||||
_enabled = false,
|
||||
_deadCounter = 0,
|
||||
_aliveCounter = 0,
|
||||
_msgSentDisconnect = true,
|
||||
_disconnected = true,
|
||||
_msgSentConnect = true,
|
||||
_connected = true,
|
||||
_msgBuffer = {},
|
||||
_msgSendCounter = 3,
|
||||
_msgTimeoutCounter = 5,
|
||||
}
|
||||
|
||||
local function prequire(package)
|
||||
local retVal, pkg = pcall(require, package)
|
||||
return retVal and pkg
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
self._enabled = true
|
||||
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)
|
||||
end
|
||||
if t.host_alias then
|
||||
self.hostAlias = t.host_alias
|
||||
else
|
||||
self.hostAlias = self.config.hostname
|
||||
end
|
||||
if t.api_token ~= nil then
|
||||
self.tgAPIToken = t.api_token
|
||||
end
|
||||
if t.chat_id ~= nil then
|
||||
self.tgChatId = t.chat_id
|
||||
end
|
||||
if (t.proxy_type and t.proxy_host and t.proxy_port) then
|
||||
if t.proxy_user and t.proxy_passwd then
|
||||
self.proxyAuthString = string.format(
|
||||
' --proxy-user "%s:%s"',
|
||||
t.proxy_user,
|
||||
t.proxy_passwd)
|
||||
end
|
||||
self.proxyString = string.format(
|
||||
" --proxy %s://%s:%d%s",
|
||||
t.proxy_type,
|
||||
t.proxy_host,
|
||||
t.proxy_port,
|
||||
self.proxyAuthString)
|
||||
end
|
||||
if t.iface then
|
||||
self.ifaceString = " --interface " .. t.iface
|
||||
end
|
||||
if tonumber(t.message_at_startup) == 1 then
|
||||
self._msgSentDisconnect = false
|
||||
self._disconnected = false
|
||||
self._msgSentConnect = false
|
||||
self._connected = false
|
||||
end
|
||||
|
||||
if unistd.access(self.curlExec, "x") then
|
||||
self._enabled = true
|
||||
else
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format("%s: %s is not available", self.name, self.curlExec))
|
||||
end
|
||||
if not self.tgAPIToken then
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format("%s: Telegram bot API token not specified.", self.name))
|
||||
end
|
||||
if not self.tgChatId then
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format("%s: Telegram chat ID not specified.", self.name))
|
||||
end
|
||||
|
||||
self._msgSendCounter = self.msgSendAttempts
|
||||
end
|
||||
|
||||
function Module:escape(str)
|
||||
local t = {}
|
||||
for i in str:gmatch(".") do
|
||||
if i:match("[^%w_]") then
|
||||
t[#t + 1] = "%" .. string.format("%x", string.byte(i))
|
||||
else
|
||||
t[#t + 1] = i
|
||||
end
|
||||
end
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
function Module:appendNotice(str)
|
||||
self._msgBuffer[#self._msgBuffer + 1] = str
|
||||
if #self._msgBuffer > self.msgMaxItems then
|
||||
local t = {}
|
||||
for i = #self._msgBuffer - self.msgMaxItems + 1, #self._msgBuffer do
|
||||
t[#t + 1] = self._msgBuffer[i]
|
||||
end
|
||||
self._msgBuffer = t
|
||||
end
|
||||
end
|
||||
|
||||
function Module:httpRequest(url)
|
||||
local retCode = 1, data
|
||||
|
||||
self.debugOutput(string.format("--- %s ---", self.name))
|
||||
|
||||
local curl = string.format(
|
||||
'%s%s%s --connect-timeout %s %s "%s"; printf "\n$?";',
|
||||
self.curlExec,
|
||||
self.ifaceString,
|
||||
self.proxyString,
|
||||
self.connectTimeout,
|
||||
self.curlParams,
|
||||
url
|
||||
)
|
||||
|
||||
self.debugOutput(curl)
|
||||
|
||||
local fh = io.popen(curl, "r")
|
||||
if fh then
|
||||
data = fh:read("*a")
|
||||
fh:close()
|
||||
if data ~= nil then
|
||||
local s, e = data:find("[0-9]+\n?$")
|
||||
retCode = tonumber(data:sub(s))
|
||||
data = data:sub(0, s - 2)
|
||||
if not data or data == "" then
|
||||
data = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
retCode = 1
|
||||
end
|
||||
|
||||
self.debugOutput(string.format(
|
||||
"data = %s; retCode = %s\n",
|
||||
tostring(data),
|
||||
tostring(retCode)))
|
||||
|
||||
return retCode, data
|
||||
end
|
||||
|
||||
function Module:parseResponse(str)
|
||||
local ok, errCode, desc
|
||||
ok = str:match('"ok":(%w+)')
|
||||
if ok == "false" then
|
||||
errCode = tonumber(str:match('"error_code":(%d+)'))
|
||||
if errCode then
|
||||
desc = str:match('"description":"([%w%s%p_]+)"')
|
||||
end
|
||||
end
|
||||
return ok, errCode, desc
|
||||
end
|
||||
|
||||
function Module:messageRequest(msg, textPattern)
|
||||
local retVal = 1
|
||||
local tgMsg = string.format(
|
||||
textPattern, self.hostAlias, self.config.serviceConfig.instance, msg)
|
||||
local url = string.format(
|
||||
self.tgMsgURLpattern, self.tgAPIToken, self.tgChatId, self:escape(tgMsg))
|
||||
|
||||
local ok, errCode, desc
|
||||
local retCode, data = self:httpRequest(url)
|
||||
if data then
|
||||
ok, errCode, desc = self:parseResponse(data)
|
||||
end
|
||||
if retCode == 0 and ok == "true" then
|
||||
retVal = 0
|
||||
self.syslog("info", string.format(
|
||||
"%s: Message sent to chat %s", self.name, self.tgChatId))
|
||||
else
|
||||
if errCode == 400 or errCode == 406 then
|
||||
retVal = 2
|
||||
elseif (errCode == 401 or
|
||||
errCode == 403 or
|
||||
errCode == 404 or
|
||||
errCode == 420) then
|
||||
retVal = 3
|
||||
end
|
||||
if errCode and desc then
|
||||
self.syslog("warning", string.format(
|
||||
"%s: %s %s", self.name, tostring(errCode), tostring(desc)))
|
||||
end
|
||||
end
|
||||
return retVal
|
||||
end
|
||||
|
||||
function Module:sendMessage(msg, textPattern)
|
||||
local retVal = self:messageRequest(msg, textPattern)
|
||||
if retVal == 0 then
|
||||
self._msgBuffer = {}
|
||||
elseif retVal == 2 then
|
||||
self.syslog("err", string.format(
|
||||
"%s: Server error (invalid API token or chat ID)", self.name))
|
||||
else
|
||||
self.syslog("err", string.format(
|
||||
"%s: An error occured while sending message", self.name))
|
||||
end
|
||||
return retVal
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if not self._enabled then
|
||||
return
|
||||
end
|
||||
|
||||
if currentStatus == 1 then
|
||||
self._aliveCounter = 0
|
||||
self._msgSentConnect = false
|
||||
|
||||
if not self._disconnected then
|
||||
self._disconnected = true
|
||||
self:appendNotice(string.format(
|
||||
self.msgDisconnectPattern, os.date("%Y.%m.%d %H:%M:%S", os.time())))
|
||||
end
|
||||
if not self._msgSentDisconnect and (self.mode == 1 or self.mode == 2) then
|
||||
if self._deadCounter >= self.deadPeriod then
|
||||
self._msgSendCounter = 0
|
||||
self._msgSentDisconnect = true
|
||||
else
|
||||
self._deadCounter = self._deadCounter + timeDiff
|
||||
end
|
||||
end
|
||||
self._connected = false
|
||||
elseif currentStatus == 0 then
|
||||
self._deadCounter = 0
|
||||
self._msgSentDisconnect = false
|
||||
|
||||
if not self._connected then
|
||||
self._connected = true
|
||||
self:appendNotice(string.format(
|
||||
self.msgConnectPattern, os.date("%Y.%m.%d %H:%M:%S", os.time())))
|
||||
end
|
||||
if not self._msgSentConnect and (self.mode == 0 or self.mode == 2) then
|
||||
if self._aliveCounter >= self.alivePeriod then
|
||||
self._msgSendCounter = 0
|
||||
self._msgSentConnect = true
|
||||
else
|
||||
self._aliveCounter = self._aliveCounter + timeDiff
|
||||
end
|
||||
end
|
||||
self._disconnected = false
|
||||
end
|
||||
|
||||
if self._msgSendCounter < self.msgSendAttempts then
|
||||
if self._msgTimeoutCounter >= self.msgSendTimeout then
|
||||
if #self._msgBuffer > 0 then
|
||||
local retVal = self:sendMessage(table.concat(self._msgBuffer, self.msgSeparator), self.msgTextPattern)
|
||||
if retVal == 1 then
|
||||
self._msgSendCounter = self._msgSendCounter + 1
|
||||
else
|
||||
self._msgSendCounter = self.msgSendAttempts
|
||||
end
|
||||
end
|
||||
self._msgTimeoutCounter = 0
|
||||
else
|
||||
self._msgTimeoutCounter = self._msgTimeoutCounter + timeDiff
|
||||
end
|
||||
else
|
||||
self._msgTimeoutCounter = self.msgSendTimeout
|
||||
end
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"luci-app-internet-detector-mod-telegram": {
|
||||
"description": "Grant access to internet-detector-mod-telegram procedures",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci.internet-detector-mod-telegram": [ "GetTgChatId" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { popen, stat } from 'fs';
|
||||
|
||||
const curlPath = '/usr/bin/curl';
|
||||
const curlExec = (stat(curlPath)?.perm?.user_exec) ? curlPath : null;
|
||||
const curlParams = '-s -g --no-keepalive';
|
||||
const curlConnectTimeout = 15;
|
||||
const tgUpdatesURLPattern = 'https://api.telegram.org/bot%s/getUpdates';
|
||||
|
||||
if(!curlExec) {
|
||||
die('Error! Curl not found!');
|
||||
}
|
||||
|
||||
function httpRequest(url, proxyType, proxyHost, proxyPort, proxyUser, proxyPasswd, iface) {
|
||||
let retCode = 1;
|
||||
let data = '';
|
||||
let proxyAuthString = '';
|
||||
let proxyString = '';
|
||||
let ifaceString = iface ? (' --interface ' + iface) : '';
|
||||
|
||||
if(!url) {
|
||||
return { retCode: 1, data };
|
||||
}
|
||||
|
||||
if(proxyType && proxyHost && proxyPort) {
|
||||
if(proxyUser && proxyPasswd) {
|
||||
proxyAuthString = sprintf(
|
||||
' --proxy-user "%s:%s"',
|
||||
proxyUser,
|
||||
proxyPasswd);
|
||||
}
|
||||
proxyString = sprintf(
|
||||
' --proxy %s://%s:%d%s',
|
||||
proxyType,
|
||||
proxyHost,
|
||||
proxyPort,
|
||||
proxyAuthString);
|
||||
}
|
||||
const curl = sprintf(
|
||||
'%s%s%s --connect-timeout %s %s "%s"; printf "\n$?";',
|
||||
curlExec,
|
||||
ifaceString,
|
||||
proxyString,
|
||||
curlConnectTimeout,
|
||||
curlParams,
|
||||
url
|
||||
);
|
||||
const fd = popen(curl, 'r');
|
||||
if(fd) {
|
||||
data = fd.read('all');
|
||||
fd.close();
|
||||
if(data) {
|
||||
const retCodeRegexp = /[0-9]+\n?$/;
|
||||
const r = match(data, retCodeRegexp);
|
||||
if(length(r) > 0) {
|
||||
retCode = r[0];
|
||||
}
|
||||
data = replace(data, retCodeRegexp, '');
|
||||
}
|
||||
}
|
||||
return { retCode, data };
|
||||
}
|
||||
|
||||
function parseResponse(str) {
|
||||
let ok = null;
|
||||
let chatId = [];
|
||||
let errCode = null;
|
||||
let errDesc = null;
|
||||
|
||||
const data = json(str);
|
||||
ok = data.ok;
|
||||
if(ok == false) {
|
||||
errCode = data.error_code;
|
||||
errDesc = data.description;
|
||||
} else {
|
||||
let chats = [];
|
||||
if(data.result) {
|
||||
for(let i in data.result) {
|
||||
if(i.message && i.message.chat && i.message.chat.id) {
|
||||
push(chats, i.message.chat.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(length(chats) > 0) {
|
||||
chatId = uniq(chats);
|
||||
}
|
||||
}
|
||||
return { ok, chatId, errCode, errDesc };
|
||||
}
|
||||
|
||||
const methods = {
|
||||
GetTgChatId: {
|
||||
args: {
|
||||
botToken : 'String',
|
||||
proxyType : 'String',
|
||||
proxyHost : 'String',
|
||||
proxyPort : 'String',
|
||||
proxyUser : 'String',
|
||||
proxyPasswd: 'String',
|
||||
iface : 'String',
|
||||
},
|
||||
call: function(request) {
|
||||
const botToken = request.args?.botToken;
|
||||
if(botToken) {
|
||||
const ret = httpRequest(
|
||||
sprintf(tgUpdatesURLPattern, botToken),
|
||||
request.args.proxyType,
|
||||
request.args.proxyHost,
|
||||
request.args.proxyPort,
|
||||
request.args.proxyUser,
|
||||
request.args.proxyPasswd,
|
||||
request.args.iface
|
||||
);
|
||||
if(ret.retCode == 0 && length(ret.data) > 0) {
|
||||
return parseResponse(ret.data);
|
||||
} else {
|
||||
return { ok: null, chatId: [], errCode: ret.retCode, errDesc: 'Network request error' };
|
||||
}
|
||||
} else {
|
||||
return { ok: null, chatId: [], errCode: 1, errDesc: 'Bot token missing' };
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return { 'luci.internet-detector-mod-telegram': methods };
|
||||
@@ -0,0 +1,67 @@
|
||||
#
|
||||
# (с) 2026 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector
|
||||
PKG_VERSION:=1.7.3
|
||||
PKG_RELEASE:=1
|
||||
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:=Internet detector
|
||||
URL:=https://github.com/gSpotx2f/luci-app-internet-detector
|
||||
PKGARCH:=all
|
||||
DEPENDS:=+lua +luaposix +libuci-lua
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
Internet-detector is a small daemon
|
||||
for checking Internet availability.
|
||||
Written in Lua using the luaposix library.
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/internet-detector
|
||||
/etc/internet-detector/down-script.internet
|
||||
/etc/internet-detector/up-script.internet
|
||||
/etc/internet-detector/public-ip-script.internet
|
||||
/etc/internet-detector/regular-script.internet
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/etc/config/internet-detector $(1)/etc/config/internet-detector
|
||||
$(INSTALL_DIR) $(1)/etc/internet-detector
|
||||
$(INSTALL_DATA) ./files/etc/internet-detector/down-script.internet $(1)/etc/internet-detector/down-script.internet
|
||||
$(INSTALL_DATA) ./files/etc/internet-detector/up-script.internet $(1)/etc/internet-detector/up-script.internet
|
||||
$(INSTALL_DATA) ./files/etc/internet-detector/public-ip-script.internet $(1)/etc/internet-detector/public-ip-script.internet
|
||||
$(INSTALL_DATA) ./files/etc/internet-detector/regular-script.internet $(1)/etc/internet-detector/regular-script.internet
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/etc/init.d/internet-detector $(1)/etc/init.d/internet-detector
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) ./files/usr/bin/internet-detector $(1)/usr/bin/internet-detector
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/internet-detector
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/main.lua $(1)/usr/lib/lua/internet-detector/main.lua
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/init.lua $(1)/usr/lib/lua/internet-detector/init.lua
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/internet-detector/modules
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_led_control.lua $(1)/usr/lib/lua/internet-detector/modules/mod_led_control.lua
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_reboot.lua $(1)/usr/lib/lua/internet-detector/modules/mod_reboot.lua
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_network_restart.lua $(1)/usr/lib/lua/internet-detector/modules/mod_network_restart.lua
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_public_ip.lua $(1)/usr/lib/lua/internet-detector/modules/mod_public_ip.lua
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_user_scripts.lua $(1)/usr/lib/lua/internet-detector/modules/mod_user_scripts.lua
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_regular_script.lua $(1)/usr/lib/lua/internet-detector/modules/mod_regular_script.lua
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
config main 'config'
|
||||
option mode '1'
|
||||
option logging_level '6'
|
||||
|
||||
config instance 'internet'
|
||||
option enabled '1'
|
||||
option description 'Default instance'
|
||||
list hosts '8.8.8.8'
|
||||
list hosts '1.1.1.1'
|
||||
list urls 'https://www.google.com'
|
||||
option check_type '0'
|
||||
option tcp_port '53'
|
||||
option interval_up '30'
|
||||
option interval_down '5'
|
||||
option connection_attempts '2'
|
||||
option connection_timeout '2'
|
||||
option mod_led_control_enabled '0'
|
||||
option mod_reboot_enabled '0'
|
||||
option mod_reboot_disconnected_at_startup '0'
|
||||
option mod_reboot_dead_period '3600'
|
||||
option mod_reboot_force_reboot_delay '300'
|
||||
option mod_network_restart_enabled '0'
|
||||
option mod_network_restart_disconnected_at_startup '0'
|
||||
option mod_network_restart_dead_period '900'
|
||||
option mod_network_restart_attempts '1'
|
||||
option mod_network_restart_attempt_interval '60'
|
||||
option mod_network_restart_device_timeout '0'
|
||||
option mod_modem_restart_enabled '0'
|
||||
option mod_modem_restart_disconnected_at_startup '0'
|
||||
option mod_modem_restart_dead_period '600'
|
||||
option mod_modem_restart_attempts '1'
|
||||
option mod_modem_restart_attempt_interval '60'
|
||||
option mod_modem_restart_iface_timeout '0'
|
||||
option mod_modem_restart_any_band '0'
|
||||
option mod_public_ip_enabled '0'
|
||||
option mod_public_ip_provider 'opendns1'
|
||||
option mod_public_ip_qtype '0'
|
||||
option mod_public_ip_interval '600'
|
||||
option mod_public_ip_interval_failed '60'
|
||||
option mod_public_ip_request_attempts '2'
|
||||
option mod_public_ip_timeout '2'
|
||||
option mod_public_ip_enable_ip_script '0'
|
||||
option mod_email_enabled '0'
|
||||
option mod_email_message_at_startup '0'
|
||||
option mod_email_mode '0'
|
||||
option mod_email_alive_period '0'
|
||||
option mod_email_mail_security 'tls'
|
||||
option mod_telegram_enabled '0'
|
||||
option mod_telegram_message_at_startup '0'
|
||||
option mod_telegram_mode '0'
|
||||
option mod_telegram_dead_period '0'
|
||||
option mod_telegram_alive_period '0'
|
||||
option mod_user_scripts_enabled '0'
|
||||
option mod_user_scripts_alive_period '0'
|
||||
option mod_user_scripts_up_script_attempts '1'
|
||||
option mod_user_scripts_up_script_attempt_interval '60'
|
||||
option mod_user_scripts_connected_at_startup '0'
|
||||
option mod_user_scripts_dead_period '0'
|
||||
option mod_user_scripts_down_script_attempts '1'
|
||||
option mod_user_scripts_down_script_attempt_interval '60'
|
||||
option mod_user_scripts_disconnected_at_startup '0'
|
||||
option mod_regular_script_enabled '0'
|
||||
option mod_regular_script_inet_state '2'
|
||||
option mod_regular_script_interval '3600'
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=97
|
||||
STOP=01
|
||||
|
||||
USE_PROCD=1
|
||||
PROG="/usr/bin/internet-detector"
|
||||
|
||||
run_instance() {
|
||||
config_get enabled "$1" enabled "0"
|
||||
if [ $enabled = "1" ]; then
|
||||
procd_open_instance "$1"
|
||||
procd_set_param command "$PROG" "-a" "nodaemon" "-i" "$1"
|
||||
procd_set_param respawn
|
||||
procd_set_param term_timeout 60
|
||||
procd_close_instance
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load "internet-detector"
|
||||
config_get mode "config" mode "0"
|
||||
if [ $mode = "1" ]; then
|
||||
config_foreach run_instance "instance"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
$PROG -a stop
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "internet-detector"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# Shell commands to run when disconnected from the Internet
|
||||
@@ -0,0 +1,2 @@
|
||||
# Shell commands that run when the public IP address changes.
|
||||
# New IP is available as value of the $PUBLIC_IP variable.
|
||||
@@ -0,0 +1 @@
|
||||
# Shell commands that are run regularly
|
||||
@@ -0,0 +1 @@
|
||||
# Shell commands that run when connected to the Internet
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
--[[
|
||||
Internet detector daemon for OpenWrt.
|
||||
|
||||
Dependences:
|
||||
lua
|
||||
luaposix
|
||||
libuci-lua
|
||||
|
||||
(с) 2025 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
--]]
|
||||
|
||||
local getopt = require("posix.unistd").getopt
|
||||
local InternetDetector = require("internet-detector.main")
|
||||
|
||||
local function help()
|
||||
return table.concat({
|
||||
[1] = string.format(
|
||||
"Usage: %s -a daemon -i <UCI instance> | -a nodaemon -i <UCI instance> | -a debug -i <UCI instance> | -a stop | -S | -I | -U | -h",
|
||||
arg[0]),
|
||||
[2] = " -a ARG action: daemon | nodaemon | debug | stop",
|
||||
[3] = " -i ARG instance: UCI instance name",
|
||||
[4] = " -S status",
|
||||
[5] = " -I inet status",
|
||||
[6] = " -U uipoll",
|
||||
[7] = " -h print this help text"
|
||||
}, "\n")
|
||||
end
|
||||
|
||||
local action, instance
|
||||
local params = {}
|
||||
local last_index = 1
|
||||
for r, optarg, optind in getopt(arg, "a:i:SIUh") do
|
||||
if r == "?" then
|
||||
print("Error! Unrecognized option")
|
||||
os.exit(1)
|
||||
end
|
||||
last_index = optind
|
||||
if r == "a" then
|
||||
action = optarg
|
||||
elseif r == "i" then
|
||||
instance = optarg
|
||||
else
|
||||
params[#params + 1] = r
|
||||
end
|
||||
end
|
||||
if action == "stop" then
|
||||
InternetDetector:stop()
|
||||
elseif action then
|
||||
if not instance then
|
||||
print("Error! Instance not specified [-i]")
|
||||
os.exit(1)
|
||||
end
|
||||
if action == "daemon" then
|
||||
if InternetDetector:setServiceConfig(instance) then
|
||||
InternetDetector:daemon()
|
||||
else
|
||||
os.exit(126)
|
||||
end
|
||||
elseif action == "nodaemon" then
|
||||
if InternetDetector:setServiceConfig(instance) then
|
||||
InternetDetector:noDaemon()
|
||||
else
|
||||
os.exit(126)
|
||||
end
|
||||
elseif action == "debug" then
|
||||
if InternetDetector:setServiceConfig(instance) then
|
||||
InternetDetector.debug = true
|
||||
InternetDetector:noDaemon()
|
||||
else
|
||||
os.exit(126)
|
||||
end
|
||||
else
|
||||
print("Error! Wrong action [-a]")
|
||||
os.exit(1)
|
||||
end
|
||||
else
|
||||
if params[1] == "S" then
|
||||
print(InternetDetector:status())
|
||||
elseif params[1] == "I" then
|
||||
print(InternetDetector:inetStatus())
|
||||
elseif params[1] == "U" then
|
||||
if InternetDetector:status() == "stoped" then
|
||||
os.exit(126)
|
||||
else
|
||||
InternetDetector:setSIGUSR()
|
||||
print(InternetDetector:inetStatus())
|
||||
end
|
||||
elseif params[1] == "h" then
|
||||
print(help())
|
||||
else
|
||||
print(help())
|
||||
os.exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
os.exit(0)
|
||||
@@ -0,0 +1 @@
|
||||
return require("internet-detector.main")
|
||||
@@ -0,0 +1,806 @@
|
||||
|
||||
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 = {
|
||||
appName = "internet-detector",
|
||||
libDir = "/usr/lib/lua",
|
||||
logLevels = {
|
||||
emerg = { level = syslog.LOG_EMERG, num = 0 },
|
||||
alert = { level = syslog.LOG_ALERT, num = 1 },
|
||||
crit = { level = syslog.LOG_CRIT, num = 2 },
|
||||
err = { level = syslog.LOG_ERR, num = 3 },
|
||||
warning = { level = syslog.LOG_WARNING, num = 4 },
|
||||
notice = { level = syslog.LOG_NOTICE, num = 5 },
|
||||
info = { level = syslog.LOG_INFO, num = 6 },
|
||||
debug = { level = syslog.LOG_DEBUG, num = 7 },
|
||||
},
|
||||
pingCmd = "/bin/ping",
|
||||
pingParams = "-c 1",
|
||||
curlExec = "/usr/bin/curl",
|
||||
curlParams = '-s -g --no-keepalive --head --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0"',
|
||||
mode = 0, -- 0: disabled, 1: Service, 2: UI detector
|
||||
loggingLevel = 6,
|
||||
hostname = "OpenWrt",
|
||||
uiRunTime = 30,
|
||||
noModules = false,
|
||||
uiAvailModules = { mod_public_ip = true },
|
||||
debug = false,
|
||||
serviceConfig = {
|
||||
hosts = {
|
||||
[1] = "8.8.8.8",
|
||||
[2] = "1.1.1.1",
|
||||
},
|
||||
urls = {
|
||||
[1] = "https://www.google.com",
|
||||
},
|
||||
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,
|
||||
proxy_type = nil,
|
||||
proxy_host = nil,
|
||||
proxy_port = nil,
|
||||
proxy_user = nil,
|
||||
proxy_passwd = nil,
|
||||
iface = nil,
|
||||
instance = nil,
|
||||
},
|
||||
modules = {},
|
||||
parsedHosts = {},
|
||||
proxyAuthString = "",
|
||||
proxyString = "",
|
||||
uiCounter = 0,
|
||||
pidFile = nil,
|
||||
statusFile = nil,
|
||||
}
|
||||
InternetDetector.configDir = string.format("/etc/%s", InternetDetector.appName)
|
||||
InternetDetector.modulesDir = string.format(
|
||||
"%s/%s/modules", InternetDetector.libDir, InternetDetector.appName)
|
||||
InternetDetector.commonDir = string.format("/tmp/run/%s", InternetDetector.appName)
|
||||
InternetDetector.appNamePattern = InternetDetector.appName:gsub("-", "%%-")
|
||||
InternetDetector.pidFilePattern = "^" .. InternetDetector.appNamePattern .. ".-%.pid$"
|
||||
|
||||
-- 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 loggingLevel, err = uciCursor:get(InternetDetector.appName, "config", "logging_level")
|
||||
if loggingLevel ~= nil then
|
||||
InternetDetector.loggingLevel = tonumber(loggingLevel)
|
||||
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
|
||||
|
||||
function InternetDetector:prequire(package)
|
||||
local ok, pkg = pcall(require, package)
|
||||
return ok and pkg
|
||||
end
|
||||
|
||||
function InternetDetector:loadInstanceConfig(instance)
|
||||
local sections = uciCursor:get_all(self.appName)
|
||||
local t = sections[instance]
|
||||
if t then
|
||||
for k, v in pairs(t) do
|
||||
if type(v) == "string" and v:match("^[%d]+$") then
|
||||
v = tonumber(v)
|
||||
end
|
||||
self.serviceConfig[k] = v
|
||||
end
|
||||
self.serviceConfig.instance = instance
|
||||
self.serviceConfig.instanceNum = t[".index"]
|
||||
return true
|
||||
end
|
||||
return false
|
||||
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)
|
||||
local levelItem = self.logLevels[level]
|
||||
local levelValue = (levelItem and levelItem.level) or self.logLevels["info"].level
|
||||
local num = (levelItem and levelItem.num) or self.logLevels["info"].num
|
||||
if num <= self.loggingLevel then
|
||||
syslog.syslog(levelValue, string.format(
|
||||
"%s: %s", self.serviceConfig.instance or "", msg))
|
||||
end
|
||||
end
|
||||
|
||||
function InternetDetector:debugOutput(msg)
|
||||
if self.debug then
|
||||
io.stdout:write(string.format("[%s] %s\n", os.date("%Y.%m.%d-%H:%M:%S"), msg))
|
||||
io.stdout:flush()
|
||||
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.modules.%s", self.appName, modName))
|
||||
else
|
||||
m = self:prequire(string.format("%s.modules.%s", self.appName, modName))
|
||||
end
|
||||
if m then
|
||||
m.config = self
|
||||
m.syslog = function(level, msg) self:writeLogMessage(level, msg) end
|
||||
m.debugOutput = function(msg) self:debugOutput(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:parseUrls()
|
||||
self.parsedHosts = {}
|
||||
for k, v in ipairs(self.serviceConfig.urls) do
|
||||
self.parsedHosts[k] = { addr = v }
|
||||
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)
|
||||
|
||||
self:debugOutput(string.format(
|
||||
"--- Ping ---\ntime = %s\n%s\nretCode = %s", os.time(), ping, retCode))
|
||||
|
||||
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
|
||||
self:debugOutput(string.format(
|
||||
"GETADDRINFO ERROR: %s, %s", errMsg, errNum))
|
||||
else
|
||||
local family = saTable[1].family
|
||||
|
||||
if family then
|
||||
local sock, errMsg, errNum = socket.socket(family, socket.SOCK_STREAM, 0)
|
||||
|
||||
if not sock then
|
||||
self:debugOutput(string.format(
|
||||
"SOCKET ERROR: %s, %s", errMsg, errNum))
|
||||
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
|
||||
self:debugOutput(string.format(
|
||||
"SOCKET ERROR: %s, %s", errMsg, errNum))
|
||||
|
||||
unistd.close(sock)
|
||||
return retCode
|
||||
end
|
||||
end
|
||||
|
||||
local success = socket.connect(sock, saTable[1])
|
||||
|
||||
if self.debug then
|
||||
if not success then
|
||||
self:debugOutput(string.format(
|
||||
"SOCKET CONNECT ERROR: %s", 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 = {}
|
||||
self:debugOutput(
|
||||
string.format("SOCKET ERROR: %s, %s", err_s, e_s))
|
||||
end
|
||||
if not peerTable then
|
||||
peerTable = {}
|
||||
self:debugOutput(
|
||||
string.format("SOCKET ERROR: %s, %s", err_p, e_p))
|
||||
end
|
||||
self:debugOutput(string.format(
|
||||
"--- TCP ---\ntime = %s\nconnection_timeout = %s\niface = %s\nhost:port = [%s]:%s\nsockname = [%s]:%s\npeername = [%s]:%s\nsuccess = %s",
|
||||
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))
|
||||
)
|
||||
end
|
||||
|
||||
socket.shutdown(sock, socket.SHUT_RDWR)
|
||||
unistd.close(sock)
|
||||
retCode = success and 0 or 1
|
||||
end
|
||||
end
|
||||
return retCode
|
||||
end
|
||||
|
||||
function InternetDetector:httpRequest(url)
|
||||
local retCode = 1, data
|
||||
local curl = string.format(
|
||||
'%s%s%s --connect-timeout %s %s "%s"; printf "\n$?";',
|
||||
self.curlExec,
|
||||
self.serviceConfig.iface and (" --interface " .. self.serviceConfig.iface) or "",
|
||||
self.proxyString,
|
||||
self.serviceConfig.connection_timeout,
|
||||
self.curlParams,
|
||||
url
|
||||
)
|
||||
local fh = io.popen(curl, "r")
|
||||
if fh then
|
||||
data = fh:read("*a")
|
||||
fh:close()
|
||||
if data ~= nil then
|
||||
local s, e = data:find("[0-9]+\n?$")
|
||||
retCode = tonumber(data:sub(s))
|
||||
data = data:sub(0, s - 2)
|
||||
if not data or data == "" then
|
||||
data = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
retCode = 1
|
||||
end
|
||||
|
||||
self:debugOutput(string.format(
|
||||
"--- Curl ---\ntime = %s\n%s\nretCode = %s\ndata = [\n%s]\n",
|
||||
os.time(),
|
||||
curl,
|
||||
retCode,
|
||||
tostring(data)))
|
||||
|
||||
return retCode, data
|
||||
end
|
||||
|
||||
function InternetDetector:getHTTPCode(data)
|
||||
local httpCode
|
||||
local respHeader = data:match("^HTTP/[^%c]+")
|
||||
if respHeader then
|
||||
httpCode = respHeader:match("%d%d%d")
|
||||
end
|
||||
return tonumber(httpCode)
|
||||
end
|
||||
|
||||
function InternetDetector:checkURL(url)
|
||||
local httpCode
|
||||
local retCode, data = self:httpRequest(url)
|
||||
if retCode == 0 and data then
|
||||
httpCode = self:getHTTPCode(data)
|
||||
end
|
||||
return (httpCode ~= 200) and 1 or 0
|
||||
end
|
||||
|
||||
function InternetDetector:exit()
|
||||
for _, e in ipairs(self.modules) do
|
||||
e:onExit()
|
||||
end
|
||||
self:removeProcessFiles()
|
||||
if self.loggingLevel > 0 then
|
||||
self:writeLogMessage("info", "stoped")
|
||||
syslog.closelog()
|
||||
end
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
function InternetDetector:resetUiCounter(signo)
|
||||
self.uiCounter = 0
|
||||
end
|
||||
|
||||
function InternetDetector:mainLoop()
|
||||
signal.signal(signal.SIGTERM, function(signo) self:exit(signo) end)
|
||||
signal.signal(signal.SIGINT, function(signo) self:exit(signo) end)
|
||||
signal.signal(signal.SIGQUIT, function(signo) self:exit(signo) end)
|
||||
signal.signal(signal.SIGUSR1, function(signo) self:resetUiCounter(signo) end)
|
||||
|
||||
local mTimeNow, mTimeDiff, mLastTime, uiTimeNow, uiLastTime
|
||||
local lastStatus = -1
|
||||
local currentStatus = -1
|
||||
local interval = self.serviceConfig.interval_up
|
||||
local modulesStatus = {}
|
||||
local counter = 0
|
||||
local inetChecked = false
|
||||
local checking = false
|
||||
local hostNum = 1
|
||||
local attempt = 1
|
||||
|
||||
local checkFunc = self.TCPConnectionToHost
|
||||
if self.serviceConfig.check_type == 1 then
|
||||
checkFunc = self.pingHost
|
||||
self:parseHosts()
|
||||
elseif self.serviceConfig.check_type == 2 then
|
||||
checkFunc = self.checkURL
|
||||
self:parseUrls()
|
||||
if (self.serviceConfig.proxy_type and self.serviceConfig.proxy_host and
|
||||
self.serviceConfig.proxy_port) then
|
||||
if self.serviceConfig.proxy_user and self.serviceConfig.proxy_passwd then
|
||||
self.proxyAuthString = string.format(
|
||||
' --proxy-user "%s:%s"',
|
||||
self.serviceConfig.proxy_user,
|
||||
self.serviceConfig.proxy_passwd)
|
||||
end
|
||||
self.proxyString = string.format(
|
||||
" --proxy %s://%s:%d%s",
|
||||
self.serviceConfig.proxy_type,
|
||||
self.serviceConfig.proxy_host,
|
||||
self.serviceConfig.proxy_port,
|
||||
self.proxyAuthString)
|
||||
end
|
||||
else
|
||||
self:parseHosts()
|
||||
end
|
||||
|
||||
self:writeValueToFile(
|
||||
self.statusFile, self:statusJson(currentStatus, self.serviceConfig.instance))
|
||||
|
||||
while true do
|
||||
if counter == 0 or counter >= interval then
|
||||
checking = true
|
||||
end
|
||||
|
||||
inetChecked = false
|
||||
|
||||
if checking then
|
||||
local newStatus = 1
|
||||
if hostNum <= #self.parsedHosts then
|
||||
if attempt <= self.serviceConfig.connection_attempts then
|
||||
local addr = self.parsedHosts[hostNum].addr
|
||||
local port = self.parsedHosts[hostNum].port
|
||||
local retCode = 1
|
||||
if self.debug then
|
||||
retCode = checkFunc(self, addr, port)
|
||||
else
|
||||
local ok, status = pcall(checkFunc, self, addr, port)
|
||||
if ok then
|
||||
retCode = status
|
||||
else
|
||||
self:writeLogMessage("err", string.format(
|
||||
"An error occurred while checking the host %s: %s",
|
||||
tostring(addr),
|
||||
tostring(status))
|
||||
)
|
||||
end
|
||||
end
|
||||
if retCode == 0 then
|
||||
attempt = 1
|
||||
hostNum = 1
|
||||
checking = false
|
||||
newStatus = 0
|
||||
counter = 0
|
||||
inetChecked = true
|
||||
else
|
||||
attempt = attempt + 1
|
||||
if attempt > self.serviceConfig.connection_attempts then
|
||||
attempt = 1
|
||||
hostNum = hostNum + 1
|
||||
end
|
||||
end
|
||||
else
|
||||
attempt = 1
|
||||
hostNum = hostNum + 1
|
||||
end
|
||||
if hostNum > #self.parsedHosts then
|
||||
hostNum = 1
|
||||
checking = false
|
||||
counter = 0
|
||||
inetChecked = true
|
||||
end
|
||||
else
|
||||
hostNum = 1
|
||||
checking = false
|
||||
counter = 0
|
||||
inetChecked = true
|
||||
end
|
||||
|
||||
if inetChecked then
|
||||
currentStatus = newStatus
|
||||
if not stat.stat(self.statusFile) then
|
||||
self:writeValueToFile(self.statusFile, self:statusJson(
|
||||
currentStatus, self.serviceConfig.instance))
|
||||
end
|
||||
if currentStatus == 0 then
|
||||
interval = self.serviceConfig.interval_up
|
||||
if currentStatus ~= lastStatus then
|
||||
self:writeValueToFile(self.statusFile, self:statusJson(
|
||||
currentStatus, self.serviceConfig.instance))
|
||||
self:writeLogMessage("notice", "Connected")
|
||||
end
|
||||
elseif currentStatus == 1 then
|
||||
interval = self.serviceConfig.interval_down
|
||||
if currentStatus ~= lastStatus then
|
||||
self:writeValueToFile(self.statusFile, self:statusJson(
|
||||
currentStatus, self.serviceConfig.instance))
|
||||
self:writeLogMessage("notice", "Disconnected")
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
|
||||
if self.debug then
|
||||
e:run(currentStatus, lastStatus, mTimeDiff, mTimeNow, inetChecked)
|
||||
else
|
||||
local ok, err = pcall(e.run, e, currentStatus, lastStatus, mTimeDiff, mTimeNow, inetChecked)
|
||||
if not ok then
|
||||
self:writeLogMessage("err", string.format(
|
||||
"%s: Module error: %s", e.name, tostring(err)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local modStatusChanged = false
|
||||
for k, v in ipairs(self.modules) do
|
||||
if modulesStatus[v.name] ~= v.status then
|
||||
modulesStatus[v.name] = v.status
|
||||
modStatusChanged = true
|
||||
end
|
||||
end
|
||||
if modStatusChanged and next(modulesStatus) then
|
||||
self:writeValueToFile(self.statusFile, self:statusJson(
|
||||
currentStatus, self.serviceConfig.instance, modulesStatus))
|
||||
end
|
||||
|
||||
unistd.sleep(1)
|
||||
|
||||
if not checking then
|
||||
lastStatus = currentStatus
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
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:exit(signal.SIGTERM)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function InternetDetector:removeProcessFiles()
|
||||
os.remove(self.statusFile)
|
||||
os.remove(self.pidFile)
|
||||
end
|
||||
|
||||
function InternetDetector:status()
|
||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||
if ok then
|
||||
for item in commonDir do
|
||||
if item:match(self.pidFilePattern) 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 statusFilePattern = "^" .. self.appNamePattern .. ".-%.status$"
|
||||
local lines = {}
|
||||
for item in commonDir do
|
||||
if item:match(statusFilePattern) 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 = false, pidValue
|
||||
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 stopping error: %s (%s). PID: "%s"\n', errMsg, errNum, pidValue))
|
||||
end
|
||||
if errNum == 3 then
|
||||
os.remove(pidFile)
|
||||
end
|
||||
retVal = true
|
||||
else
|
||||
os.remove(pidFile)
|
||||
end
|
||||
end
|
||||
if not pidValue then
|
||||
io.stderr:write(
|
||||
string.format('PID file "%s" does not exists. Is the %s not running?\n',
|
||||
pidFile, self.appName))
|
||||
end
|
||||
return retVal
|
||||
end
|
||||
|
||||
function InternetDetector:stop()
|
||||
local nopids = false
|
||||
for i = 0, 100 do
|
||||
nopids = true
|
||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||
if ok then
|
||||
for item in commonDir do
|
||||
if item:match(self.pidFilePattern) then
|
||||
if self:stopInstance(string.format("%s/%s", self.commonDir, item)) then
|
||||
nopids = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if nopids then
|
||||
break
|
||||
end
|
||||
time.nanosleep({ tv_sec = 0, tv_nsec = 10000000 })
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function InternetDetector:setSIGUSR()
|
||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||
if ok then
|
||||
for item in commonDir do
|
||||
if item:match(self.pidFilePattern) 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
|
||||
local s = stat.stat(self.commonDir)
|
||||
if not s or not (stat.S_ISDIR(s.st_mode) ~= 0) then
|
||||
if not stat.mkdir(self.commonDir) then
|
||||
io.stderr:write(
|
||||
string.format('Error occurred while creating %s. Exit.\n', self.commonDir))
|
||||
os.exit(1)
|
||||
end
|
||||
end
|
||||
if self.serviceConfig.check_type == 2 and not unistd.access(self.curlExec, "x") then
|
||||
io.stderr:write(string.format(
|
||||
"Error, %s is not available. You need to install curl.\n", self.curlExec))
|
||||
os.exit(1)
|
||||
end
|
||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||
if ok then
|
||||
local instancePattern = "^" .. self.appNamePattern .. "%." .. self.serviceConfig.instance .. "%.[%d]+%.pid$"
|
||||
for item in commonDir do
|
||||
if item:match(instancePattern) then
|
||||
self:stopInstance(string.format("%s/%s", self.commonDir, item))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function InternetDetector:run()
|
||||
local pidValue = unistd.getpid()
|
||||
self.pidFile = string.format(
|
||||
"%s/%s.%s.%s.pid", self.commonDir, self.appName, self.serviceConfig.instance, pidValue)
|
||||
self.statusFile = string.format(
|
||||
"%s/%s.%s.status", self.commonDir, self.appName, self.serviceConfig.instance)
|
||||
self:writeValueToFile(self.pidFile, pidValue)
|
||||
|
||||
if self.loggingLevel > 0 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
|
||||
self:debugOutput(string.format(
|
||||
"%s%s = %s", 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
|
||||
|
||||
self:debugOutput("--- Config ---")
|
||||
inspectTable()(self, "self.")
|
||||
end
|
||||
|
||||
self:mainLoop()
|
||||
self:exit()
|
||||
end
|
||||
|
||||
function InternetDetector:noDaemon()
|
||||
self:preRun()
|
||||
self:run()
|
||||
end
|
||||
|
||||
function InternetDetector:daemon()
|
||||
self:preRun()
|
||||
-- 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:loadInstanceConfig(instance) then
|
||||
if self.mode == 2 then
|
||||
self.loggingLevel = 0
|
||||
self.noModules = true
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return InternetDetector
|
||||
@@ -0,0 +1,334 @@
|
||||
|
||||
local dirent = require("posix.dirent")
|
||||
local time = require("posix.time")
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_led_control",
|
||||
runPrio = 10,
|
||||
config = {},
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
runInterval = 5,
|
||||
sysLedsDir = "/sys/class/leds",
|
||||
ledsPerInstance = 3,
|
||||
ledAction1Default = 1, -- 1: off, 2: on, 3: blinking, 4: netdev
|
||||
ledAction2Default = 1,
|
||||
ledBlinkDelayDefault = 500,
|
||||
ledNetlinkDeviceDefault = nil,
|
||||
ledNetdevModeLinkDefault = "1",
|
||||
ledNetdevModeRxDefault = "0",
|
||||
ledNetdevModeTxDefault = "0",
|
||||
status = nil,
|
||||
_enabled = false,
|
||||
_leds = {},
|
||||
_counter = 0,
|
||||
_exit = false,
|
||||
}
|
||||
|
||||
function Module:setLedAttrs(t)
|
||||
t.ledDir = string.format("%s/%s", self.sysLedsDir, t.ledName)
|
||||
t.ledMaxBrightnessFile = string.format("%s/max_brightness", t.ledDir)
|
||||
t.ledBrightnessFile = string.format("%s/brightness", t.ledDir)
|
||||
t.ledMaxBrightness = self.readValue(t.ledMaxBrightnessFile) or "1"
|
||||
t.ledTriggerFile = string.format("%s/trigger", t.ledDir)
|
||||
t.ledDelayOnFile = string.format("%s/delay_on", t.ledDir)
|
||||
t.ledDelayOffFile = string.format("%s/delay_off", t.ledDir)
|
||||
t.ledDeviceNameFile = string.format("%s/device_name", t.ledDir)
|
||||
t.ledLinkFile = string.format("%s/link", t.ledDir)
|
||||
t.ledRxFile = string.format("%s/rx", t.ledDir)
|
||||
t.ledTxFile = string.format("%s/tx", t.ledDir)
|
||||
t.ledPrevState = {
|
||||
brightness = self.readValue(t.ledBrightnessFile),
|
||||
trigger = self.readValue(t.ledTriggerFile),
|
||||
}
|
||||
if t.ledPrevState.trigger then
|
||||
local val = t.ledPrevState.trigger:match("%[[%w%-_]+%]")
|
||||
if val then
|
||||
t.ledPrevState.trigger = val:gsub("[%]%[]", "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Module:checkLed(t)
|
||||
return (unistd.access(t.ledDir, "r") and
|
||||
unistd.access(t.ledBrightnessFile, "rw") and
|
||||
unistd.access(t.ledTriggerFile, "rw"))
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
for i = 1, self.ledsPerInstance do
|
||||
self._leds[i] = {}
|
||||
end
|
||||
if t.led1_name then
|
||||
self._enabled = true
|
||||
else
|
||||
return
|
||||
end
|
||||
for i, l in ipairs(self._leds) do
|
||||
local led = "led" .. i
|
||||
if t[led .. "_name"] ~= nil then
|
||||
l.ledName = t[led .. "_name"]
|
||||
l.ledAction1 = tonumber(t[led .. "_action_1"]) or self.ledAction1Default
|
||||
l.ledAction2 = tonumber(t[led .. "_action_2"]) or self.ledAction2Default
|
||||
l.ledBlinkOnDelay1 = tonumber(t[led .. "_blink_on_delay_1"]) or self.ledBlinkDelayDefault
|
||||
l.ledBlinkOffDelay1 = tonumber(t[led .. "_blink_off_delay_1"]) or self.ledBlinkDelayDefault
|
||||
l.ledBlinkOnDelay2 = tonumber(t[led .. "_blink_on_delay_2"]) or self.ledBlinkDelayDefault
|
||||
l.ledBlinkOffDelay2 = tonumber(t[led .. "_blink_off_delay_2"]) or self.ledBlinkDelayDefault
|
||||
l.ledNetlinkDevice1 = t[led .. "_netdev_device_1"] or self.ledNetlinkDeviceDefault
|
||||
l.ledNetlinkDevice2 = t[led .. "_netdev_device_2"] or self.ledNetlinkDeviceDefault
|
||||
l.ledNetdevModeLink1 = self.ledNetdevModeLinkDefault
|
||||
l.ledNetdevModeTx1 = self.ledNetdevModeTxDefault
|
||||
l.ledNetdevModeRx1 = self.ledNetdevModeRxDefault
|
||||
l.ledNetdevModeLink2 = self.ledNetdevModeLinkDefault
|
||||
l.ledNetdevModeTx2 = self.ledNetdevModeTxDefault
|
||||
l.ledNetdevModeRx2 = self.ledNetdevModeRxDefault
|
||||
local ndm1 = t[led .. "_netdev_mode_1"]
|
||||
if ndm1 ~= nil and type(ndm1) == "table" then
|
||||
local enabledFlags = {}
|
||||
for _, v in ipairs(ndm1) do
|
||||
enabledFlags[v] = "1"
|
||||
end
|
||||
l.ledNetdevModeLink1 = enabledFlags.link or "0"
|
||||
l.ledNetdevModeTx1 = enabledFlags.tx or "0"
|
||||
l.ledNetdevModeRx1 = enabledFlags.rx or "0"
|
||||
end
|
||||
local ndm2 = t[led .. "_netdev_mode_2"]
|
||||
if ndm2 ~= nil and type(ndm2) == "table" then
|
||||
local enabledFlags = {}
|
||||
for _, v in ipairs(ndm2) do
|
||||
enabledFlags[v] = "1"
|
||||
end
|
||||
l.ledNetdevModeLink2 = enabledFlags.link or "0"
|
||||
l.ledNetdevModeTx2 = enabledFlags.tx or "0"
|
||||
l.ledNetdevModeRx2 = enabledFlags.rx or "0"
|
||||
end
|
||||
self:setLedAttrs(l)
|
||||
l.enabled = true
|
||||
else
|
||||
l.enabled = false
|
||||
end
|
||||
if l.enabled and not self:checkLed(l) then
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format(
|
||||
"%s: module disabled. LED '%s' is not available", self.name, l.ledName))
|
||||
end
|
||||
self._exit = false
|
||||
end
|
||||
end
|
||||
|
||||
function Module:checkLedTimer(t)
|
||||
return (unistd.access(t.ledDelayOnFile, "rw") and unistd.access(t.ledDelayOffFile, "rw"))
|
||||
end
|
||||
|
||||
function Module:checkLedNetdev(t)
|
||||
return (unistd.access(t.ledDeviceNameFile, "rw") and
|
||||
unistd.access(t.ledLinkFile, "rw") and
|
||||
unistd.access(t.ledRxFile, "rw") and
|
||||
unistd.access(t.ledTxFile, "rw"))
|
||||
end
|
||||
|
||||
function Module:setTriggerNone(t)
|
||||
self.writeValue(t.ledTriggerFile, "none")
|
||||
self.debugOutput(string.format(
|
||||
"%s: LED TRIGGER SET: none, %s", self.name, t.ledTriggerFile))
|
||||
end
|
||||
|
||||
function Module:setTriggerTimer(t, delayOn, delayOff)
|
||||
if not delayOn then
|
||||
delayOn = self.ledBlinkDelayDefault
|
||||
end
|
||||
if not delayOff then
|
||||
delayOff = self.ledBlinkDelayDefault
|
||||
end
|
||||
|
||||
self.writeValue(t.ledTriggerFile, "timer")
|
||||
|
||||
for i = 0, 10 do
|
||||
if self:checkLedTimer(t) then
|
||||
self.writeValue(t.ledDelayOnFile, delayOn)
|
||||
self.writeValue(t.ledDelayOffFile, delayOff)
|
||||
break
|
||||
else
|
||||
time.nanosleep({ tv_sec = 0, tv_nsec = 500000 })
|
||||
end
|
||||
end
|
||||
|
||||
self.debugOutput(string.format(
|
||||
"%s: LED TRIGGER SET: timer, %s; delayOn = %s, delayOff = %s",
|
||||
self.name, t.ledTriggerFile, tostring(delayOn), tostring(delayOff))
|
||||
)
|
||||
end
|
||||
|
||||
function Module:setTriggerNetdev(t, device, link, tx, rx)
|
||||
if not device then
|
||||
return
|
||||
end
|
||||
|
||||
self.writeValue(t.ledTriggerFile, "netdev")
|
||||
|
||||
for i = 0, 10 do
|
||||
if self:checkLedNetdev(t) then
|
||||
self.writeValue(t.ledDeviceNameFile, device)
|
||||
self.writeValue(t.ledLinkFile, link)
|
||||
self.writeValue(t.ledTxFile, tx)
|
||||
self.writeValue(t.ledRxFile, rx)
|
||||
break
|
||||
else
|
||||
time.nanosleep({ tv_sec = 0, tv_nsec = 500000 })
|
||||
end
|
||||
end
|
||||
|
||||
self.debugOutput(string.format(
|
||||
"%s: LED TRIGGER SET: netdev, %s; device = %s, link = %s, rx = %s, tx = %s",
|
||||
self.name, t.ledTriggerFile, tostring(device), tostring(link), tostring(rx), tostring(tx))
|
||||
)
|
||||
end
|
||||
|
||||
function Module:getCurrentTrigger(t)
|
||||
local trigger = self.readValue(t.ledTriggerFile)
|
||||
if trigger then
|
||||
if trigger:match("%[timer%]") then
|
||||
return "timer"
|
||||
elseif trigger:match("%[netdev%]") then
|
||||
return "netdev"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Module:getTriggerValues(t, trigger)
|
||||
local currentTrigger = self:getCurrentTrigger(t)
|
||||
if trigger == currentTrigger then
|
||||
if trigger == "timer" then
|
||||
return {
|
||||
trigger = currentTrigger,
|
||||
delayOn = tonumber(self.readValue(t.ledDelayOnFile)),
|
||||
delayOff = tonumber(self.readValue(t.ledDelayOffFile)),
|
||||
}
|
||||
elseif trigger == "netdev" then
|
||||
return {
|
||||
trigger = currentTrigger,
|
||||
device = self.readValue(t.ledDeviceNameFile),
|
||||
link = self.readValue(t.ledLinkFile),
|
||||
tx = self.readValue(t.ledTxFile),
|
||||
rx = self.readValue(t.ledRxFile),
|
||||
}
|
||||
end
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
function Module:on(t)
|
||||
self:setTriggerNone(t)
|
||||
self.writeValue(t.ledBrightnessFile, t.ledMaxBrightness)
|
||||
|
||||
self.debugOutput(string.format("%s: LED ON: %s", self.name, t.ledBrightnessFile))
|
||||
end
|
||||
|
||||
function Module:off(t)
|
||||
self:setTriggerNone(t)
|
||||
self.writeValue(t.ledBrightnessFile, "0")
|
||||
|
||||
self.debugOutput(string.format("%s: LED OFF: %s", self.name, t.ledBrightnessFile))
|
||||
end
|
||||
|
||||
function Module:getCurrentState(t)
|
||||
local state = self.readValue(t.ledBrightnessFile)
|
||||
if state and tonumber(state) > 0 then
|
||||
return tonumber(state)
|
||||
end
|
||||
end
|
||||
|
||||
function Module:ledRunFunc(t, currentStatus)
|
||||
if currentStatus == 0 then
|
||||
if t.ledAction1 == 1 then
|
||||
if self:getCurrentState(t) or self:getCurrentTrigger(t) then
|
||||
self:off(t)
|
||||
end
|
||||
elseif t.ledAction1 == 2 then
|
||||
if not self:getCurrentState(t) or self:getCurrentTrigger(t) then
|
||||
self:on(t)
|
||||
end
|
||||
elseif t.ledAction1 == 3 then
|
||||
local triggerValues = self:getTriggerValues(t, "timer")
|
||||
if (not next(triggerValues)) or (triggerValues.delayOn ~= t.ledBlinkOnDelay1 or
|
||||
triggerValues.delayOff ~= t.ledBlinkOffDelay1) then
|
||||
self:setTriggerTimer(t, t.ledBlinkOnDelay1, t.ledBlinkOffDelay1)
|
||||
end
|
||||
elseif t.ledAction1 == 4 then
|
||||
local triggerValues = self:getTriggerValues(t, "netdev")
|
||||
if (not next(triggerValues)) or (triggerValues.device ~= t.ledNetlinkDevice1 or
|
||||
triggerValues.link ~= t.ledNetdevModeLink1 or
|
||||
triggerValues.tx ~= t.ledNetdevModeTx1 or
|
||||
triggerValues.rx ~= t.ledNetdevModeRx1) then
|
||||
self:setTriggerNetdev(t,
|
||||
t.ledNetlinkDevice1, t.ledNetdevModeLink1,
|
||||
t.ledNetdevModeTx1, t.ledNetdevModeRx1
|
||||
)
|
||||
end
|
||||
end
|
||||
elseif currentStatus == 1 then
|
||||
if t.ledAction2 == 1 then
|
||||
if self:getCurrentState(t) or self:getCurrentTrigger(t) then
|
||||
self:off(t)
|
||||
end
|
||||
elseif t.ledAction2 == 2 then
|
||||
if not self:getCurrentState(t) or self:getCurrentTrigger(t) then
|
||||
self:on(t)
|
||||
end
|
||||
elseif t.ledAction2 == 3 then
|
||||
local triggerValues = self:getTriggerValues(t, "timer")
|
||||
if (not next(triggerValues)) or (triggerValues.delayOn ~= t.ledBlinkOnDelay2 or
|
||||
triggerValues.delayOff ~= t.ledBlinkOffDelay2) then
|
||||
self:setTriggerTimer(t, t.ledBlinkOnDelay2, t.ledBlinkOffDelay2)
|
||||
end
|
||||
elseif t.ledAction2 == 4 then
|
||||
local triggerValues = self:getTriggerValues(t, "netdev")
|
||||
if (not next(triggerValues)) or (triggerValues.device ~= t.ledNetlinkDevice2 or
|
||||
triggerValues.link ~= t.ledNetdevModeLink2 or
|
||||
triggerValues.tx ~= t.ledNetdevModeTx2 or
|
||||
triggerValues.rx ~= t.ledNetdevModeRx2) then
|
||||
self:setTriggerNetdev(t,
|
||||
t.ledNetlinkDevice2, t.ledNetdevModeLink2,
|
||||
t.ledNetdevModeTx2, t.ledNetdevModeRx2
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if not self._enabled then
|
||||
return
|
||||
end
|
||||
if self._counter == 0 or self._counter >= self.runInterval or currentStatus ~= lastStatus then
|
||||
for _, t in ipairs(self._leds) do
|
||||
if self._exit then
|
||||
break
|
||||
end
|
||||
if t.enabled then
|
||||
self:ledRunFunc(t, currentStatus)
|
||||
end
|
||||
end
|
||||
self._counter = 0
|
||||
end
|
||||
self._counter = self._counter + timeDiff
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
self._exit = true
|
||||
for _, l in ipairs(self._leds) do
|
||||
if l.ledPrevState then
|
||||
if l.ledPrevState.brightness then
|
||||
self.writeValue(l.ledBrightnessFile, l.ledPrevState.brightness)
|
||||
end
|
||||
if l.ledPrevState.trigger then
|
||||
self.writeValue(l.ledTriggerFile, l.ledPrevState.trigger)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Module
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_network_restart",
|
||||
runPrio = 30,
|
||||
config = {},
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
deadPeriod = 900,
|
||||
attempts = 1,
|
||||
attemptInterval = 15,
|
||||
deviceTimeout = 0,
|
||||
status = nil,
|
||||
_attemptsCounter = 0,
|
||||
_attemptIntervalCounter = 0,
|
||||
_deadCounter = 0,
|
||||
_firstAttempt = true,
|
||||
_ifaceRestarting = false,
|
||||
_ifaceRestartCounter = 0,
|
||||
_netIfaces = {},
|
||||
_netDevices = {},
|
||||
_netItemsNum = 0,
|
||||
_disconnectedAtStartup = false,
|
||||
}
|
||||
|
||||
function Module:toggleDevices(flag)
|
||||
if #self._netDevices == 0 then
|
||||
return
|
||||
end
|
||||
local ip = "/sbin/ip"
|
||||
if unistd.access(ip, "x") then
|
||||
for _, v in ipairs(self._netDevices) do
|
||||
os.execute(string.format("%s link set dev %s %s", ip, v, (flag and "up" or "down")))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Module:toggleIfaces(flag)
|
||||
if #self._netIfaces == 0 then
|
||||
return
|
||||
end
|
||||
for _, v in ipairs(self._netIfaces) do
|
||||
os.execute(string.format("%s %s", (flag and "/sbin/ifup" or "/sbin/ifdown"), v))
|
||||
end
|
||||
end
|
||||
|
||||
function Module:netItemsUp()
|
||||
self:toggleDevices(true)
|
||||
self:toggleIfaces(true)
|
||||
end
|
||||
|
||||
function Module:netItemsDown()
|
||||
self:toggleIfaces(false)
|
||||
self:toggleDevices(false)
|
||||
end
|
||||
|
||||
function Module:restartNetworkService()
|
||||
return os.execute("/etc/init.d/network restart")
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
if t.ifaces ~= nil and type(t.ifaces) == "table" then
|
||||
self._netIfaces = {}
|
||||
self._netDevices = {}
|
||||
self._netItemsNum = 0
|
||||
for k, v in ipairs(t.ifaces) do
|
||||
if v:match("^@") then
|
||||
self._netIfaces[#self._netIfaces + 1] = v:gsub("^@", "")
|
||||
else
|
||||
self._netDevices[#self._netDevices + 1] = v
|
||||
end
|
||||
self._netItemsNum = self._netItemsNum + 1
|
||||
end
|
||||
end
|
||||
if t.dead_period ~= nil then
|
||||
self.deadPeriod = tonumber(t.dead_period)
|
||||
end
|
||||
if t.attempts ~= nil then
|
||||
self.attempts = tonumber(t.attempts)
|
||||
end
|
||||
if t.attempt_interval ~= nil then
|
||||
self.attemptInterval = tonumber(t.attempt_interval)
|
||||
end
|
||||
if t.device_timeout ~= nil then
|
||||
self.deviceTimeout = tonumber(t.device_timeout)
|
||||
end
|
||||
if tonumber(t.disconnected_at_startup) == 1 then
|
||||
self._disconnectedAtStartup = true
|
||||
end
|
||||
end
|
||||
|
||||
function Module:networkRestartFunc()
|
||||
if self._netItemsNum > 0 then
|
||||
if #self._netIfaces > 0 then
|
||||
self.syslog("info", string.format("%s: restarting interfaces: %s",
|
||||
self.name, table.concat(self._netIfaces, ", ")))
|
||||
end
|
||||
if #self._netDevices > 0 then
|
||||
self.syslog("info", string.format("%s: restarting devices: %s",
|
||||
self.name, table.concat(self._netDevices, ", ")))
|
||||
end
|
||||
self:netItemsDown()
|
||||
if self.deviceTimeout < 1 then
|
||||
self:netItemsUp()
|
||||
else
|
||||
self._ifaceRestarting = true
|
||||
end
|
||||
else
|
||||
self.syslog("info", string.format(
|
||||
"%s: restarting network", self.name))
|
||||
self:restartNetworkService()
|
||||
end
|
||||
if self.attempts > 0 then
|
||||
self._attemptsCounter = self._attemptsCounter + 1
|
||||
end
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if self._ifaceRestarting then
|
||||
if self._ifaceRestartCounter >= self.deviceTimeout then
|
||||
self:netItemsUp()
|
||||
self._ifaceRestarting = false
|
||||
self._ifaceRestartCounter = 0
|
||||
else
|
||||
self._ifaceRestartCounter = self._ifaceRestartCounter + timeDiff
|
||||
end
|
||||
else
|
||||
if currentStatus == 1 then
|
||||
if self._disconnectedAtStartup and self._deadCounter >= self.deadPeriod then
|
||||
if self.attempts == 0 or self._attemptsCounter < self.attempts then
|
||||
if self._firstAttempt or self._attemptIntervalCounter >= self.attemptInterval then
|
||||
self:networkRestartFunc()
|
||||
self._attemptIntervalCounter = 0
|
||||
self._firstAttempt = false
|
||||
else
|
||||
self._attemptIntervalCounter = self._attemptIntervalCounter + timeDiff
|
||||
end
|
||||
end
|
||||
else
|
||||
self._deadCounter = self._deadCounter + timeDiff
|
||||
end
|
||||
else
|
||||
self._attemptsCounter = 0
|
||||
self._attemptIntervalCounter = 0
|
||||
self._deadCounter = 0
|
||||
self._disconnectedAtStartup = true
|
||||
self._firstAttempt = true
|
||||
end
|
||||
self._ifaceRestartCounter = 0
|
||||
end
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
@@ -0,0 +1,533 @@
|
||||
|
||||
local socket = require("posix.sys.socket")
|
||||
local stdlib = require("posix.stdlib")
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_public_ip",
|
||||
runPrio = 50,
|
||||
config = {
|
||||
noModules = false,
|
||||
debug = false,
|
||||
serviceConfig = {
|
||||
iface = nil,
|
||||
proxy_type = nil,
|
||||
proxy_host = nil,
|
||||
proxy_port = nil,
|
||||
},
|
||||
},
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
port = 53,
|
||||
runInterval = 600,
|
||||
runIntervalFailed = 60,
|
||||
runIntervalIPFailed = 1,
|
||||
requestAttempts = 2,
|
||||
timeout = 3,
|
||||
curlExec = "/usr/bin/curl",
|
||||
curlParams = '-s -g --no-keepalive --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0"',
|
||||
providers = {
|
||||
opendns1 = {
|
||||
name = "opendns1", type = "dns", host = "myip.opendns.com",
|
||||
server = "208.67.222.222", server6 = "2620:119:35::35",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
opendns2 = {
|
||||
name = "opendns2", type = "dns", host = "myip.opendns.com",
|
||||
server = "208.67.220.220", server6 = "2620:119:35::35",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
opendns3 = {
|
||||
name = "opendns3", type = "dns", host = "myip.opendns.com",
|
||||
server = "208.67.222.220", server6 = "2620:119:35::35",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
opendns4 = {
|
||||
name = "opendns4", type = "dns", host = "myip.opendns.com",
|
||||
server = "208.67.220.222", server6 = "2620:119:35::35",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
google = {
|
||||
name = "google", type = "dns", host = "o-o.myaddr.l.google.com",
|
||||
server = "ns1.google.com", server6 = "ns1.google.com",
|
||||
port = 53, queryType = "TXT", queryType6 = "TXT",
|
||||
},
|
||||
akamai = {
|
||||
name = "akamai", type = "dns", host = "whoami.akamai.net",
|
||||
server = "ns1-1.akamaitech.net", server6 = "ns1-1.akamaitech.net",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
akamai_http = {
|
||||
name = "akamai_http", type = "http", url = "http://whatismyip.akamai.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
amazonaws= {
|
||||
name = "amazonaws", type = "http", url = "http://checkip.amazonaws.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
wgetip= {
|
||||
name = "wgetip", type = "http", url = "http://wgetip.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
ifconfig= {
|
||||
name = "ifconfig", type = "http", url = "http://ifconfig.me/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
ipecho= {
|
||||
name = "ipecho", type = "http", url = "http://ipecho.net/plain",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
canhazip= {
|
||||
name = "canhazip", type = "http", url = "http://canhazip.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
icanhazip = {
|
||||
name = "icanhazip", type = "http", url = "http://icanhazip.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
},
|
||||
ipScript = "",
|
||||
enableIpScript = false,
|
||||
status = nil,
|
||||
_proxyString = "",
|
||||
_provider = nil,
|
||||
_qtype = false,
|
||||
_currentIp = nil,
|
||||
_lastResolvedIp = nil,
|
||||
_enabled = false,
|
||||
_counter = 0,
|
||||
_IPFalseCounter = 0,
|
||||
_interval = 600,
|
||||
_DNSPacket = nil,
|
||||
_requestIP = nil,
|
||||
}
|
||||
|
||||
function Module:runIpScript()
|
||||
if not self.config.noModules and self.enableIpScript and unistd.access(self.ipScript, "r") then
|
||||
stdlib.setenv("PUBLIC_IP", self.status)
|
||||
os.execute(string.format('/bin/sh "%s" &', self.ipScript))
|
||||
end
|
||||
end
|
||||
|
||||
function Module:getQueryType(type)
|
||||
local types = {
|
||||
A = 1,
|
||||
NS = 2,
|
||||
MD = 3,
|
||||
MF = 4,
|
||||
CNAME = 5,
|
||||
SOA = 6,
|
||||
MB = 7,
|
||||
MG = 8,
|
||||
MR = 9,
|
||||
NULL = 10,
|
||||
WKS = 11,
|
||||
PTS = 12,
|
||||
HINFO = 13,
|
||||
MINFO = 14,
|
||||
MX = 15,
|
||||
TXT = 16,
|
||||
AAAA = 28,
|
||||
}
|
||||
return types[type]
|
||||
end
|
||||
|
||||
function Module:buildMessage(address, queryType)
|
||||
if not queryType then
|
||||
queryType = "A"
|
||||
end
|
||||
queryType = self:getQueryType(queryType)
|
||||
|
||||
local addressString = ""
|
||||
for part in address:gmatch("[^.]+") do
|
||||
local t = {}
|
||||
for i in part:gmatch(".") do
|
||||
t[#t + 1] = i
|
||||
end
|
||||
addrLen = #part
|
||||
addrPart = table.concat(t)
|
||||
addressString = addressString .. string.char(addrLen) .. addrPart
|
||||
end
|
||||
|
||||
local data = (
|
||||
string.char(
|
||||
0xaa, 0xaa,
|
||||
0x01, 0x00,
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
) ..
|
||||
addressString ..
|
||||
string.char(
|
||||
0x00,
|
||||
0x00, queryType,
|
||||
0x00, 0x01
|
||||
)
|
||||
)
|
||||
return data
|
||||
end
|
||||
|
||||
function Module:sendUDPMessage(message, server, port)
|
||||
local success
|
||||
local retCode = 1
|
||||
local data
|
||||
|
||||
self.debugOutput(string.format("--- %s ---", self.name))
|
||||
|
||||
local saTable, errMsg, errNum = socket.getaddrinfo(server, port)
|
||||
|
||||
if not saTable then
|
||||
self.debugOutput(string.format(
|
||||
"GETADDRINFO ERROR: %s, %s", errMsg, errNum))
|
||||
else
|
||||
local family = saTable[1].family
|
||||
|
||||
if family then
|
||||
local sock, errMsg, errNum = socket.socket(family, socket.SOCK_DGRAM, 0)
|
||||
|
||||
if not sock then
|
||||
self.debugOutput(string.format(
|
||||
"SOCKET ERROR: %s, %s", errMsg, errNum))
|
||||
return retCode
|
||||
end
|
||||
|
||||
socket.setsockopt(sock, socket.SOL_SOCKET,
|
||||
socket.SO_SNDTIMEO, self.timeout, 0)
|
||||
socket.setsockopt(sock, socket.SOL_SOCKET,
|
||||
socket.SO_RCVTIMEO, self.timeout, 0)
|
||||
|
||||
if self.config.serviceConfig.iface then
|
||||
local ok, errMsg, errNum = socket.setsockopt(sock, socket.SOL_SOCKET,
|
||||
socket.SO_BINDTODEVICE, self.config.serviceConfig.iface)
|
||||
if not ok then
|
||||
self.debugOutput(string.format(
|
||||
"SOCKET ERROR: %s, %s", errMsg, errNum))
|
||||
unistd.close(sock)
|
||||
return retCode
|
||||
end
|
||||
end
|
||||
|
||||
local ok, errMsg, errNum = socket.sendto(sock, message, saTable[1])
|
||||
|
||||
local response = {}
|
||||
if ok then
|
||||
local ret, resp, errNum = socket.recvfrom(sock, 1024)
|
||||
data = ret
|
||||
if data then
|
||||
success = true
|
||||
response = resp
|
||||
else
|
||||
self.debugOutput(string.format(
|
||||
"SOCKET RECV ERROR: %s, %s", tostring(resp), tostring(errNum)))
|
||||
end
|
||||
else
|
||||
self.debugOutput(string.format(
|
||||
"SOCKET SEND ERROR: %s, %s", tostring(errMsg), tostring(errNum)))
|
||||
end
|
||||
|
||||
self.debugOutput(string.format(
|
||||
"--- UDP ---\ntime = %s\nconnection_timeout = %s\niface = %s\nserver = %s:%s\nsockname = %s:%s\nsuccess = %s",
|
||||
os.time(),
|
||||
self.timeout,
|
||||
tostring(self.config.serviceConfig.iface),
|
||||
server,
|
||||
tostring(port),
|
||||
tostring(response.addr),
|
||||
tostring(response.port),
|
||||
tostring(success))
|
||||
)
|
||||
|
||||
unistd.close(sock)
|
||||
retCode = success and 0 or 1
|
||||
end
|
||||
end
|
||||
return retCode, tostring(data)
|
||||
end
|
||||
|
||||
function Module:parseParts(message, start, parts)
|
||||
local partStart = start + 2
|
||||
local partLen = message:sub(start, start + 1)
|
||||
if #partLen == 0 then
|
||||
return parts
|
||||
end
|
||||
local partEnd = partStart + (tonumber(partLen, 16) * 2)
|
||||
parts[#parts + 1] = message:sub(partStart, partEnd - 1)
|
||||
if message:sub(partEnd, partEnd + 1) == "00" or partEnd > #message then
|
||||
return parts
|
||||
else
|
||||
return self:parseParts(message, partEnd, parts)
|
||||
end
|
||||
end
|
||||
|
||||
function Module:decodeMessage(message)
|
||||
local retTable = {}
|
||||
local t = {}
|
||||
for i = 1, #message do
|
||||
t[#t + 1] = string.format("%.2x", string.byte(message, i))
|
||||
end
|
||||
message = table.concat(t)
|
||||
|
||||
local ANCOUNT = message:sub(13, 16)
|
||||
local NSCOUNT = message:sub(17, 20)
|
||||
local ARCOUNT = message:sub(21, 24)
|
||||
|
||||
-- Question section
|
||||
local questionSectionStarts = 25
|
||||
local questionParts = self:parseParts(message, questionSectionStarts, {})
|
||||
local qtypeStarts = questionSectionStarts + (#table.concat(questionParts)) + (#questionParts * 2) + 1
|
||||
local qclassStarts = qtypeStarts + 4
|
||||
|
||||
-- Answer section
|
||||
local answerSectionStarts = qclassStarts + 4
|
||||
local numAnswers = math.max(
|
||||
tonumber(ANCOUNT, 16), tonumber(NSCOUNT, 16), tonumber(ARCOUNT, 16))
|
||||
|
||||
if numAnswers > 0 then
|
||||
for answerCount = 1, numAnswers do
|
||||
if answerSectionStarts < #message then
|
||||
local ATYPE = tonumber(
|
||||
message:sub(answerSectionStarts + 5, answerSectionStarts + 8), 16)
|
||||
local RDLENGTH = tonumber(
|
||||
message:sub(answerSectionStarts + 21, answerSectionStarts + 24), 16)
|
||||
local RDDATA = message:sub(
|
||||
answerSectionStarts + 25, answerSectionStarts + 24 + (RDLENGTH * 2))
|
||||
local RDDATA_decoded = ""
|
||||
|
||||
if #RDDATA > 0 then
|
||||
if ATYPE == self:getQueryType("A") or ATYPE == self:getQueryType("AAAA") then
|
||||
local octets = {}
|
||||
local sep = "."
|
||||
if #RDDATA > 8 then
|
||||
sep = ":"
|
||||
for i = 1, #RDDATA, 4 do
|
||||
local string = RDDATA:sub(i, i + 3)
|
||||
string = string:gsub("^00?0?", "")
|
||||
octets[#octets + 1] = string
|
||||
end
|
||||
else
|
||||
for i = 1, #RDDATA, 2 do
|
||||
octets[#octets + 1] = tonumber(RDDATA:sub(i, i + 1), 16)
|
||||
end
|
||||
end
|
||||
RDDATA_decoded = table.concat(octets, sep):gsub("0:[0:]+", "::", 1):gsub("::+", "::")
|
||||
else
|
||||
local rdata_t = {}
|
||||
for _, v in ipairs(self:parseParts(RDDATA, 1, {})) do
|
||||
local t = {}
|
||||
for i = 1, #v, 2 do
|
||||
t[#t + 1] = string.char(tonumber(v:sub(i, i + 1), 16))
|
||||
end
|
||||
rdata_t[#rdata_t + 1] = table.concat(t)
|
||||
end
|
||||
RDDATA_decoded = table.concat(rdata_t)
|
||||
end
|
||||
end
|
||||
answerSectionStarts = answerSectionStarts + 24 + (RDLENGTH * 2)
|
||||
|
||||
if RDDATA_decoded:match("^[a-fA-F0-9.:]+$") then
|
||||
retTable[#retTable + 1] = RDDATA_decoded
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return retTable
|
||||
end
|
||||
|
||||
function Module:requestIPDNS()
|
||||
local res
|
||||
local qtype = self._qtype and self._provider.queryType6 or self._provider.queryType
|
||||
local server = self._qtype and self._provider.server6 or self._provider.server
|
||||
local port = self._provider.port or self.port
|
||||
if not self._DNSPacket then
|
||||
self._DNSPacket = self:buildMessage(self._provider.host, qtype)
|
||||
end
|
||||
local retCode, response = self:sendUDPMessage(self._DNSPacket, server, port)
|
||||
if retCode == 0 and response then
|
||||
local retTable = self:decodeMessage(response)
|
||||
if #retTable > 0 then
|
||||
res = table.concat(retTable, ", ")
|
||||
end
|
||||
else
|
||||
self.syslog("warning", string.format(
|
||||
"%s: UDP error when requesting an IP address", self.name))
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function Module:httpRequest(url)
|
||||
local retCode = 1, data
|
||||
local curl = string.format(
|
||||
'%s%s%s --connect-timeout %s %s "%s"; printf "\n$?";',
|
||||
self.curlExec,
|
||||
self.config.serviceConfig.iface and (" --interface " .. self.config.serviceConfig.iface) or "",
|
||||
self._proxyString,
|
||||
self.timeout,
|
||||
self.curlParams,
|
||||
url
|
||||
)
|
||||
local fh = io.popen(curl, "r")
|
||||
if fh then
|
||||
data = fh:read("*a")
|
||||
fh:close()
|
||||
if data ~= nil then
|
||||
local s, e = data:find("[0-9]+\n?$")
|
||||
retCode = tonumber(data:sub(s))
|
||||
data = data:sub(0, s - 2)
|
||||
if not data or data == "" then
|
||||
data = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
retCode = 1
|
||||
end
|
||||
|
||||
self.debugOutput(string.format(
|
||||
"--- Curl ---\ntime = %s\n%s\nretCode = %s\ndata = [\n%s]\n",
|
||||
os.time(),
|
||||
curl,
|
||||
retCode,
|
||||
tostring(data))
|
||||
)
|
||||
return retCode, data
|
||||
end
|
||||
|
||||
function Module:parseHTTPResponse(data)
|
||||
data = data:gsub("^[%s%c]+", ""):gsub("[%s%c]+$", "")
|
||||
if data:match("^[a-fA-F0-9.:]+$") then
|
||||
return data
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function Module:requestIPHTTP()
|
||||
local res
|
||||
local url = self._provider.url
|
||||
local parseResponseFunc = self._provider.parseResponseFunc
|
||||
if url then
|
||||
local retCode, data = self:httpRequest(url)
|
||||
if retCode == 0 and data then
|
||||
if type(parseResponseFunc) == "function" then
|
||||
res = parseResponseFunc(data)
|
||||
else
|
||||
res = self:parseHTTPResponse(data)
|
||||
end
|
||||
else
|
||||
self.syslog("warning", string.format(
|
||||
"%s: HTTP error when requesting an IP address", self.name))
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
if t.interval ~= nil then
|
||||
self.runInterval = tonumber(t.interval)
|
||||
end
|
||||
if t.interval_failed ~= nil then
|
||||
self.runIntervalFailed = tonumber(t.interval_failed)
|
||||
end
|
||||
if t.request_attempts ~= nil then
|
||||
self.requestAttempts = tonumber(t.request_attempts)
|
||||
end
|
||||
if t.timeout ~= nil then
|
||||
self.timeout = tonumber(t.timeout)
|
||||
end
|
||||
if t.provider ~= nil then
|
||||
self._provider = self.providers[t.provider]
|
||||
else
|
||||
self._provider = self.providers.opendns1
|
||||
end
|
||||
if self.config.configDir then
|
||||
self.ipScript = string.format(
|
||||
"%s/public-ip-script.%s", self.config.configDir, self.config.serviceConfig.instance)
|
||||
if t.enable_ip_script ~= nil then
|
||||
self.enableIpScript = (tonumber(t.enable_ip_script) ~= 0)
|
||||
end
|
||||
end
|
||||
if t.qtype ~= nil then
|
||||
self._qtype = (tonumber(t.qtype) ~= 0)
|
||||
end
|
||||
self._currentIp = nil
|
||||
self._lastResolvedIp = nil
|
||||
self._DNSPacket = nil
|
||||
self._interval = self.runInterval
|
||||
self._IPFalseCounter = 0
|
||||
self._enabled = true
|
||||
if not self._provider then
|
||||
self._enabled = false
|
||||
else
|
||||
if self._provider.url and not unistd.access(self.curlExec, "x") then
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format(
|
||||
"%s: %s is not available. You need to install curl.", self.name, self.curlExec))
|
||||
end
|
||||
if self._provider.type == "dns" then
|
||||
self._requestIP = self.requestIPDNS
|
||||
elseif self._provider.type == "http" then
|
||||
self._requestIP = self.requestIPHTTP
|
||||
else
|
||||
self._enabled = false
|
||||
end
|
||||
end
|
||||
if (self.config.serviceConfig.proxy_type and
|
||||
self.config.serviceConfig.proxy_host and
|
||||
self.config.serviceConfig.proxy_port) then
|
||||
self._proxyString = string.format(
|
||||
" --proxy %s://%s:%d",
|
||||
self.config.serviceConfig.proxy_type,
|
||||
self.config.serviceConfig.proxy_host,
|
||||
self.config.serviceConfig.proxy_port)
|
||||
end
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if not self._enabled then
|
||||
return
|
||||
end
|
||||
if currentStatus == 0 then
|
||||
if self._counter == 0 or self._counter >= self._interval or currentStatus ~= lastStatus then
|
||||
local ip = self:_requestIP()
|
||||
if not ip then
|
||||
ip = ""
|
||||
self._IPFalseCounter = self._IPFalseCounter + 1
|
||||
if self._IPFalseCounter >= self.requestAttempts then
|
||||
self._interval = self.runIntervalFailed
|
||||
self._IPFalseCounter = 0
|
||||
else
|
||||
self._interval = self.runIntervalIPFailed
|
||||
end
|
||||
else
|
||||
self._interval = self.runInterval
|
||||
self._IPFalseCounter = 0
|
||||
end
|
||||
if ip ~= self._currentIp then
|
||||
self.status = ip
|
||||
if ip ~= "" then
|
||||
if self._counter > 0 and ip ~= self._lastResolvedIp then
|
||||
self.syslog(
|
||||
"notice",
|
||||
string.format("%s: public IP address changed to %s", self.name, ip)
|
||||
)
|
||||
self:runIpScript()
|
||||
end
|
||||
self._lastResolvedIp = ip
|
||||
end
|
||||
end
|
||||
self._currentIp = ip
|
||||
self._counter = 0
|
||||
end
|
||||
else
|
||||
self._currentIp = nil
|
||||
self.status = self._currentIp
|
||||
self._IPFalseCounter = 0
|
||||
self._counter = 0
|
||||
self._interval = self.runInterval
|
||||
end
|
||||
self._counter = self._counter + timeDiff
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_reboot",
|
||||
runPrio = 20,
|
||||
config = {},
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
deadPeriod = 3600,
|
||||
forceRebootDelay = 300,
|
||||
antiBootloopDelay = 300,
|
||||
status = nil,
|
||||
_deadCounter = 0,
|
||||
_rebooted = true,
|
||||
}
|
||||
|
||||
function Module:rebootDevice()
|
||||
self.syslog("warning", string.format("%s: reboot", self.name))
|
||||
os.execute("/sbin/reboot &")
|
||||
if self.forceRebootDelay > 0 then
|
||||
unistd.sleep(self.forceRebootDelay)
|
||||
self.syslog("warning", string.format("%s: force reboot", self.name))
|
||||
self.writeValue("/proc/sys/kernel/sysrq", "1")
|
||||
self.writeValue("/proc/sysrq-trigger", "b")
|
||||
end
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
if t.dead_period ~= nil then
|
||||
self.deadPeriod = tonumber(t.dead_period)
|
||||
end
|
||||
if t.force_reboot_delay ~= nil then
|
||||
self.forceRebootDelay = tonumber(t.force_reboot_delay)
|
||||
end
|
||||
if tonumber(t.disconnected_at_startup) == 1 then
|
||||
self._rebooted = false
|
||||
end
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if currentStatus == 1 then
|
||||
if not self._rebooted then
|
||||
if timeNow >= self.antiBootloopDelay and self._deadCounter >= self.deadPeriod then
|
||||
self:rebootDevice()
|
||||
self._rebooted = true
|
||||
else
|
||||
self._deadCounter = self._deadCounter + timeDiff
|
||||
end
|
||||
end
|
||||
else
|
||||
self._deadCounter = 0
|
||||
self._rebooted = false
|
||||
end
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
local stdlib = require("posix.stdlib")
|
||||
local time = require("posix.time")
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_regular_script",
|
||||
runPrio = 90,
|
||||
config = {},
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
inetState = 2, -- 0: connected, 1: disconnected, 2: both
|
||||
runInterval = 3600,
|
||||
script = "",
|
||||
status = nil,
|
||||
_nextTime = nil,
|
||||
_firstRun = true,
|
||||
}
|
||||
|
||||
function Module:runExternalScript(scriptPath, currentStatus)
|
||||
if unistd.access(scriptPath, "r") then
|
||||
stdlib.setenv("INET_STATE", currentStatus)
|
||||
os.execute(string.format('/bin/sh "%s" &', scriptPath))
|
||||
end
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
if t.inet_state ~= nil then
|
||||
self.inetState = tonumber(t.inet_state)
|
||||
end
|
||||
if t.interval ~= nil then
|
||||
self.runInterval = tonumber(t.interval)
|
||||
end
|
||||
if self.config.configDir then
|
||||
self.script = string.format(
|
||||
"%s/regular-script.%s", self.config.configDir, self.config.serviceConfig.instance)
|
||||
end
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if not self._nextTime then
|
||||
if timeNow < self.runInterval then
|
||||
self._nextTime = self.runInterval
|
||||
else
|
||||
self._nextTime = timeNow - (timeNow % self.runInterval) + self.runInterval
|
||||
end
|
||||
end
|
||||
if self._firstRun then
|
||||
self.status = time.strftime ("%Y-%m-%d %H:%M:%S %z", time.localtime(time.time() + self._nextTime - timeNow))
|
||||
self._firstRun = false
|
||||
end
|
||||
if timeNow >= self._nextTime then
|
||||
self._nextTime = self._nextTime + self.runInterval
|
||||
if self.inetState == 2 or (self.inetState == 0 and currentStatus == 0) or (self.inetState == 1 and currentStatus == 1) then
|
||||
self.status = time.strftime ("%Y-%m-%d %H:%M:%S %z", time.localtime(time.time() + self._nextTime - timeNow))
|
||||
self:runExternalScript(self.script, currentStatus)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
@@ -0,0 +1,134 @@
|
||||
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_user_scripts",
|
||||
runPrio = 80,
|
||||
config = {},
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
deadPeriod = 0,
|
||||
alivePeriod = 0,
|
||||
upScript = "",
|
||||
downScript = "",
|
||||
upScriptAttempts = 1,
|
||||
upScriptAttemptInterval = 15,
|
||||
downScriptAttempts = 1,
|
||||
downScriptAttemptInterval = 15,
|
||||
status = nil,
|
||||
_deadCounter = 0,
|
||||
_aliveCounter = 0,
|
||||
_upScriptAttemptsCounter = 0,
|
||||
_upScriptAttemptIntervalCounter = 0,
|
||||
_downScriptAttemptsCounter = 0,
|
||||
_downScriptAttemptIntervalCounter = 0,
|
||||
_upScriptFirstAttempt = true,
|
||||
_downScriptFirstAttempt = true,
|
||||
_disconnectedAtStartup = false,
|
||||
_connectedAtStartup = false,
|
||||
}
|
||||
|
||||
function Module:runExternalScript(scriptPath)
|
||||
if unistd.access(scriptPath, "r") then
|
||||
os.execute(string.format('/bin/sh "%s" &', scriptPath))
|
||||
end
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
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)
|
||||
end
|
||||
if t.up_script_attempts ~= nil then
|
||||
self.upScriptAttempts = tonumber(t.up_script_attempts)
|
||||
end
|
||||
if t.up_script_attempt_interval ~= nil then
|
||||
self.upScriptAttemptInterval = tonumber(t.up_script_attempt_interval)
|
||||
end
|
||||
if t.down_script_attempts ~= nil then
|
||||
self.downScriptAttempts = tonumber(t.down_script_attempts)
|
||||
end
|
||||
if t.down_script_attempt_interval ~= nil then
|
||||
self.downScriptAttemptInterval = tonumber(t.down_script_attempt_interval)
|
||||
end
|
||||
if self.config.configDir then
|
||||
self.upScript = string.format(
|
||||
"%s/up-script.%s", self.config.configDir, self.config.serviceConfig.instance)
|
||||
self.downScript = string.format(
|
||||
"%s/down-script.%s", self.config.configDir, self.config.serviceConfig.instance)
|
||||
end
|
||||
if tonumber(t.connected_at_startup) == 1 then
|
||||
self._connectedAtStartup = true
|
||||
end
|
||||
if tonumber(t.disconnected_at_startup) == 1 then
|
||||
self._disconnectedAtStartup = true
|
||||
end
|
||||
end
|
||||
|
||||
function Module:runUpScriptFunc()
|
||||
self:runExternalScript(self.upScript)
|
||||
if self.upScriptAttempts > 0 then
|
||||
self._upScriptAttemptsCounter = self._upScriptAttemptsCounter + 1
|
||||
end
|
||||
end
|
||||
|
||||
function Module:runDownScriptFunc()
|
||||
self:runExternalScript(self.downScript)
|
||||
if self.downScriptAttempts > 0 then
|
||||
self._downScriptAttemptsCounter = self._downScriptAttemptsCounter + 1
|
||||
end
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if currentStatus == 1 then
|
||||
self._upScriptAttemptsCounter = 0
|
||||
self._upScriptAttemptIntervalCounter = 0
|
||||
self._aliveCounter = 0
|
||||
self._connectedAtStartup = true
|
||||
self._upScriptFirstAttempt = true
|
||||
|
||||
if self._disconnectedAtStartup and self._deadCounter >= self.deadPeriod then
|
||||
if self.downScriptAttempts == 0 or self._downScriptAttemptsCounter < self.downScriptAttempts then
|
||||
if self._downScriptFirstAttempt or self._downScriptAttemptIntervalCounter >= self.downScriptAttemptInterval then
|
||||
self:runDownScriptFunc()
|
||||
self._downScriptAttemptIntervalCounter = 0
|
||||
self._downScriptFirstAttempt = false
|
||||
else
|
||||
self._downScriptAttemptIntervalCounter = self._downScriptAttemptIntervalCounter + timeDiff
|
||||
end
|
||||
end
|
||||
else
|
||||
self._deadCounter = self._deadCounter + timeDiff
|
||||
end
|
||||
elseif currentStatus == 0 then
|
||||
self._downScriptAttemptsCounter = 0
|
||||
self._downScriptAttemptIntervalCounter = 0
|
||||
self._deadCounter = 0
|
||||
self._disconnectedAtStartup = true
|
||||
self._downScriptFirstAttempt = true
|
||||
|
||||
if self._connectedAtStartup and self._aliveCounter >= self.alivePeriod then
|
||||
if self.upScriptAttempts == 0 or self._upScriptAttemptsCounter < self.upScriptAttempts then
|
||||
if self._upScriptFirstAttempt or self._upScriptAttemptIntervalCounter >= self.upScriptAttemptInterval then
|
||||
self:runUpScriptFunc()
|
||||
self._upScriptAttemptIntervalCounter = 0
|
||||
self._upScriptFirstAttempt = false
|
||||
else
|
||||
self._upScriptAttemptIntervalCounter = self._upScriptAttemptIntervalCounter + timeDiff
|
||||
end
|
||||
end
|
||||
else
|
||||
self._aliveCounter = self._aliveCounter + timeDiff
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
@@ -0,0 +1,46 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ipt2socks
|
||||
PKG_VERSION:=1.1.4
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/zfl9/ipt2socks/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=68dc76e63951d655c2fd9b420e175b5a75a50014d6db6e729398b41f2c988356
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
PKG_LICENSE:=AGPL-3.0
|
||||
PKG_LICENSE_FILE:=LICENSE
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/ipt2socks
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Convert iptables to socks5
|
||||
URL:=https://github.com/zfl9/ipt2socks
|
||||
DEPENDS:=+libpthread
|
||||
endef
|
||||
|
||||
define Package/ipt2socks/description
|
||||
Utility for converting iptables (redirect/tproxy) to socks5.
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS+= $(FPIC) -flto
|
||||
MAKE_FLAGS+= \
|
||||
CFLAGS="-std=c99 -pthread $(TARGET_CFLAGS)" \
|
||||
EVCFLAGS="$(TARGET_CFLAGS)"
|
||||
|
||||
define Package/ipt2socks/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ipt2socks $(1)/usr/bin
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,ipt2socks))
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
#
|
||||
# Copyright (C) 2006-2016 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=kismet
|
||||
PKG_VERSION:=2016-07-R1
|
||||
PKG_RELEASE:=6
|
||||
|
||||
PKG_LICENSE:=LGPLv2.1
|
||||
PKG_CPE_ID:=cpe:/a:kismet:kismet
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=http://www.kismetwireless.net/code
|
||||
PKG_HASH:=bdb21f153311f1ff3b16621bf0d6740f66369bf0982b0a289c9a12af8847e237
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/kismet/Default
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Kismet
|
||||
MAINTAINER:=Jean-Michel lacroix <lacroix@lepine-lacroix.info>
|
||||
DEPENDS:= +libstdcpp +libnl @BROKEN
|
||||
URL:=http://www.kismetwireless.net/
|
||||
SUBMENU:=Wireless
|
||||
endef
|
||||
|
||||
define Package/kismet/Default/description
|
||||
An 802.11 layer2 wireless network detector, sniffer, and intrusion
|
||||
detection system.
|
||||
endef
|
||||
|
||||
define Package/kismet-client/conffiles
|
||||
/etc/kismet/kismet.conf
|
||||
endef
|
||||
|
||||
define Package/kismet-drone/conffiles
|
||||
/etc/config/kismet_drone
|
||||
/etc/kismet/kismet_drone.conf
|
||||
endef
|
||||
|
||||
define Package/kismet-server/conffiles
|
||||
/etc/config/kismet_server
|
||||
/etc/kismet/kismet.conf
|
||||
endef
|
||||
|
||||
define Package/kismet-client
|
||||
$(call Package/kismet/Default)
|
||||
TITLE+= client
|
||||
DEPENDS+= +libncurses
|
||||
endef
|
||||
|
||||
define Package/kismet-client/description
|
||||
$(call Package/kismet/Default/description)
|
||||
This package contains the kismet text interface client.
|
||||
endef
|
||||
|
||||
define Package/kismet-drone
|
||||
$(call Package/kismet/Default)
|
||||
DEPENDS+= +libpcap +libpcre +libcap +wireless-tools
|
||||
TITLE+= drone
|
||||
endef
|
||||
|
||||
define Package/kismet-drone/description
|
||||
$(call Package/kismet/Default/description)
|
||||
This package contains the kismet remote sniffing.and monitoring drone.
|
||||
endef
|
||||
|
||||
define Package/kismet-server
|
||||
$(call Package/kismet/Default)
|
||||
DEPENDS+= +libpcap +libpcre +libcap +wireless-tools
|
||||
TITLE+= server
|
||||
endef
|
||||
|
||||
define Package/kismet-server/description
|
||||
$(call Package/kismet/Default/description)
|
||||
This package contains the kismet server.
|
||||
endef
|
||||
|
||||
CONFIGURE_ARGS += \
|
||||
--sysconfdir=/etc/kismet \
|
||||
|
||||
CONFIGURE_VARS += \
|
||||
CXXFLAGS="$$$$CXXFLAGS -fno-rtti" \
|
||||
LIBS="-lm" \
|
||||
CLIENTCLIBS="-lm"
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C $(PKG_BUILD_DIR) \
|
||||
LD="$(TARGET_CXX)" \
|
||||
all
|
||||
endef
|
||||
|
||||
define Package/kismet/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin/
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/scripts/kismet $(1)/usr/bin/kismet
|
||||
endef
|
||||
|
||||
define Package/kismet-client/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/kismet_client $(1)/usr/bin/
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/kismet/
|
||||
$(INSTALL_CONF) ./files/kismet.conf $(1)/etc/kismet/
|
||||
endef
|
||||
|
||||
define Package/kismet-drone/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/kismet_drone $(1)/usr/bin/
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/kismet/
|
||||
$(INSTALL_CONF) ./files/kismet_drone.conf $(1)/etc/kismet/
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/kismet_drone.config $(1)/etc/config/kismet_drone
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/kismet_drone.init $(1)/etc/init.d/kismet_drone
|
||||
endef
|
||||
|
||||
define Package/kismet-server/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/kismet_server $(1)/usr/bin/
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/kismet/
|
||||
$(INSTALL_CONF) ./files/kismet.conf $(1)/etc/kismet/
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/kismet_server.config $(1)/etc/config/kismet_server
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/kismet_server.init $(1)/etc/init.d/kismet_server
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,kismet-client))
|
||||
$(eval $(call BuildPackage,kismet-drone))
|
||||
$(eval $(call BuildPackage,kismet-server))
|
||||
@@ -0,0 +1,304 @@
|
||||
# Kismet config file
|
||||
# Most of the "static" configs have been moved to here -- the command line
|
||||
# config was getting way too crowded and cryptic. We want functionality,
|
||||
# not continually reading --help!
|
||||
|
||||
# Version of Kismet config
|
||||
version=2009-newcore
|
||||
|
||||
# Name of server (Purely for organizational purposes)
|
||||
# If commented out, defaults to host name of system
|
||||
# servername=Kismet Server
|
||||
|
||||
# Prefix of where we log (as used in the logtemplate later)
|
||||
logprefix=/tmp
|
||||
|
||||
# Do we process the contents of data frames? If this is enabled, data
|
||||
# frames will be truncated to the headers only immediately after frame type
|
||||
# detection. This will disable IP detection, etc, however it is likely
|
||||
# safer (and definitely more polite) if monitoring networks you do not own.
|
||||
# hidedata=true
|
||||
|
||||
# Do we allow plugins to be used? This will load plugins from the system
|
||||
# and user plugin directiories when set to true (See the README for the default
|
||||
# plugin locations).
|
||||
allowplugins=false
|
||||
|
||||
# See the README for full information on the new source format
|
||||
# ncsource=interface:options
|
||||
# for example:
|
||||
# ncsource=wifi0:type=madwifi
|
||||
# ncsource=wlan0:name=intel,hop=false,channel=11
|
||||
ncsource=wlan0
|
||||
|
||||
# Comma-separated list of sources to enable. This is only needed if you defined
|
||||
# multiple sources and only want to enable some of them. By default, all defined
|
||||
# sources are enabled.
|
||||
# For example, if sources with name=prismsource and name=ciscosource are defined,
|
||||
# and you only want to enable those two:
|
||||
# enablesources=prismsource,ciscosource
|
||||
|
||||
# Control which channels we like to spend more time on. By default, the list
|
||||
# of channels is pulled from the driver automatically. By setting preferred channels,
|
||||
# if they are present in the channel list, they'll be set with a timing delay so that
|
||||
# more time is spent on them. Since 1, 6, 11 are the common default channels, it makes
|
||||
# sense to spend more time monitoring them.
|
||||
# For finer control, see further down in the config for the channellist= directives.
|
||||
preferredchannels=1,6,11
|
||||
|
||||
# How many channels per second do we hop? (1-10)
|
||||
channelvelocity=3
|
||||
|
||||
# By setting the dwell time for channel hopping we override the channelvelocity
|
||||
# setting above and dwell on each channel for the given number of seconds.
|
||||
#channeldwell=10
|
||||
|
||||
# Channels are defined as:
|
||||
# channellist=name:ch1,ch2,ch3
|
||||
# or
|
||||
# channellist=name:range-start-end-width-offset,ch,range,ch,...
|
||||
#
|
||||
# Channels may be a numeric channel or a frequency
|
||||
#
|
||||
# Channels may specify an additional wait period. For common default channels,
|
||||
# an additional wait period can be useful. Wait periods delay for that number
|
||||
# of times per second - so a configuration hopping 10 times per second with a
|
||||
# channel of 6:3 would delay 3/10ths of a second on channel 6.
|
||||
#
|
||||
# Channel lists may have up to 256 channels and ranges (combined). For power
|
||||
# users scanning more than 256 channels with a single card, ranges must be used.
|
||||
#
|
||||
# Ranges are meant for "power users" who wish to define a very large number of
|
||||
# channels. A range may specify channels or frequencies, and will automatically
|
||||
# sort themselves to cover channels in a non-overlapping fashion. An example
|
||||
# range for the normal 802.11b/g spectrum would be:
|
||||
#
|
||||
# range-1-11-3-1
|
||||
#
|
||||
# which indicates starting at 1, ending at 11, a channel width of 3 channels,
|
||||
# incrementing by one. A frequency based definition would be:
|
||||
#
|
||||
# range-2412-2462-22-5
|
||||
#
|
||||
# since 11g channels are 22 mhz wide and 5 mhz apart.
|
||||
#
|
||||
# Ranges have the flaw that they cannot be shared between sources in a non-overlapping
|
||||
# way, so multiple sources using the same range may hop in lockstep with each other
|
||||
# and duplicate the coverage.
|
||||
#
|
||||
# channellist=demo:1:3,6:3,11:3,range-5000-6000-20-10
|
||||
|
||||
# Default channel lists
|
||||
# These channel lists MUST BE PRESENT for Kismet to work properly. While it is
|
||||
# possible to change these, it is not recommended. These are used when the supported
|
||||
# channel list can not be found for the source; to force using these instead of
|
||||
# the detected supported channels, override with channellist= in the source definition
|
||||
#
|
||||
# IN GENERAL, if you think you want to modify these, what you REALLY want to do is
|
||||
# copy them and use channellist= in the packet source.
|
||||
channellist=IEEE80211b:1:3,6:3,11:3,2,7,3,8,4,9,5,10
|
||||
channellist=IEEE80211a:36,40,44,48,52,56,60,64,149,153,157,161,165
|
||||
channellist=IEEE80211ab:1:3,6:3,11:3,2,7,3,8,4,9,5,10,36,40,44,48,52,56,60,64,149,153,157,161,165
|
||||
|
||||
# Client/server listen config
|
||||
listen=tcp://127.0.0.1:2501
|
||||
#listen=tcp://0.0.0.0:2501
|
||||
|
||||
# People allowed to connect, comma separated IP addresses or network/mask
|
||||
# blocks. Netmasks can be expressed as dotted quad (/255.255.255.0) or as
|
||||
# numbers (/24)
|
||||
allowedhosts=127.0.0.1
|
||||
# Maximum number of concurrent GUI's
|
||||
maxclients=5
|
||||
# Maximum backlog before we start throwing out or killing clients. The
|
||||
# bigger this number, the more memory and the more power it will use.
|
||||
maxbacklog=5000
|
||||
|
||||
# Server + Drone config options. To have a Kismet server export live packets
|
||||
# as if it were a drone, uncomment these.
|
||||
# dronelisten=tcp://127.0.0.1:3501
|
||||
# droneallowedhosts=127.0.0.1
|
||||
# dronemaxclients=5
|
||||
# droneringlen=65535
|
||||
|
||||
# OUI file, expected format 00:11:22<tab>manufname
|
||||
# IEEE OUI file used to look up manufacturer info. We default to the
|
||||
# wireshark one since most people have that.
|
||||
#ouifile=/usr/share/manuf
|
||||
|
||||
# Do we have a GPS?
|
||||
gps=false
|
||||
# Do we use a locally serial attached GPS, or use a gpsd server, or
|
||||
# use a fixed virtual gps?
|
||||
# (Pick only one)
|
||||
gpstype=gpsd
|
||||
# Host:port that GPSD is running on. This can be localhost OR remote!
|
||||
gpshost=localhost:2947
|
||||
|
||||
|
||||
# gpstype=serial
|
||||
# What serial device do we look for the GPS on?
|
||||
# gpsdevice=/dev/rfcomm0
|
||||
|
||||
# gpstype=virtual
|
||||
# gpsposition=100,-50
|
||||
# gpsaltitude=1234
|
||||
|
||||
# Do we lock the mode? This overrides coordinates of lock "0", which will
|
||||
# generate some bad information until you get a GPS lock, but it will
|
||||
# fix problems with GPS units with broken NMEA that report lock 0
|
||||
gpsmodelock=false
|
||||
# Do we try to reconnect if we lose our link to the GPS, or do we just
|
||||
# let it die and be disabled?
|
||||
gpsreconnect=true
|
||||
|
||||
# Do we export packets over tun/tap virtual interfaces?
|
||||
tuntap_export=false
|
||||
# What virtual interface do we use
|
||||
tuntap_device=kistap0
|
||||
|
||||
# Packet filtering options:
|
||||
# filter_tracker - Packets filtered from the tracker are not processed or
|
||||
# recorded in any way.
|
||||
# filter_export - Controls what packets influence the exported CSV, network,
|
||||
# xml, gps, etc files.
|
||||
# All filtering options take arguments containing the type of address and
|
||||
# addresses to be filtered. Valid address types are 'ANY', 'BSSID',
|
||||
# 'SOURCE', and 'DEST'. Filtering can be inverted by the use of '!' before
|
||||
# the address. For example,
|
||||
# filter_tracker=ANY(!"00:00:DE:AD:BE:EF")
|
||||
# has the same effect as the previous mac_filter config file option.
|
||||
# filter_tracker=...
|
||||
# filter_dump=...
|
||||
# filter_export=...
|
||||
# filter_netclient=...
|
||||
|
||||
# Alerts to be reported and the throttling rates.
|
||||
# alert=name,throttle/unit,burst
|
||||
# The throttle/unit describes the number of alerts of this type that are
|
||||
# sent per time unit. Valid time units are second, minute, hour, and day.
|
||||
# Burst describes the number of alerts sent before throttling takes place.
|
||||
# For example:
|
||||
# alert=FOO,10/min,5
|
||||
# Would allow 5 alerts through before throttling is enabled, and will then
|
||||
# limit the number of alerts to 10 per minute.
|
||||
# A throttle rate of 0 disables throttling of the alert.
|
||||
# See the README for a list of alert types.
|
||||
alert=ADHOCCONFLICT,5/min,1/sec
|
||||
alert=AIRJACKSSID,5/min,1/sec
|
||||
alert=APSPOOF,10/min,1/sec
|
||||
alert=BCASTDISCON,5/min,2/sec
|
||||
alert=BSSTIMESTAMP,5/min,1/sec
|
||||
alert=CHANCHANGE,5/min,1/sec
|
||||
alert=CRYPTODROP,5/min,1/sec
|
||||
alert=DISASSOCTRAFFIC,10/min,1/sec
|
||||
alert=DEAUTHFLOOD,5/min,2/sec
|
||||
alert=DEAUTHCODEINVALID,5/min,1/sec
|
||||
alert=DISCONCODEINVALID,5/min,1/sec
|
||||
alert=DHCPNAMECHANGE,5/min,1/sec
|
||||
alert=DHCPOSCHANGE,5/min,1/sec
|
||||
alert=DHCPCLIENTID,5/min,1/sec
|
||||
alert=DHCPCONFLICT,10/min,1/sec
|
||||
alert=NETSTUMBLER,5/min,1/sec
|
||||
alert=LUCENTTEST,5/min,1/sec
|
||||
alert=LONGSSID,5/min,1/sec
|
||||
alert=MSFBCOMSSID,5/min,1/sec
|
||||
alert=MSFDLINKRATE,5/min,1/sec
|
||||
alert=MSFNETGEARBEACON,5/min,1/sec
|
||||
alert=NULLPROBERESP,5/min,1/sec
|
||||
alert=PROBENOJOIN,5/min,1/sec
|
||||
|
||||
# Controls behavior of the APSPOOF alert. SSID may be a literal match (ssid=) or
|
||||
# a regex (ssidregex=) if PCRE was available when kismet was built. The allowed
|
||||
# MAC list must be comma-separated and enclosed in quotes if there are multiple
|
||||
# MAC addresses allowed. MAC address masks are allowed.
|
||||
#apspoof=Foo1:ssidregex="(?i:foobar)",validmacs=00:11:22:33:44:55
|
||||
#apspoof=Foo2:ssid="Foobar",validmacs="00:11:22:33:44:55,aa:bb:cc:dd:ee:ff"
|
||||
|
||||
# Known WEP keys to decrypt, bssid,hexkey. This is only for networks where
|
||||
# the keys are already known, and it may impact throughput on slower hardware.
|
||||
# Multiple wepkey lines may be used for multiple BSSIDs.
|
||||
# wepkey=00:DE:AD:C0:DE:00,FEEDFACEDEADBEEF01020304050607080900
|
||||
|
||||
# Is transmission of the keys to the client allowed? This may be a security
|
||||
# risk for some. If you disable this, you will not be able to query keys from
|
||||
# a client.
|
||||
allowkeytransmit=true
|
||||
|
||||
# How often (in seconds) do we write all our data files (0 to disable)
|
||||
writeinterval=10
|
||||
|
||||
# Do we use sound?
|
||||
# Not to be confused with GUI sound parameter, this controls wether or not the
|
||||
# server itself will play sound. Primarily for headless or automated systems.
|
||||
enablesound=false
|
||||
# Path to sound player
|
||||
soundbin=play
|
||||
|
||||
sound=newnet,true
|
||||
sound=newcryptnet,true
|
||||
sound=packet,true
|
||||
sound=gpslock,true
|
||||
sound=gpslost,true
|
||||
sound=alert,true
|
||||
|
||||
# Does the server have speech? (Again, not to be confused with the GUI's speech)
|
||||
enablespeech=false
|
||||
# Binary used for speech (if not in path, full path must be specified)
|
||||
speechbin=flite
|
||||
# Specify raw or festival; Flite (and anything else that doesn't need formatting
|
||||
# around the string to speak) is 'raw', festival requires the string be wrapped in
|
||||
# SayText("...")
|
||||
speechtype=raw
|
||||
|
||||
# How do we speak? Valid options:
|
||||
# speech Normal speech
|
||||
# nato NATO spellings (alpha, bravo, charlie)
|
||||
# spell Spell the letters out (aye, bee, sea)
|
||||
speechencoding=nato
|
||||
|
||||
speech=new,"New network detected s.s.i.d. %1 channel %2"
|
||||
speech=alert,"Alert %1"
|
||||
speech=gpslost,"G.P.S. signal lost"
|
||||
speech=gpslock,"G.P.S. signal O.K."
|
||||
|
||||
# How many alerts do we backlog for new clients? Only change this if you have
|
||||
# a -very- low memory system and need those extra bytes, or if you have a high
|
||||
# memory system and a huge number of alert conditions.
|
||||
alertbacklog=50
|
||||
|
||||
# File types to log, comma separated. Built-in log file types:
|
||||
# alert Text file of alerts
|
||||
# gpsxml XML per-packet GPS log
|
||||
# nettxt Networks in text format
|
||||
# netxml Networks in XML format
|
||||
# pcapdump tcpdump/wireshark compatible pcap log file
|
||||
# string All strings seen (increases CPU load)
|
||||
logtypes=pcapdump,gpsxml,netxml,alert
|
||||
|
||||
# Format of the pcap dump (PPI or 80211)
|
||||
pcapdumpformat=ppi
|
||||
# pcapdumpformat=80211
|
||||
|
||||
# Default log title
|
||||
logdefault=Kismet
|
||||
|
||||
# logtemplate - Filename logging template.
|
||||
# This is, at first glance, really nasty and ugly, but you'll hardly ever
|
||||
# have to touch it so don't complain too much.
|
||||
#
|
||||
# %p is replaced by the logging prefix + '/'
|
||||
# %n is replaced by the logging instance name
|
||||
# %d is replaced by the starting date as Mon-DD-YYYY
|
||||
# %D is replaced by the current date as YYYYMMDD
|
||||
# %t is replaced by the starting time as HH-MM-SS
|
||||
# %i is replaced by the increment log in the case of multiple logs
|
||||
# %l is replaced by the log type (pcapdump, strings, etc)
|
||||
# %h is replaced by the home directory
|
||||
|
||||
logtemplate=%p%n-%D-%t-%i.%l
|
||||
|
||||
# Where state info, etc, is stored. You shouldn't ever need to change this.
|
||||
# This is a directory.
|
||||
configdir=%h/.kismet/
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Kismet drone config file
|
||||
|
||||
version=newcore.1
|
||||
|
||||
# Name of drone server (informational)
|
||||
servername=Kismet-Drone
|
||||
|
||||
# Drone configuration
|
||||
# Protocol, interface, and port to listen on
|
||||
dronelisten=tcp://127.0.0.1:2502
|
||||
# Hosts allowed to connect, comma separated. May include netmasks.
|
||||
# allowedhosts=127.0.0.1,10.10.10.0/255.255.255.0
|
||||
droneallowedhosts=127.0.0.1
|
||||
# Maximum number of drone clients
|
||||
dronemaxclients=10
|
||||
droneringlen=65535
|
||||
|
||||
# Do we have a GPS?
|
||||
gps=true
|
||||
# Do we use a locally serial attached GPS, or use a gpsd server?
|
||||
# (Pick only one)
|
||||
gpstype=gpsd
|
||||
# gpstype=serial
|
||||
# What serial device do we look for the GPS on?
|
||||
gpsdevice=/dev/rfcomm0
|
||||
# Host:port that GPSD is running on. This can be localhost OR remote!
|
||||
gpshost=localhost:2947
|
||||
# Do we lock the mode? This overrides coordinates of lock "0", which will
|
||||
# generate some bad information until you get a GPS lock, but it will
|
||||
# fix problems with GPS units with broken NMEA that report lock 0
|
||||
gpsmodelock=false
|
||||
# Do we try to reconnect if we lose our link to the GPS, or do we just
|
||||
# let it die and be disabled?
|
||||
gpsreconnect=true
|
||||
|
||||
# See the README for full information on the new source format
|
||||
# ncsource=interface:options
|
||||
ncsource=null
|
||||
# for example:
|
||||
# ncsource=wlan0
|
||||
# ncsource=wifi0:type=madwifi
|
||||
# ncsource=wlan0:name=intel,hop=false,channel=11
|
||||
|
||||
# Special per-source options
|
||||
# sourceopts=[sourcename|*]:opt1,opt2
|
||||
# sourceopts=*:fuzzycrypt,weakvalidate
|
||||
|
||||
# Comma-separated list of sources to enable, if you don't want to enable all
|
||||
# the sources you defined.
|
||||
# enablesource=source1,source2
|
||||
|
||||
# How many channels per second do we hop? (1-10)
|
||||
channelvelocity=5
|
||||
|
||||
# By setting the dwell time for channel hopping we override the channelvelocity
|
||||
# setting above and dwell on each channel for the given number of seconds.
|
||||
#channeldwell=10
|
||||
|
||||
# Users outside the US might want to use this list:
|
||||
# channellist=IEEE80211b:1,7,13,2,8,3,14,9,4,10,5,11,6,12
|
||||
channellist=IEEE80211b:1:3,6:3,11:3,2,7,3,8,4,9,5,10
|
||||
|
||||
# US IEEE 80211a
|
||||
channellist=IEEE80211a:36,40,44,48,52,56,60,64,149,153,157,161,165
|
||||
|
||||
# Combo
|
||||
channellist=IEEE80211ab:1:3,6:3,11:3,2,7,3,8,4,9,5,10,36,40,44,48,52,56,60,64,149,153,157,161,165
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
config kismet_drone
|
||||
option enabled 0
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2013-2014 OpenWrt.org
|
||||
|
||||
START=99
|
||||
STOP=99
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/kismet_drone
|
||||
NAME=kismet_drone
|
||||
|
||||
kismet_drone_instance() {
|
||||
procd_open_instance
|
||||
procd_set_param command "${PROG}"
|
||||
procd_append_param command -f /etc/kismet/kismet_drone.conf -s
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load "${NAME}"
|
||||
config_foreach kismet_drone_instance
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
service_stop "${PROG}"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
config kismet_server
|
||||
option enabled 0
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2013-2014 OpenWrt.org
|
||||
|
||||
START=99
|
||||
STOP=99
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/kismet_server
|
||||
NAME=kismet_server
|
||||
|
||||
kismet_server_instance() {
|
||||
procd_open_instance
|
||||
procd_set_param command "${PROG}"
|
||||
procd_append_param command -f /etc/kismet/kismet.conf -s
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load "${NAME}"
|
||||
config_foreach kismet_server_instance
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
service_stop "${PROG}"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -5981,9 +5981,6 @@ fi
|
||||
|
||||
|
||||
|
||||
-# Add additional cflags since some distros bury panel.h
|
||||
-CPPFLAGS="$CPPFLAGS -I/usr/include/ncurses"
|
||||
-
|
||||
termcontrol="none";
|
||||
|
||||
if test "$wantclient" = "yes"; then
|
||||
@@ -0,0 +1,32 @@
|
||||
--- a/configfile.cc
|
||||
+++ b/configfile.cc
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
+#include <time.h>
|
||||
#include "configfile.h"
|
||||
#include "util.h"
|
||||
|
||||
--- a/dumpfile_tuntap.cc
|
||||
+++ b/dumpfile_tuntap.cc
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
-#include "dumpfile_tuntap.h"
|
||||
#include "ifcontrol.h"
|
||||
+#include "dumpfile_tuntap.h"
|
||||
#include "ipc_remote.h"
|
||||
|
||||
#ifndef SYS_CYGWIN
|
||||
--- a/ifcontrol.h
|
||||
+++ b/ifcontrol.h
|
||||
@@ -39,7 +39,6 @@
|
||||
#include <net/if_arp.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
-#include <netinet/if_ether.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
--- a/ipc_remote.cc
|
||||
+++ b/ipc_remote.cc
|
||||
@@ -914,7 +914,7 @@ int RootIPCRemote::OpenFDPassSock() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
- if (bind(ipc_fd_fd, (const struct sockaddr *) &unixsock, sizeof(unixsock))) {
|
||||
+ if (::bind(ipc_fd_fd, (const struct sockaddr *) &unixsock, sizeof(unixsock))) {
|
||||
close(ipc_fd_fd);
|
||||
_MSG("Failed to bind socket to pass file descriptors: " +
|
||||
string(strerror(errno)), MSGFLAG_ERROR);
|
||||
--- a/packetsource_wext.cc
|
||||
+++ b/packetsource_wext.cc
|
||||
@@ -405,7 +405,7 @@ void PacketSource_Wext::OpenWpaSupplican
|
||||
wpa_local.sun_family = AF_UNIX;
|
||||
snprintf(wpa_local.sun_path, sizeof(wpa_local.sun_path),
|
||||
"%s", wpa_local_path.c_str());
|
||||
- if (bind(wpa_sock, (struct sockaddr *) &wpa_local, sizeof(wpa_local)) < 0) {
|
||||
+ if (::bind(wpa_sock, (struct sockaddr *) &wpa_local, sizeof(wpa_local)) < 0) {
|
||||
_MSG("Source '" + parent + "' failed to bind local socket for "
|
||||
"wpa_supplicant, disabling scan_wpa: " + string(strerror(errno)),
|
||||
MSGFLAG_PRINTERROR);
|
||||
--- a/tcpclient.cc
|
||||
+++ b/tcpclient.cc
|
||||
@@ -75,7 +75,7 @@ int TcpClient::Connect(const char *in_re
|
||||
local_sock.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
local_sock.sin_port = htons(0);
|
||||
|
||||
- if (bind(cli_fd, (struct sockaddr *) &local_sock, sizeof(local_sock)) < 0) {
|
||||
+ if (::bind(cli_fd, (struct sockaddr *) &local_sock, sizeof(local_sock)) < 0) {
|
||||
snprintf(errstr, 1024, "TCP client bind() failed: %s", strerror(errno));
|
||||
globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
|
||||
close(cli_fd);
|
||||
@@ -170,7 +170,7 @@ int TcpClient::ConnectSync(const char *i
|
||||
local_sock.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
local_sock.sin_port = htons(0);
|
||||
|
||||
- if (bind(cli_fd, (struct sockaddr *) &local_sock, sizeof(local_sock)) < 0) {
|
||||
+ if (::bind(cli_fd, (struct sockaddr *) &local_sock, sizeof(local_sock)) < 0) {
|
||||
snprintf(errstr, 1024, "TCP client bind() failed: %s", strerror(errno));
|
||||
globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
|
||||
close(cli_fd);
|
||||
--- a/tcpserver.cc
|
||||
+++ b/tcpserver.cc
|
||||
@@ -84,7 +84,7 @@ int TcpServer::EnableServer() {
|
||||
}
|
||||
|
||||
// Bind the socket
|
||||
- if (bind(serv_fd, (struct sockaddr *) &serv_sock, sizeof(serv_sock)) < 0) {
|
||||
+ if (::bind(serv_fd, (struct sockaddr *) &serv_sock, sizeof(serv_sock)) < 0) {
|
||||
snprintf(errstr, STATUS_MAX, "TCP server bind() failed: %s",
|
||||
strerror(errno));
|
||||
globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
|
||||
--- a/unixdomainserver.cc
|
||||
+++ b/unixdomainserver.cc
|
||||
@@ -64,7 +64,7 @@ int UnixDomainServer::EnableServer() {
|
||||
}
|
||||
|
||||
// Bind the socket
|
||||
- if (bind(serv_fd, (struct sockaddr *) &serv_sock, sizeof(serv_sock)) < 0) {
|
||||
+ if (::bind(serv_fd, (struct sockaddr *) &serv_sock, sizeof(serv_sock)) < 0) {
|
||||
_MSG("Unix domain server bind() failed: " + string(strerror(errno)),
|
||||
MSGFLAG_ERROR);
|
||||
return -1;
|
||||
@@ -0,0 +1,12 @@
|
||||
--- a/ipc_remote.cc
|
||||
+++ b/ipc_remote.cc
|
||||
@@ -941,8 +941,8 @@ int RootIPCRemote::OpenFDPassSock() {
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
- struct cmsghdr header;
|
||||
int fd;
|
||||
+ struct cmsghdr header;
|
||||
} __attribute__((packed)) cmsg_fd;
|
||||
|
||||
int RootIPCRemote::SendDescriptor(int in_fd) {
|
||||
@@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2022-2023 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=neturl
|
||||
PKG_VERSION:=1.2-1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/golgote/neturl/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=fc4ea1b114125ae821bef385936cd12429485204576e77b6283692bd0cc9b1ab
|
||||
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICNESE_FILES:=LICENSE.txt
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/lua-neturl
|
||||
SUBMENU:=Lua
|
||||
SECTION:=lang
|
||||
CATEGORY:=Languages
|
||||
TITLE:=URL and Query string parser, builder, normalizer for Lua
|
||||
URL:=https://github.com/golgote/neturl
|
||||
DEPENDS:=+lua
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/lua-neturl/description
|
||||
This small Lua library provides a few functions to parse URL with
|
||||
querystring and build new URL easily.
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/lua-neturl/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua
|
||||
$(CP) $(PKG_BUILD_DIR)/lib/net/url.lua $(1)/usr/lib/lua/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,lua-neturl))
|
||||
@@ -0,0 +1,20 @@
|
||||
--- a/lib/net/url.lua
|
||||
+++ b/lib/net/url.lua
|
||||
@@ -340,7 +340,7 @@ function M:setAuthority(authority)
|
||||
self.password = v
|
||||
return ''
|
||||
end)
|
||||
- if string.find(userinfo, "^[%w%+%.]+$") then
|
||||
+ if string.find(userinfo, "^[%p%w%+%.]+$") then
|
||||
self.user = userinfo
|
||||
else
|
||||
-- incorrect userinfo
|
||||
@@ -369,7 +369,7 @@ function M.parse(url)
|
||||
comp.fragment = v
|
||||
return ''
|
||||
end)
|
||||
- url =url:gsub('^([%w][%w%+%-%.]*)%:', function(v)
|
||||
+ url =url:gsub('^([%w][%w%+%-%_%.]*)%:', function(v)
|
||||
comp.scheme = v:lower()
|
||||
return ''
|
||||
end)
|
||||
@@ -0,0 +1,18 @@
|
||||
# See /LICENSE for more information.
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_TITLE:=LuCI Bandix app for network traffic monitoring
|
||||
LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix
|
||||
|
||||
PKG_MAINTAINER:=timsaya
|
||||
|
||||
PKG_VERSION:=0.12.8
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,831 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require rpc';
|
||||
'require tools.widgets as widgets';
|
||||
|
||||
|
||||
// 暗色模式检测已改为使用 CSS 媒体查询 @media (prefers-color-scheme: dark)
|
||||
|
||||
// 简单的 Markdown 解析函数
|
||||
function parseMarkdown(text) {
|
||||
if (!text) return '';
|
||||
|
||||
// 转义 HTML 特殊字符
|
||||
function escapeHtml(html) {
|
||||
var div = document.createElement('div');
|
||||
div.textContent = html;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// 先处理代码块(避免其他格式化影响代码)
|
||||
var codeBlocks = [];
|
||||
var codeBlockIndex = 0;
|
||||
text = text.replace(/```([\s\S]*?)```/g, function (match, code) {
|
||||
var id = 'CODE_BLOCK_' + codeBlockIndex++;
|
||||
codeBlocks.push({
|
||||
id: id,
|
||||
content: escapeHtml(code.trim())
|
||||
});
|
||||
return id;
|
||||
});
|
||||
|
||||
// 处理行内代码
|
||||
text = text.replace(/`([^`]+)`/g, function (match, code) {
|
||||
return '<code style="background: rgba(0,0,0,0.1); padding: 2px 4px; border-radius: 3px; font-family: monospace; font-size: 0.9em;">' + escapeHtml(code) + '</code>';
|
||||
});
|
||||
|
||||
// 处理换行(GitHub markdown 使用 \r\n)
|
||||
text = text.replace(/\r\n/g, '\n');
|
||||
|
||||
// 处理标题
|
||||
text = text.replace(/^### (.*$)/gm, '<h3 style="font-size: 1.1em; font-weight: 600; margin: 12px 0 8px 0;">$1</h3>');
|
||||
text = text.replace(/^## (.*$)/gm, '<h2 style="font-size: 1.2em; font-weight: 600; margin: 14px 0 10px 0;">$1</h2>');
|
||||
text = text.replace(/^# (.*$)/gm, '<h1 style="font-size: 1.3em; font-weight: 600; margin: 16px 0 12px 0;">$1</h1>');
|
||||
|
||||
// 处理粗体
|
||||
text = text.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
||||
text = text.replace(/__(.+?)__/g, '<strong>$1</strong>');
|
||||
|
||||
// 处理斜体
|
||||
text = text.replace(/\*(.+?)\*/g, function (match, content) {
|
||||
// 避免匹配代码块中的 *
|
||||
if (match.indexOf('<code') === -1 && match.indexOf('</code>') === -1) {
|
||||
return '<em>' + content + '</em>';
|
||||
}
|
||||
return match;
|
||||
});
|
||||
text = text.replace(/_(.+?)_/g, function (match, content) {
|
||||
// 避免匹配代码块中的 _
|
||||
if (match.indexOf('<code') === -1 && match.indexOf('</code>') === -1) {
|
||||
return '<em>' + content + '</em>';
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
// 处理链接
|
||||
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener" style="color: #3b82f6; text-decoration: underline;">$1</a>');
|
||||
|
||||
// 处理图片
|
||||
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" style="max-width: 100%; height: auto; border-radius: 4px; margin: 8px 0;" />');
|
||||
|
||||
// 处理无序列表
|
||||
var lines = text.split('\n');
|
||||
var inList = false;
|
||||
var result = [];
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
var listMatch = line.match(/^[\s]*[-*+]\s+(.+)$/);
|
||||
|
||||
if (listMatch) {
|
||||
if (!inList) {
|
||||
result.push('<ul style="margin: 8px 0; padding-left: 20px;">');
|
||||
inList = true;
|
||||
}
|
||||
result.push('<li style="margin: 4px 0;">' + listMatch[1] + '</li>');
|
||||
} else {
|
||||
if (inList) {
|
||||
result.push('</ul>');
|
||||
inList = false;
|
||||
}
|
||||
result.push(line);
|
||||
}
|
||||
}
|
||||
if (inList) {
|
||||
result.push('</ul>');
|
||||
}
|
||||
text = result.join('\n');
|
||||
|
||||
// 处理有序列表
|
||||
lines = text.split('\n');
|
||||
inList = false;
|
||||
result = [];
|
||||
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
line = lines[i];
|
||||
var orderedMatch = line.match(/^[\s]*\d+\.\s+(.+)$/);
|
||||
|
||||
if (orderedMatch) {
|
||||
if (!inList) {
|
||||
result.push('<ol style="margin: 8px 0; padding-left: 20px;">');
|
||||
inList = true;
|
||||
}
|
||||
result.push('<li style="margin: 4px 0;">' + orderedMatch[1] + '</li>');
|
||||
} else {
|
||||
if (inList) {
|
||||
result.push('</ol>');
|
||||
inList = false;
|
||||
}
|
||||
result.push(line);
|
||||
}
|
||||
}
|
||||
if (inList) {
|
||||
result.push('</ol>');
|
||||
}
|
||||
text = result.join('\n');
|
||||
|
||||
// 处理段落(空行分隔)
|
||||
text = text.split(/\n\s*\n/).map(function (para) {
|
||||
para = para.trim();
|
||||
if (para && !para.match(/^<[h|u|o|l|i]/)) {
|
||||
return '<p style="margin: 8px 0; line-height: 1.6;">' + para + '</p>';
|
||||
}
|
||||
return para;
|
||||
}).join('\n');
|
||||
|
||||
// 处理单行换行(两个空格或反斜杠结尾)
|
||||
text = text.replace(/ \n/g, '<br />\n');
|
||||
text = text.replace(/\\\n/g, '<br />\n');
|
||||
|
||||
// 恢复代码块
|
||||
for (var j = 0; j < codeBlocks.length; j++) {
|
||||
var block = codeBlocks[j];
|
||||
text = text.replace(block.id, '<pre style="background: rgba(0,0,0,0.05); padding: 12px; border-radius: 4px; overflow-x: auto; font-family: monospace; font-size: 0.875rem; margin: 8px 0;"><code>' + block.content + '</code></pre>');
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// 声明 RPC 调用方法
|
||||
var callClearData = rpc.declare({
|
||||
object: 'luci.bandix',
|
||||
method: 'clearData',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callRestartService = rpc.declare({
|
||||
object: 'luci.bandix',
|
||||
method: 'restartService',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callGetVersion = rpc.declare({
|
||||
object: 'luci.bandix',
|
||||
method: 'getVersion',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callCheckUpdate = rpc.declare({
|
||||
object: 'luci.bandix',
|
||||
method: 'checkUpdate',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callInstallUpdate = rpc.declare({
|
||||
object: 'luci.bandix',
|
||||
method: 'installUpdate',
|
||||
params: ['package_type', 'download_url']
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
var self = this;
|
||||
return Promise.all([
|
||||
uci.load('bandix'),
|
||||
uci.load('network'),
|
||||
uci.load('luci'),
|
||||
uci.load('argon').catch(function () {
|
||||
// argon 配置可能不存在,忽略错误
|
||||
return null;
|
||||
}),
|
||||
callGetVersion().then(function (result) {
|
||||
// 存储版本信息到 view 实例
|
||||
self.versionInfo = result || {};
|
||||
return result;
|
||||
}).catch(function (err) {
|
||||
// 如果获取失败,使用默认值
|
||||
self.versionInfo = {
|
||||
luci_app_version: _('Unknown'),
|
||||
bandix_version: _('Unknown')
|
||||
};
|
||||
return null;
|
||||
})
|
||||
]);
|
||||
},
|
||||
|
||||
render: function (data) {
|
||||
var m, s, o;
|
||||
|
||||
// 确保UCI section存在,否则表单不会显示
|
||||
if (!uci.get('bandix', 'general')) {
|
||||
uci.add('bandix', 'general', 'general');
|
||||
}
|
||||
if (!uci.get('bandix', 'traffic')) {
|
||||
uci.add('bandix', 'traffic', 'traffic');
|
||||
}
|
||||
if (!uci.get('bandix', 'connections')) {
|
||||
uci.add('bandix', 'connections', 'connections');
|
||||
}
|
||||
if (!uci.get('bandix', 'dns')) {
|
||||
uci.add('bandix', 'dns', 'dns');
|
||||
}
|
||||
|
||||
// 创建表单
|
||||
m = new form.Map('bandix');
|
||||
|
||||
// 1. 基本设置部分 (general)
|
||||
s = m.section(form.NamedSection, 'general', 'general', _('Basic Settings'));
|
||||
s.description = _('Configure basic parameters for Bandix service');
|
||||
s.addremove = false;
|
||||
|
||||
// 添加端口设置选项
|
||||
o = s.option(form.Value, 'port', _('Port'),
|
||||
_('Port for Bandix service to listen on'));
|
||||
o.default = '8686';
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '8686';
|
||||
o.rmempty = false;
|
||||
|
||||
// 使用 OpenWrt 自带的设备选择器
|
||||
o = s.option(widgets.DeviceSelect, 'iface', _('Monitor Interface'),
|
||||
_('Select the LAN network interface to monitor'));
|
||||
o.rmempty = false;
|
||||
o.noaliases = true;
|
||||
o.nobridges = false;
|
||||
o.nocreate = true;
|
||||
|
||||
// 添加日志级别选择选项
|
||||
o = s.option(form.ListValue, 'log_level', _('Log Level'),
|
||||
_('Set the log level for Bandix service'));
|
||||
o.value('trace', 'Trace');
|
||||
o.value('debug', 'Debug');
|
||||
o.value('info', 'Info');
|
||||
o.value('warn', 'Warn');
|
||||
o.value('error', 'Error');
|
||||
o.default = 'info';
|
||||
o.rmempty = false;
|
||||
|
||||
// 添加数据目录设置
|
||||
o = s.option(form.Value, 'data_dir', _('Data Directory'),
|
||||
_('If you change the directory, please manually add it to the backup directory'));
|
||||
o.default = '/usr/share/bandix';
|
||||
o.datatype = 'string';
|
||||
o.placeholder = '/usr/share/bandix';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.ListValue, 'tc_backend', _('TC backend'),
|
||||
_('Select TC attach backend. Recommendation: use auto by default; tcx is preferred on kernel >= 6.6; netlink is safer on older kernels.'));
|
||||
o.value('auto', 'auto');
|
||||
o.value('tcx', 'tcx');
|
||||
o.value('netlink', 'netlink');
|
||||
o.default = 'auto';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.ListValue, 'tc_order', _('TC order'));
|
||||
o.value('first', 'first');
|
||||
o.value('default', 'default');
|
||||
o.value('last', 'last');
|
||||
o.value('before', 'before');
|
||||
o.value('after', 'after');
|
||||
o.default = 'default';
|
||||
o.rmempty = false;
|
||||
o.depends('tc_backend', 'tcx');
|
||||
|
||||
o = s.option(form.Value, 'netlink_priority', _('Netlink priority'),
|
||||
_('Only used when backend is netlink. Range: 0..65535 (0 means default).'));
|
||||
o.datatype = 'range(0,65535)';
|
||||
o.default = '0';
|
||||
o.placeholder = '0';
|
||||
o.rmempty = false;
|
||||
o.depends('tc_backend', 'netlink');
|
||||
|
||||
o = s.option(form.Value, 'tcx_anchor_ingress_id', _('TCX ingress anchor program id'),
|
||||
_('Used when tc_order is before/after. Must be a valid ingress program id on the same interface.'));
|
||||
o.datatype = 'uinteger';
|
||||
o.rmempty = true;
|
||||
o.depends({ tc_backend: 'tcx', tc_order: 'before' });
|
||||
o.depends({ tc_backend: 'tcx', tc_order: 'after' });
|
||||
|
||||
o = s.option(form.Value, 'tcx_anchor_egress_id', _('TCX egress anchor program id'),
|
||||
_('Used when tc_order is before/after. Must be a valid egress program id on the same interface.'));
|
||||
o.datatype = 'uinteger';
|
||||
o.rmempty = true;
|
||||
o.depends({ tc_backend: 'tcx', tc_order: 'before' });
|
||||
o.depends({ tc_backend: 'tcx', tc_order: 'after' });
|
||||
|
||||
// 添加版本信息显示(合并显示)
|
||||
o = s.option(form.DummyValue, 'version', _('Version'));
|
||||
o.cfgvalue = function () {
|
||||
var versionInfo = this.map.view.versionInfo || {};
|
||||
var luciVersion = versionInfo.luci_app_version || _('Unknown');
|
||||
var bandixVersion = versionInfo.bandix_version || _('Unknown');
|
||||
return 'luci-app-bandix: ' + luciVersion + ' / bandix: ' + bandixVersion;
|
||||
};
|
||||
|
||||
// 添加检查更新按钮
|
||||
var checkUpdateOption = s.option(form.Button, 'check_update', _('Check for Updates'));
|
||||
checkUpdateOption.inputtitle = _('Check for Updates');
|
||||
checkUpdateOption.inputstyle = 'apply';
|
||||
checkUpdateOption.onclick = function () {
|
||||
var button = this;
|
||||
var originalText = button.inputtitle;
|
||||
|
||||
// 显示加载状态
|
||||
button.inputtitle = _('Checking...');
|
||||
button.disabled = true;
|
||||
|
||||
return callCheckUpdate().then(function (result) {
|
||||
// 恢复按钮状态
|
||||
button.inputtitle = originalText;
|
||||
button.disabled = false;
|
||||
|
||||
if (!result) {
|
||||
ui.addNotification(null, E('p', _('Failed to check for updates')), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
var messages = [];
|
||||
var hasUpdate = false;
|
||||
|
||||
// 显示调试信息(架构、包管理器和资产数量)
|
||||
if (result.detected_arch) {
|
||||
var debugInfo = E('div', {
|
||||
'style': 'font-size: 0.75rem; color: #6b7280; margin-bottom: 12px; padding: 8px; background: rgba(0,0,0,0.02); border-radius: 4px;'
|
||||
}, [
|
||||
E('div', {}, _('Detected Architecture') + ': ' + result.detected_arch),
|
||||
E('div', {}, _('Package Manager') + ': ' + (result.detected_pkg_manager || _('Unknown'))),
|
||||
E('div', {}, _('LuCI Assets') + ': ' + (result.luci_asset_count || 0)),
|
||||
E('div', {}, _('Bandix Assets') + ': ' + (result.bandix_asset_count || 0))
|
||||
]);
|
||||
messages.push(debugInfo);
|
||||
}
|
||||
|
||||
// 检查 luci-app-bandix 更新
|
||||
if (result.luci_has_update === true || result.luci_has_update === '1' || result.luci_has_update === 1) {
|
||||
hasUpdate = true;
|
||||
var luciContainer = E('div', { 'style': 'margin-bottom: 20px;' });
|
||||
|
||||
var luciMsg = E('p', { 'style': 'font-weight: 600; margin-bottom: 8px;' },
|
||||
_('LuCI App has update: ') + (result.current_luci_version || _('Unknown')) +
|
||||
' → ' + (result.latest_luci_version || _('Unknown'))
|
||||
);
|
||||
luciContainer.appendChild(luciMsg);
|
||||
|
||||
// 显示更新日志(解析 Markdown)
|
||||
if (result.luci_release_body) {
|
||||
var changelogDiv = E('div', {
|
||||
'style': 'background: rgba(0,0,0,0.05); padding: 12px; border-radius: 4px; margin: 8px 0; max-height: 300px; overflow-y: auto; font-size: 0.875rem; line-height: 1.6;'
|
||||
});
|
||||
changelogDiv.innerHTML = parseMarkdown(result.luci_release_body);
|
||||
luciContainer.appendChild(changelogDiv);
|
||||
}
|
||||
|
||||
// 检查是否有错误(找不到匹配格式的包)
|
||||
if (result.luci_error) {
|
||||
var errorMsg = E('div', {
|
||||
'style': 'background: rgba(239, 68, 68, 0.1); color: #dc2626; padding: 10px; border-radius: 4px; margin: 8px 0; font-size: 0.875rem;'
|
||||
}, result.luci_error);
|
||||
luciContainer.appendChild(errorMsg);
|
||||
}
|
||||
|
||||
// 添加下载安装按钮(只有在有下载链接时才显示)
|
||||
if (result.luci_download_url) {
|
||||
// 创建状态提示区域
|
||||
var statusDiv = E('div', {
|
||||
'style': 'margin-top: 8px; padding: 10px; border-radius: 4px; display: none;'
|
||||
});
|
||||
luciContainer.appendChild(statusDiv);
|
||||
|
||||
var installBtn = E('button', {
|
||||
'class': 'btn cbi-button-action',
|
||||
'style': 'margin-top: 8px;',
|
||||
'click': function () {
|
||||
var installButton = this;
|
||||
var originalBtnText = installButton.textContent;
|
||||
|
||||
installButton.textContent = _('Installing...');
|
||||
installButton.disabled = true;
|
||||
|
||||
// 显示开始安装的提示
|
||||
statusDiv.style.display = 'block';
|
||||
statusDiv.style.background = 'rgba(59, 130, 246, 0.1)';
|
||||
statusDiv.style.color = '#1e40af';
|
||||
statusDiv.textContent = _('Starting installation... The page will refresh automatically in 30 seconds.');
|
||||
|
||||
// 对于 luci-app-bandix 安装,由于会重启 uhttpd/rpcd 导致连接断开,
|
||||
// 不等待响应,固定 30 秒后刷新页面,避免刷新过早未完成安装
|
||||
callInstallUpdate('luci', result.luci_download_url).catch(function (err) {
|
||||
});
|
||||
|
||||
setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 30000);
|
||||
}
|
||||
}, _('Download and Install'));
|
||||
luciContainer.appendChild(installBtn);
|
||||
}
|
||||
|
||||
// 添加手动下载链接
|
||||
if (result.luci_update_url) {
|
||||
var manualLink = E('a', {
|
||||
'href': result.luci_update_url,
|
||||
'target': '_blank',
|
||||
'style': 'display: inline-block; margin-left: 8px; margin-top: 8px;'
|
||||
}, _('Manual Download'));
|
||||
luciContainer.appendChild(manualLink);
|
||||
}
|
||||
|
||||
messages.push(luciContainer);
|
||||
} else {
|
||||
messages.push(E('p', {}, _('LuCI App is up to date: ') + (result.current_luci_version || result.latest_luci_version || _('Unknown'))));
|
||||
}
|
||||
|
||||
// 检查 bandix 更新
|
||||
if (result.bandix_has_update === true || result.bandix_has_update === '1' || result.bandix_has_update === 1) {
|
||||
hasUpdate = true;
|
||||
var bandixContainer = E('div', { 'style': 'margin-bottom: 20px;' });
|
||||
|
||||
var bandixMsg = E('p', { 'style': 'font-weight: 600; margin-bottom: 8px;' },
|
||||
_('Bandix has update: ') + (result.current_bandix_version || _('Unknown')) +
|
||||
' → ' + (result.latest_bandix_version || _('Unknown'))
|
||||
);
|
||||
bandixContainer.appendChild(bandixMsg);
|
||||
|
||||
// 显示更新日志(解析 Markdown)
|
||||
if (result.bandix_release_body) {
|
||||
var changelogDiv = E('div', {
|
||||
'style': 'background: rgba(0,0,0,0.05); padding: 12px; border-radius: 4px; margin: 8px 0; max-height: 300px; overflow-y: auto; font-size: 0.875rem; line-height: 1.6;'
|
||||
});
|
||||
changelogDiv.innerHTML = parseMarkdown(result.bandix_release_body);
|
||||
bandixContainer.appendChild(changelogDiv);
|
||||
}
|
||||
|
||||
// 检查是否有错误(找不到匹配格式的包)
|
||||
if (result.bandix_error) {
|
||||
var errorMsg = E('div', {
|
||||
'style': 'background: rgba(239, 68, 68, 0.1); color: #dc2626; padding: 10px; border-radius: 4px; margin: 8px 0; font-size: 0.875rem;'
|
||||
}, result.bandix_error);
|
||||
bandixContainer.appendChild(errorMsg);
|
||||
}
|
||||
|
||||
// 添加下载安装按钮(只有在有下载链接时才显示)
|
||||
if (result.bandix_download_url) {
|
||||
// 创建状态提示区域
|
||||
var statusDiv = E('div', {
|
||||
'style': 'margin-top: 8px; padding: 10px; border-radius: 4px; display: none;'
|
||||
});
|
||||
bandixContainer.appendChild(statusDiv);
|
||||
|
||||
var installBtn = E('button', {
|
||||
'class': 'btn cbi-button-action',
|
||||
'style': 'margin-top: 8px;',
|
||||
'click': function () {
|
||||
var installButton = this;
|
||||
var originalBtnText = installButton.textContent;
|
||||
|
||||
installButton.textContent = _('Installing...');
|
||||
installButton.disabled = true;
|
||||
|
||||
// 显示开始安装的提示
|
||||
statusDiv.style.display = 'block';
|
||||
statusDiv.style.background = 'rgba(59, 130, 246, 0.1)';
|
||||
statusDiv.style.color = '#1e40af';
|
||||
statusDiv.textContent = _('Starting installation...');
|
||||
|
||||
return callInstallUpdate('bandix', result.bandix_download_url).then(function (installResult) {
|
||||
// 判断成功:success 为 true 或 step 为 completed
|
||||
var isSuccess = installResult && (
|
||||
installResult.success === true ||
|
||||
installResult.success === 1 ||
|
||||
installResult.success === "true" ||
|
||||
installResult.success === "1" ||
|
||||
installResult.step === "completed"
|
||||
);
|
||||
|
||||
if (isSuccess) {
|
||||
installButton.textContent = originalBtnText;
|
||||
installButton.disabled = false;
|
||||
// 显示成功提示
|
||||
var successMsg = _('Bandix updated successfully!');
|
||||
if (installResult.message) {
|
||||
successMsg = installResult.message;
|
||||
}
|
||||
if (installResult.restart_exit_code !== undefined) {
|
||||
if (installResult.restart_exit_code === 0) {
|
||||
successMsg += ' ' + _('Service restarted successfully.');
|
||||
} else {
|
||||
successMsg += ' ' + _('Service restart may have failed. Please check manually.');
|
||||
}
|
||||
} else {
|
||||
successMsg += ' ' + _('Please restart the service.');
|
||||
}
|
||||
statusDiv.style.background = 'rgba(34, 197, 94, 0.1)';
|
||||
statusDiv.style.color = '#15803d';
|
||||
statusDiv.textContent = successMsg + ' ' + _('Page will refresh in 3 seconds.');
|
||||
// 3秒后刷新页面
|
||||
setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 3000);
|
||||
} else {
|
||||
installButton.textContent = originalBtnText;
|
||||
installButton.disabled = false;
|
||||
var errorMsg = installResult && installResult.error ? installResult.error : _('Installation failed');
|
||||
if (installResult && installResult.step) {
|
||||
errorMsg = _('Installation failed at step: ') + installResult.step + '. ' + errorMsg;
|
||||
}
|
||||
if (installResult && installResult.output) {
|
||||
// 截取输出信息的前500个字符,避免提示过长
|
||||
var output = installResult.output;
|
||||
if (output.length > 500) {
|
||||
output = output.substring(0, 500) + '...';
|
||||
}
|
||||
errorMsg += '\n' + _('Details: ') + output;
|
||||
}
|
||||
statusDiv.style.background = 'rgba(239, 68, 68, 0.1)';
|
||||
statusDiv.style.color = '#dc2626';
|
||||
statusDiv.innerHTML = errorMsg.replace(/\n/g, '<br>');
|
||||
}
|
||||
}).catch(function (err) {
|
||||
installButton.textContent = originalBtnText;
|
||||
installButton.disabled = false;
|
||||
var errorMsg = _('Installation failed: ') + (err.message || err || _('Unknown error'));
|
||||
statusDiv.style.background = 'rgba(239, 68, 68, 0.1)';
|
||||
statusDiv.style.color = '#dc2626';
|
||||
statusDiv.textContent = errorMsg;
|
||||
});
|
||||
}
|
||||
}, _('Download and Install'));
|
||||
bandixContainer.appendChild(installBtn);
|
||||
}
|
||||
|
||||
// 添加手动下载链接
|
||||
if (result.bandix_update_url) {
|
||||
var manualLink = E('a', {
|
||||
'href': result.bandix_update_url,
|
||||
'target': '_blank',
|
||||
'style': 'display: inline-block; margin-left: 8px; margin-top: 8px;'
|
||||
}, _('Manual Download'));
|
||||
bandixContainer.appendChild(manualLink);
|
||||
}
|
||||
|
||||
messages.push(bandixContainer);
|
||||
} else {
|
||||
messages.push(E('p', {}, _('Bandix is up to date: ') + (result.current_bandix_version || result.latest_bandix_version || _('Unknown'))));
|
||||
}
|
||||
|
||||
// 显示结果(添加关闭按钮)
|
||||
var title = hasUpdate ? _('Updates Available') : _('No Updates Available');
|
||||
|
||||
// 添加红色提示文字(如果有更新)
|
||||
if (hasUpdate) {
|
||||
var warningMsg = E('div', {
|
||||
'style': 'background: rgba(239, 68, 68, 0.1); color: #dc2626; padding: 12px; border-radius: 4px; margin-bottom: 16px; font-weight: 600;'
|
||||
}, _('Please clear your browser cache manually after updating.'));
|
||||
messages.unshift(warningMsg);
|
||||
}
|
||||
|
||||
// 创建弹窗内容,包含关闭按钮
|
||||
var modalContent = E('div', {}, messages);
|
||||
|
||||
// 添加关闭按钮
|
||||
var closeBtn = E('button', {
|
||||
'class': 'btn',
|
||||
'style': 'margin-top: 16px;',
|
||||
'click': ui.hideModal
|
||||
}, _('Close'));
|
||||
|
||||
modalContent.appendChild(closeBtn);
|
||||
|
||||
ui.showModal(title, modalContent);
|
||||
}).catch(function (err) {
|
||||
// 恢复按钮状态
|
||||
button.inputtitle = originalText;
|
||||
button.disabled = false;
|
||||
ui.addNotification(null, E('p', _('Failed to check for updates: ') + (err.message || err)), 'error');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// 添加清空数据按钮
|
||||
o = s.option(form.Button, 'clear_data', _('Clear Traffic Data'));
|
||||
o.inputtitle = _('Clear Traffic Data');
|
||||
o.inputstyle = 'reset';
|
||||
o.onclick = function () {
|
||||
return ui.showModal(_('Clear Traffic Data'), [
|
||||
E('p', _('Are you sure you want to clear all traffic data? This action cannot be undone.')),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.hideModal
|
||||
}, _('Cancel')),
|
||||
' ',
|
||||
E('button', {
|
||||
'class': 'btn cbi-button-negative',
|
||||
'click': function () {
|
||||
ui.hideModal();
|
||||
return callClearData()
|
||||
.then(function (result) {
|
||||
if (result && !result.success) {
|
||||
ui.addNotification(null, E('p', _('Failed to clear traffic data: ') + (result.error || 'Unknown error')), 'error');
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
ui.addNotification(null, E('p', _('Failed to clear traffic data: ') + err.message), 'error');
|
||||
});
|
||||
}
|
||||
}, _('Confirm'))
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
// 添加重启服务按钮
|
||||
o = s.option(form.Button, 'restart_service', _('Restart Service'));
|
||||
o.inputtitle = _('Restart Bandix Service');
|
||||
o.inputstyle = 'apply';
|
||||
o.onclick = function () {
|
||||
return ui.showModal(_('Restart Service'), [
|
||||
E('p', _('Are you sure you want to restart the Bandix service?')),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.hideModal
|
||||
}, _('Cancel')),
|
||||
' ',
|
||||
E('button', {
|
||||
'class': 'btn cbi-button-action',
|
||||
'click': function () {
|
||||
ui.hideModal();
|
||||
return callRestartService()
|
||||
.then(function (result) {
|
||||
if (result && !result.success) {
|
||||
ui.addNotification(null, E('p', _('Failed to restart service: ') + (result.error || 'Unknown error')), 'error');
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
ui.addNotification(null, E('p', _('Failed to restart service: ') + err.message), 'error');
|
||||
});
|
||||
}
|
||||
}, _('Confirm'))
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
// 添加意见反馈信息
|
||||
o = s.option(form.Button, 'feedback_info', _('Feedback'));
|
||||
o.inputtitle = _('Feedback');
|
||||
o.inputstyle = 'link';
|
||||
o.onclick = function () {
|
||||
window.open('https://github.com/timsaya', '_blank');
|
||||
return false;
|
||||
};
|
||||
|
||||
// 2. 流量监控设置部分 (traffic)
|
||||
s = m.section(form.NamedSection, 'traffic', 'traffic', _('Traffic Monitor Settings'));
|
||||
s.description = _('Configure traffic monitoring related parameters');
|
||||
s.addremove = false;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enable Traffic Monitoring'),
|
||||
_('Enable Bandix Traffic Monitor Service'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
// 添加网速单位选择选项
|
||||
o = s.option(form.ListValue, 'speed_unit', _('Speed Units'),
|
||||
_('Select the speed display unit format'));
|
||||
o.value('bytes', _('Bytes Units (B/s, KB/s, MB/s)'));
|
||||
o.value('bits', _('Bits Units (bps, Kbps, Mbps)'));
|
||||
o.default = 'bytes';
|
||||
o.rmempty = false;
|
||||
|
||||
// 添加离线超时时间(秒)
|
||||
// o = s.option(form.Value, 'offline_timeout', _('Offline Timeout'),
|
||||
// _('Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline'));
|
||||
// o.datatype = 'uinteger';
|
||||
// o.placeholder = '120';
|
||||
// o.default = '120';
|
||||
// o.rmempty = true;
|
||||
|
||||
// 添加持久化历史数据选项
|
||||
o = s.option(form.Flag, 'traffic_enable_storage', _('Persist History Data'),
|
||||
_('Enable data persistence functionality, data will only be persisted to disk when this option is enabled'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
// 添加数据 flush 间隔(秒)
|
||||
o = s.option(form.ListValue, 'traffic_flush_interval', _('Data Flush Interval'),
|
||||
_('Set the interval for flushing data to disk'));
|
||||
o.value('60', _('1 minute'));
|
||||
o.value('300', _('5 minutes'));
|
||||
o.value('600', _('10 minutes'));
|
||||
o.value('900', _('15 minutes'));
|
||||
o.value('1800', _('30 minutes'));
|
||||
o.value('3600', _('1 hour'));
|
||||
o.value('7200', _('2 hours'));
|
||||
o.value('43200', _('12 hours'));
|
||||
o.value('86400', _('24 hours'));
|
||||
o.default = '600';
|
||||
o.rmempty = false;
|
||||
o.depends('traffic_enable_storage', '1');
|
||||
|
||||
o = s.option(form.Flag, 'traffic_neighbor_flush_enable', _('Enable Neighbor Flush'),
|
||||
_('Enable periodic flush of neighbor table data. Generally not recommended if the device list works normally.'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.ListValue, 'traffic_neighbor_flush_interval', _('Neighbor Flush Interval'),
|
||||
_('Set the interval for flushing neighbor table data'));
|
||||
o.value('600', _('10 minutes'));
|
||||
o.value('900', _('15 minutes'));
|
||||
o.value('1800', _('30 minutes'));
|
||||
o.value('3600', _('1 hour'));
|
||||
o.value('7200', _('2 hours'));
|
||||
o.value('43200', _('12 hours'));
|
||||
o.value('86400', _('24 hours'));
|
||||
o.default = '600';
|
||||
o.rmempty = false;
|
||||
o.depends('traffic_neighbor_flush_enable', '1');
|
||||
|
||||
// 添加历史流量周期(秒)
|
||||
o = s.option(form.ListValue, 'traffic_realtime_window', _('Realtime Traffic Period'),
|
||||
_('Does not occupy storage space, stored only in memory'));
|
||||
o.value('600', _('10 minutes'));
|
||||
o.value('900', _('15 minutes'));
|
||||
o.value('1800', _('30 minutes'));
|
||||
o.value('3600', _('1 hour'));
|
||||
o.default = '600';
|
||||
o.rmempty = false;
|
||||
|
||||
// 添加流量导出URL选项
|
||||
o = s.option(form.Value, 'traffic_export_url', _('Traffic Push URL'),
|
||||
_('Push traffic data to a remote HTTP endpoint (POST JSON). Leave empty to disable.'));
|
||||
o.datatype = 'string';
|
||||
o.placeholder = '';
|
||||
o.rmempty = true;
|
||||
|
||||
// 添加设备事件导出URL选项
|
||||
o = s.option(form.Value, 'traffic_event_url', _('Device Event Push URL'),
|
||||
_('Push device online/offline events to a remote HTTP endpoint (POST JSON on state change). Leave empty to disable.'));
|
||||
o.datatype = 'string';
|
||||
o.placeholder = '';
|
||||
o.rmempty = true;
|
||||
|
||||
// 添加额外子网选项
|
||||
o = s.option(form.Value, 'traffic_additional_subnets', _('Additional Subnets'),
|
||||
_('Additional local subnets (comma-separated CIDR notation, e.g. \'192.168.2.0/24,10.0.0.0/8\'). Leave empty to use only interface subnet.'));
|
||||
o.datatype = 'string';
|
||||
o.placeholder = '192.168.2.0/24,10.0.0.0/8';
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.option(form.Flag, 'traffic_exclude_iface_device', _('Exclude Interface Device'),
|
||||
_('Exclude the router itself (interface device) from traffic statistics'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
// 3. 连接监控设置部分 (connections)
|
||||
s = m.section(form.NamedSection, 'connections', 'connections', _('Connection Monitor Settings'));
|
||||
s.description = _('Configure connection monitoring related parameters');
|
||||
s.addremove = false;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enable Connection Monitoring'),
|
||||
_('Enable Bandix connection monitoring'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
// 4. DNS监控设置部分 (dns)
|
||||
s = m.section(form.NamedSection, 'dns', 'dns', _('DNS Monitor Settings'));
|
||||
s.description = _('Configure DNS monitoring related parameters');
|
||||
s.addremove = false;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enable DNS Monitoring'),
|
||||
_('Enable Bandix DNS monitoring'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
// 添加DNS最大记录数选项
|
||||
o = s.option(form.Value, 'dns_max_records', _('DNS Max Records'),
|
||||
_('Set the maximum number of DNS query records to keep. Older records will be deleted when this limit is exceeded'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = '10000';
|
||||
o.default = '10000';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Flag, 'dns_enable_storage', _('Persist DNS Data'),
|
||||
_('Enable DNS data persistence functionality, data will only be persisted to disk when this option is enabled'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.ListValue, 'dns_flush_interval', _('DNS Data Flush Interval'),
|
||||
_('Set the interval for flushing DNS data to disk'));
|
||||
o.value('60', _('1 minute'));
|
||||
o.value('300', _('5 minutes'));
|
||||
o.value('600', _('10 minutes'));
|
||||
o.value('900', _('15 minutes'));
|
||||
o.value('1800', _('30 minutes'));
|
||||
o.value('3600', _('1 hour'));
|
||||
o.value('7200', _('2 hours'));
|
||||
o.value('43200', _('12 hours'));
|
||||
o.value('86400', _('24 hours'));
|
||||
o.default = '900';
|
||||
o.rmempty = false;
|
||||
o.depends('dns_enable_storage', '1');
|
||||
|
||||
// 将 view 实例关联到 form map,以便在 cfgvalue 中访问
|
||||
m.view = this;
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1798
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"admin/network/bandix": {
|
||||
"title": "Bandix",
|
||||
"order": 90,
|
||||
"action": {
|
||||
"type": "alias",
|
||||
"path": "admin/network/bandix/index"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-bandix" ],
|
||||
"uci": { "bandix": true }
|
||||
}
|
||||
},
|
||||
"admin/network/bandix/index": {
|
||||
"title": "Status",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "bandix/index"
|
||||
}
|
||||
},
|
||||
"admin/network/bandix/connection": {
|
||||
"title": "Connections",
|
||||
"order": 15,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "bandix/connection"
|
||||
}
|
||||
},
|
||||
"admin/network/bandix/dns": {
|
||||
"title": "DNS",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "bandix/dns"
|
||||
}
|
||||
},
|
||||
"admin/network/bandix/settings": {
|
||||
"title": "Settings",
|
||||
"order": 25,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "bandix/settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"bandix": {
|
||||
"title": "Bandix 流量监控",
|
||||
"description": "局域网设备流量监控和统计服务",
|
||||
"instances": {
|
||||
"instance": {
|
||||
"type": "process",
|
||||
"init": "bandix"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"luci-app-bandix": {
|
||||
"description": "Grant access to bandix traffic monitoring",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci.bandix": [
|
||||
"getStatus",
|
||||
"getMetrics",
|
||||
"getMetricsDay",
|
||||
"getMetricsWeek",
|
||||
"getMetricsMonth",
|
||||
"getConnection",
|
||||
"getConnectionFlows",
|
||||
"setHostname",
|
||||
"deleteDevice",
|
||||
"getDnsQueries",
|
||||
"getDnsStats",
|
||||
"getScheduleLimits",
|
||||
"setScheduleLimit",
|
||||
"deleteScheduleLimit",
|
||||
"clearData",
|
||||
"restartService",
|
||||
"getVersion",
|
||||
"getSystemArch",
|
||||
"checkUpdate",
|
||||
"installUpdate",
|
||||
"getTrafficUsageRanking",
|
||||
"getTrafficUsageIncrements",
|
||||
"getRateLimitWhitelist"
|
||||
]
|
||||
},
|
||||
"uci": [
|
||||
"bandix"
|
||||
]
|
||||
},
|
||||
"write": {
|
||||
"ubus": {
|
||||
"luci.bandix": [
|
||||
"getStatus",
|
||||
"getMetrics",
|
||||
"getMetricsDay",
|
||||
"getMetricsWeek",
|
||||
"getMetricsMonth",
|
||||
"getConnection",
|
||||
"getConnectionFlows",
|
||||
"setHostname",
|
||||
"deleteDevice",
|
||||
"getDnsQueries",
|
||||
"getDnsStats",
|
||||
"getScheduleLimits",
|
||||
"setScheduleLimit",
|
||||
"deleteScheduleLimit",
|
||||
"clearData",
|
||||
"restartService",
|
||||
"getVersion",
|
||||
"getSystemArch",
|
||||
"checkUpdate",
|
||||
"installUpdate",
|
||||
"getTrafficUsageRanking",
|
||||
"getTrafficUsageIncrements",
|
||||
"getRateLimitWhitelist",
|
||||
"getRateLimitWhitelistEnabled",
|
||||
"setRateLimitWhitelistEnabled",
|
||||
"addRateLimitWhitelist",
|
||||
"deleteRateLimitWhitelist",
|
||||
"setDefaultRateLimit",
|
||||
"updateScheduleLimit"
|
||||
]
|
||||
},
|
||||
"uci": [
|
||||
"bandix"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-banmac-ipt
|
||||
PKG_VERSION:=20211118
|
||||
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/luci-app-banmac-ipt
|
||||
SECTION:=luci
|
||||
CATEGORY:=LuCI
|
||||
SUBMENU:=3. Applications
|
||||
TITLE:=LuCI Support for Ban Client
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/luci-app-banmac-ipt/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
|
||||
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci
|
||||
$(INSTALL_DIR) $(1)/usr/banmac
|
||||
$(INSTALL_BIN) ./files/banmac/* $(1)/usr/banmac
|
||||
$(INSTALL_DIR) $(1)/etc
|
||||
$(INSTALL_CONF) ./files/banmaclog $(1)/etc/banmaclog
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/banmac.conf $(1)/etc/config/banmac
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,luci-app-banmac-ipt))
|
||||
Loaded 100 of 759 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user