mirror of
https://github.com/kenzok8/small-package.git
synced 2026-02-04 13:57:45 +08:00
update 2026-01-05 00:28:49
This commit is contained in:
@@ -7,13 +7,13 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=alist
|
||||
PKG_VERSION:=3.55.0
|
||||
PKG_WEB_VERSION:=3.55.0
|
||||
PKG_VERSION:=3.56.0
|
||||
PKG_WEB_VERSION:=3.56.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:=c9947567d4b2b19d63f3170ecab626a9c670a7d15965f88b14889a58e11900ab
|
||||
PKG_HASH:=bd4621a15e10e5c446d7bce44c31ba1be5be653f4d255f3724a2bedb53265bd9
|
||||
|
||||
PKG_LICENSE:=GPL-3.0
|
||||
PKG_LICENSE_FILE:=LICENSE
|
||||
@@ -23,7 +23,7 @@ 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:=1857caa4e64c0c9c2d92daea5953e8c5f2a5576e33e799b172a51e974f16fd6c
|
||||
HASH:=3afcfd6488ddbc112a37858b7e2a6a8c83711cc118af0072903280dd89995a81
|
||||
endef
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# AutoUpdate for Openwrt
|
||||
# Dependences: wget-ssl/wget/uclient-fetch curl jq expr sysupgrade
|
||||
|
||||
Version=V6.10.5
|
||||
Version=V6.10.6
|
||||
|
||||
function TITLE() {
|
||||
clear && echo "Openwrt-AutoUpdate Script by Hyy2001 ${Version}"
|
||||
@@ -22,19 +22,19 @@ function SHELL_HELP() {
|
||||
-f 跳过版本号校验,并强制刷写固件 ${Red}(危险)${White} *
|
||||
-F, --force-flash 强制刷写固件 ${Red}(危险)${White} *
|
||||
-P, --proxy 优先开启镜像加速下载固件 *
|
||||
${Next} A 自动选择镜像地址
|
||||
${Next} E ghproxy.cn
|
||||
${Next} F ghps.cc
|
||||
${Next} G ghgo.xyz
|
||||
${Next} A 自动选择镜像站
|
||||
${Next} E [镜像站] ghproxy.cn
|
||||
${Next} F [镜像站] ghps.cc
|
||||
${Next} G [镜像站] ghgo.xyz
|
||||
-D <Downloader> 使用指定的下载器 <wget-ssl | wget | curl | uclient-fetch> *
|
||||
--decompress 解压 img.gz 固件后再更新固件 *
|
||||
--skip-verify 跳过固件 SHA256 校验 ${Red}(危险)${White} *
|
||||
--path <PATH> 固件下载到用户提供的绝对路径 <PATH> *
|
||||
|
||||
更新程序:
|
||||
-x 更新 autoupdate 程序
|
||||
-x -path <PATH> 更新 autoupdate 程序 (保存程序到用户提供的绝对路径 <PATH>) *
|
||||
-x -url <URL> 更新 autoupdate 程序 (使用提供的地址 <URL> 更新程序) *
|
||||
-x 自动更新 autoupdate 程序
|
||||
-x -path <PATH> 自动更新 autoupdate 程序 (保存程序到用户提供的绝对路径 <PATH>) *
|
||||
-x -url <URL> 手动更新 autoupdate 程序 (使用用户提供的地址 <URL> 更新 autoupdate 程序) *
|
||||
|
||||
其他参数:
|
||||
--help 打印 AutoUpdate 程序帮助信息
|
||||
@@ -70,7 +70,7 @@ function SHOW_VARIABLE() {
|
||||
固件版本: ${OP_VERSION} *
|
||||
固件标签: ${TARGET_FLAG} *
|
||||
内核版本: $(uname -r)
|
||||
运行内存: Memory: $(MEMINFO Mem)M | swap: $(MEMINFO Swap)M | Total: $(MEMINFO All)M
|
||||
运行内存: Memory: $(MEMINFO Mem)MB | Swap: $(MEMINFO Swap)MB | Total: $(MEMINFO All)MB
|
||||
其他参数: ${TARGET_BOARD} / ${TARGET_SUBTARGET}
|
||||
固件作者: ${Author} *
|
||||
作者仓库: ${Github} *
|
||||
@@ -89,48 +89,110 @@ EOF
|
||||
}
|
||||
|
||||
function MEMINFO() {
|
||||
[[ ! $1 || ! $* =~ (All|Mem|Swap) ]] && return 1
|
||||
# 只接受 Mem / Swap / All
|
||||
[[ -z "$1" || ! "$1" =~ ^(Mem|Swap|All)$ ]] && return 1
|
||||
local want="$1"
|
||||
|
||||
local Mem Swap All Result
|
||||
Mem=$(free | grep Mem: | awk '{Mem=$7/1024} {printf("%.0f\n",Mem)}' 2> /dev/null)
|
||||
Swap=$(free | grep Swap: | awk '{Swap=$4/1024} {printf("%.0f\n",Swap)}' 2> /dev/null)
|
||||
All=$(expr ${Mem} + ${Swap} 2> /dev/null)
|
||||
Result=$(eval echo '$'$1)
|
||||
if [[ ${Result} ]]
|
||||
then
|
||||
LOGGER "[$1] 可用运行内存: [${Result}M]"
|
||||
echo ${Result}
|
||||
|
||||
# 只调用一次 free,用 awk 同时解析:
|
||||
# - Mem: 优先取最后一列(一般是 available),若没有 available 列则退回 free 列
|
||||
# - Swap: 取 free 列(通常是第4列),若列不够则取最后一列
|
||||
read -r Mem Swap < <(
|
||||
free 2>/dev/null | awk '
|
||||
$1=="Mem:" {
|
||||
# 通常 NF>=7 时最后一列是 available,更符合“可用内存”概念
|
||||
mem_kb = (NF>=7 ? $(NF) : $4)
|
||||
next
|
||||
}
|
||||
$1=="Swap:" {
|
||||
swap_kb = ($4 ? $4 : $(NF))
|
||||
next
|
||||
}
|
||||
END{
|
||||
# free 默认一般是 KiB,这里换算成 MB(与你原逻辑一致)
|
||||
if(mem_kb=="") mem_kb=0
|
||||
if(swap_kb=="") swap_kb=0
|
||||
printf "%.0f %.0f\n", mem_kb/1024, swap_kb/1024
|
||||
}'
|
||||
)
|
||||
|
||||
# 兜底,避免空值导致算术报错
|
||||
[[ "$Mem" =~ ^[0-9]+$ ]] || Mem=0
|
||||
[[ "$Swap" =~ ^[0-9]+$ ]] || Swap=0
|
||||
|
||||
All=$((Mem + Swap))
|
||||
Result="${!want}"
|
||||
|
||||
if [[ "$Result" =~ ^[0-9]+$ ]]; then
|
||||
LOGGER "[$want] 可用运行内存: [${Result}M]"
|
||||
echo "$Result"
|
||||
return 0
|
||||
else
|
||||
LOGGER "[$1] 可用内存获取失败!"
|
||||
LOGGER "[$want] 可用内存获取失败!"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function SPACEINFO() {
|
||||
[[ ! $1 ]] && return 1
|
||||
local Result Path
|
||||
Path="$(awk -F '/' '{print $2}' <<< $1)"
|
||||
Result="$(df -m /${Path} 2> /dev/null | grep -v Filesystem | awk '{print $4}')"
|
||||
if [[ ${Result} ]]
|
||||
then
|
||||
LOGGER "[/${Path}] 可用存储空间: [${Result}M]"
|
||||
echo "${Result}"
|
||||
local path="$1"
|
||||
local Result
|
||||
|
||||
# 目标:不存在就输出 0
|
||||
if [[ -z "$path" || ! -e "$path" ]]; then
|
||||
LOGGER "[${path:-<empty>}] 路径不存在,可用存储空间: [0M]"
|
||||
echo 0
|
||||
return 1
|
||||
fi
|
||||
|
||||
# df -P 保证格式稳定;-m 输出单位 MB(OpenWrt busybox 通常也支持 -m)
|
||||
Result="$(df -Pm -- "$path" 2>/dev/null | awk 'NR==2{print $4}')"
|
||||
|
||||
# 兜底:df 失败/异常输出 → 0
|
||||
if [[ "$Result" =~ ^[0-9]+$ ]]; then
|
||||
LOGGER "[$path] 可用存储空间: [${Result}M]"
|
||||
echo "$Result"
|
||||
return 0
|
||||
else
|
||||
LOGGER "[/${Path}] 可用存储空间获取失败!"
|
||||
LOGGER "[$path] 可用存储空间获取失败,输出 0"
|
||||
echo 0
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function RM() {
|
||||
for i in $@
|
||||
do
|
||||
rm -r "$i" 2> /dev/null
|
||||
LOGGER "删除文件: [$i]"
|
||||
local i
|
||||
|
||||
for i in "$@"; do
|
||||
# 1) 空参数直接跳过(防止 rm -r "" 这类异常)
|
||||
[[ -z "$i" ]] && { LOGGER "删除跳过: [空路径]"; continue; }
|
||||
|
||||
# 2) 禁止危险路径(兜底防止误删系统)
|
||||
case "$i" in
|
||||
/|/.|/..|.|..)
|
||||
LOGGER "删除拒绝(危险路径): [$i]"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
# 3) 不存在就跳过(你的需求)
|
||||
# -e: 存在(文件/目录); -L: 断链符号链接也算“存在可删”
|
||||
if [[ ! -e "$i" && ! -L "$i" ]]; then
|
||||
LOGGER "删除跳过(不存在): [$i]"
|
||||
continue
|
||||
fi
|
||||
|
||||
# 4) 执行删除(文件/目录都覆盖)
|
||||
rm -rf -- "$i" 2>/dev/null
|
||||
if [[ $? == 0 ]]; then
|
||||
LOGGER "删除成功: [$i]"
|
||||
else
|
||||
LOGGER "删除失败: [$i]"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
GET_DOWNLOADER() {
|
||||
function GET_DOWNLOADER() {
|
||||
for i in ${DL_DEPENDS[@]}
|
||||
do
|
||||
if [[ $(CHECK_PKG $i) == true ]]
|
||||
@@ -262,7 +324,7 @@ function LOGGER() {
|
||||
fi
|
||||
}
|
||||
|
||||
function RANDOM() {
|
||||
function RANDOM_HEX() {
|
||||
head -n 5 /dev/urandom | md5sum | cut -c 1-$1
|
||||
}
|
||||
|
||||
@@ -278,55 +340,84 @@ function GET_SHA256SUM() {
|
||||
}
|
||||
|
||||
function GET_VARIABLE() {
|
||||
local Result="$(grep "$1=" $2 2> /dev/null | grep -v "#" | awk -F '=' '{print $2}')"
|
||||
if [[ ${Result} && $? == 0 ]]
|
||||
then
|
||||
eval echo "${Result}"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
local key="$1" file="$2"
|
||||
local Result
|
||||
Result="$(grep -E "^${key}=" "$file" 2>/dev/null | tail -n1 | cut -d= -f2-)"
|
||||
[[ -n "$Result" ]] && { echo "$Result"; return 0; }
|
||||
return 1
|
||||
}
|
||||
|
||||
function EDIT_VARIABLE() {
|
||||
local Mode=$1
|
||||
local Mode="$1"
|
||||
shift
|
||||
[[ ! -s $1 ]] && ECHO r "未检测到环境变量文件: [$1] !" && return 1
|
||||
case "${Mode}" in
|
||||
edit)
|
||||
if [[ ! $(GET_VARIABLE $2 $1) ]]
|
||||
then
|
||||
LOGGER "[EDIT_VARIABLE] 新增环境变量 [$2 = $3]"
|
||||
echo -e "\n$2=$3" >> $1
|
||||
RETURN=0
|
||||
else
|
||||
sed -i "s?$(GET_VARIABLE $2 $1)?$3?g" $1 2> /dev/null
|
||||
if [[ $? == 0 ]]
|
||||
then
|
||||
LOGGER "[EDIT_VARIABLE] 环境变量 [$2 > $3] 修改成功!"
|
||||
|
||||
local File="$1"
|
||||
shift
|
||||
|
||||
[[ ! -s "$File" ]] && ECHO r "未检测到环境变量文件: [$File] !" && return 1
|
||||
|
||||
local Key="$1"
|
||||
local Val="$2"
|
||||
local RETURN=1
|
||||
|
||||
# key 基本校验(保守一些,避免 sed 正则炸)
|
||||
[[ -z "$Key" ]] && ECHO r "环境变量 Key 不能为空!" && return 1
|
||||
|
||||
# sed 正则/替换转义
|
||||
_escape_sed_regex() { printf '%s' "$1" | sed 's/[.[\*^$(){}+?|\\/]/\\&/g'; }
|
||||
_escape_sed_repl() { printf '%s' "$1" | sed 's/[\/&|\\]/\\&/g'; }
|
||||
|
||||
local Key_Re="$(_escape_sed_regex "$Key")"
|
||||
local Val_Repl="$(_escape_sed_repl "${Val:-}")"
|
||||
|
||||
case "$Mode" in
|
||||
edit)
|
||||
[[ -z "$Val" && "$Val" != "0" ]] && { ECHO r "环境变量 [$Key] 的值不能为空!"; return 1; }
|
||||
|
||||
if ! grep -q -E "^${Key_Re}=" "$File" 2>/dev/null; then
|
||||
LOGGER "[EDIT_VARIABLE] 新增环境变量 [$Key = $Val]"
|
||||
# 不用 echo -e,避免反斜杠被解释
|
||||
printf '\n%s=%s\n' "$Key" "$Val" >> "$File"
|
||||
RETURN=$?
|
||||
else
|
||||
# 精确替换整行:KEY=...
|
||||
sed -i -E "s|^${Key_Re}=.*|${Key}=${Val_Repl}|" "$File" 2>/dev/null
|
||||
if [[ $? == 0 ]]; then
|
||||
LOGGER "[EDIT_VARIABLE] 环境变量 [$Key > $Val] 修改成功!"
|
||||
RETURN=0
|
||||
else
|
||||
LOGGER "[EDIT_VARIABLE] 环境变量 [$Key > $Val] 修改失败!"
|
||||
RETURN=1
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
rm)
|
||||
# 只删除以 KEY= 开头的行
|
||||
sed -i -E "/^${Key_Re}=/d" "$File" 2>/dev/null
|
||||
if [[ $? == 0 ]]; then
|
||||
LOGGER "[EDIT_VARIABLE] 从 $File 删除环境变量 [$Key] ... 成功"
|
||||
RETURN=0
|
||||
else
|
||||
LOGGER "[EDIT_VARIABLE] 环境变量 [$2 > $3] 修改失败!"
|
||||
LOGGER "[EDIT_VARIABLE] 从 $File 删除环境变量 [$Key] ... 失败"
|
||||
RETURN=1
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
rm)
|
||||
sed -i "/$2=/d" $1
|
||||
if [[ $? == 0 ]]
|
||||
then
|
||||
LOGGER "[EDIT_VARIABLE] 从 $1 删除环境变量 [$2] ... 成功"
|
||||
RETURN=0
|
||||
else
|
||||
LOGGER "[EDIT_VARIABLE] 从 $1 删除环境变量 [$2] ... 失败"
|
||||
RETURN=1
|
||||
fi
|
||||
;;
|
||||
;;
|
||||
*)
|
||||
ECHO r "EDIT_VARIABLE 模式错误: [$Mode] (仅支持 edit/rm)"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
cp -a ${Config_Custom} ${Tmp_Path}/custom
|
||||
grep -vE "^[[:blank:]]*$" ${Tmp_Path}/custom > ${Config_Custom}
|
||||
echo >> ${Config_Custom}
|
||||
return ${RETURN}
|
||||
|
||||
# 保留你原来的功能:把 Config_Custom 去空行并保证末尾有空行
|
||||
# (即使本次修改的不是 Config_Custom,也按原行为执行)
|
||||
[[ -d "$Tmp_Path" ]] || mkdir -p "$Tmp_Path" 2>/dev/null
|
||||
if [[ -f "$Config_Custom" ]]; then
|
||||
cp -a "$Config_Custom" "$Tmp_Path/custom" 2>/dev/null
|
||||
grep -vE "^[[:blank:]]*$" "$Tmp_Path/custom" > "$Config_Custom"
|
||||
echo >> "$Config_Custom"
|
||||
fi
|
||||
|
||||
return $RETURN
|
||||
}
|
||||
|
||||
function LOAD_CONFIG() {
|
||||
@@ -522,7 +613,7 @@ function ANALYZE_API() {
|
||||
exit 1
|
||||
}
|
||||
local API_Cache=${Tmp_Path}/API_Cache
|
||||
if [[ $(CHECK_TIME ${API_File} 1) == false ]]
|
||||
if [[ $(CHECK_TIME ${API_Cache} 1) == false ]]
|
||||
then
|
||||
DOWNLOADER --path ${Tmp_Path} --file-name API_Cache --dl ${DL_DEPENDS[@]} --url "${Github_API}@@1 $(Proxy_X ${Github_Release}/API G@@1 F@@1 E@@1)" --no-url-name --timeout 5
|
||||
[[ ! $? == 0 || -z $(cat ${API_Cache} 2> /dev/null) ]] && {
|
||||
@@ -1053,9 +1144,9 @@ function DOWNLOADER() {
|
||||
}
|
||||
|
||||
function REMOVE_CACHE() {
|
||||
rm -r ${Tmp_Path}/API \
|
||||
${Tmp_Path}/Update_Logs.json \
|
||||
${Tmp_Path}/API_Cache 2> /dev/null
|
||||
RM ${Tmp_Path}/API
|
||||
RM ${Tmp_Path}/Update_Logs.json
|
||||
RM ${Tmp_Path}/API_Cache
|
||||
}
|
||||
|
||||
function LOG() {
|
||||
@@ -1076,7 +1167,7 @@ function LOG() {
|
||||
fi
|
||||
EDIT_VARIABLE rm ${Config_Custom} Log_Path
|
||||
EDIT_VARIABLE edit ${Config_Custom} Log_Path $2
|
||||
Log_Path=${Log_Path}
|
||||
Log_Path="$2"
|
||||
uci set autoupdate.@autoupdate[0].logpath=$2 2> /dev/null
|
||||
uci commit autoupdate
|
||||
ECHO y "AutoUpdate 日志保存路径已修改为: [$2]!"
|
||||
@@ -1248,7 +1339,7 @@ function AutoUpdate_Main() {
|
||||
exit $?
|
||||
;;
|
||||
--backup)
|
||||
local Backup_File="backup-$(uname -n)-$(date +%Y-%m-%d)-$(RANDOM 5).tar.gz"
|
||||
local Backup_File="backup-$(uname -n)-$(date +%Y-%m-%d)-$(RANDOM_HEX 5).tar.gz"
|
||||
shift
|
||||
[[ $# -gt 1 ]] && SHELL_HELP
|
||||
if [[ ! $1 ]]
|
||||
@@ -1373,7 +1464,7 @@ function AutoUpdate_Main() {
|
||||
exit $?
|
||||
;;
|
||||
--reset)
|
||||
rm -r ${Config_Default} ${Config_Custom}
|
||||
RM ${Config_Default} ${Config_Custom}
|
||||
cp -a /rom/$(dirname ${Config_Default})/* $(dirname ${Config_Default})/
|
||||
cp -a /rom/etc/config/autoupdate /etc/config
|
||||
REMOVE_CACHE
|
||||
@@ -1388,10 +1479,18 @@ function AutoUpdate_Main() {
|
||||
}
|
||||
|
||||
function KILL_PROCESS() {
|
||||
local i ; for i in $(ps -ef 2> /dev/null | grep -v grep | grep $1 | grep -v $$ | awk '{print $1}' 2> /dev/null)
|
||||
do
|
||||
kill -9 ${i} 2> /dev/null &
|
||||
LOGGER "结束进程: ${i}"
|
||||
local pattern="$1"
|
||||
local pids=""
|
||||
|
||||
if command -v pgrep >/dev/null 2>&1; then
|
||||
pids="$(pgrep -f "$pattern" 2>/dev/null | grep -v "^$$\$" || true)"
|
||||
else
|
||||
pids="$(ps -ef 2>/dev/null \
|
||||
| awk -v pat="$pattern" -v self="$$" '$0 ~ pat && $2 != self && $0 !~ /grep/ {print $2}')"
|
||||
fi
|
||||
|
||||
for pid in $pids; do
|
||||
kill -9 "$pid" 2>/dev/null && LOGGER "结束进程 PID: $pid (pattern=$pattern)"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Copyright (C) 2020-2023 Hyy2001X <https://github.com/Hyy2001X>
|
||||
# Copyright (C) 2020-2026 Hyy2001X <https://github.com/Hyy2001X>
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI support for iPerf3 server
|
||||
LUCI_DEPENDS:=+iperf3
|
||||
LUCI_PKGARCH:=all
|
||||
PKG_VERSION:=2.0
|
||||
PKG_RELEASE:=4
|
||||
PKG_VERSION:=3.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
|
||||
@@ -1,17 +1,168 @@
|
||||
module("luci.controller.iperf3-server",package.seeall)
|
||||
module("luci.controller.iperf3-server", package.seeall)
|
||||
|
||||
function index()
|
||||
if not nixio.fs.access("/etc/config/iperf3-server") then
|
||||
return
|
||||
end
|
||||
|
||||
entry({"admin", "services", "iperf3-server"}, cbi("iperf3-server"), _("iPerf3 Server"),99)
|
||||
entry({"admin", "services", "iperf3-server", "status"}, call("act_status")).leaf = true
|
||||
entry({"admin", "services", "iperf3-server"}, cbi("iperf3-server"), _("iPerf3 Server"), 99)
|
||||
|
||||
entry({"admin", "services", "iperf3-server", "status"}, call("act_status")).leaf = true
|
||||
entry({"admin", "services", "iperf3-server", "start"}, call("act_start")).leaf = true
|
||||
entry({"admin", "services", "iperf3-server", "stop"}, call("act_stop")).leaf = true
|
||||
entry({"admin", "services", "iperf3-server", "restart"}, call("act_restart")).leaf = true
|
||||
end
|
||||
|
||||
function act_status()
|
||||
local e = {}
|
||||
e.running = luci.sys.call("pgrep iperf3 > /dev/null") == 0
|
||||
local function json_ok(extra)
|
||||
local e = extra or {}
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function act_status()
|
||||
local uci = require("luci.model.uci").cursor()
|
||||
local sys = require("luci.sys")
|
||||
|
||||
local result = {
|
||||
running = false, -- 是否任意一个 server 正常运行
|
||||
servers = {} -- 每个端口的详细状态
|
||||
}
|
||||
|
||||
-- 解析 /proc/net/tcp & /proc/net/tcp6,判断端口是否 LISTEN
|
||||
local function is_listening_in_proc(path, port)
|
||||
local f = io.open(path, "r")
|
||||
if not f then return false end
|
||||
|
||||
local hex = string.format("%04X", port) -- 端口 16 进制,大写,固定 4 位
|
||||
-- /proc/net/tcp 列格式: sl local_address rem_address st ...
|
||||
-- local_address: AAAAAAAA:PPPP
|
||||
-- st = 0A 表示 LISTEN
|
||||
for line in f:lines() do
|
||||
-- 跳过表头
|
||||
if not line:match("^%s*sl%s+") then
|
||||
local st = line:match("^%s*%d+:%s+%x+:" .. hex .. "%s+%x+:%x+%s+(%x+)")
|
||||
if st == "0A" then
|
||||
f:close()
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
f:close()
|
||||
return false
|
||||
end
|
||||
|
||||
local function is_listening(port)
|
||||
-- IPv4 LISTEN
|
||||
if is_listening_in_proc("/proc/net/tcp", port) then
|
||||
return true
|
||||
end
|
||||
-- IPv6 LISTEN(有的系统会只在 tcp6 里出现)
|
||||
if is_listening_in_proc("/proc/net/tcp6", port) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- 用 ps 判断:是否存在 iperf3 server / delay 阶段 sleep+iperf3
|
||||
local ps = sys.exec("ps w 2>/dev/null") or ""
|
||||
|
||||
local function has_iperf3_server_proc(port)
|
||||
local p = tostring(port)
|
||||
|
||||
for line in ps:gmatch("[^\r\n]+") do
|
||||
-- 只要这行同时包含:
|
||||
-- 1) iperf3
|
||||
-- 2) -s(server)
|
||||
-- 3) -p 端口(支持 "-p 5201" 或 "-p5201")
|
||||
if line:find("iperf3", 1, true) then
|
||||
local hasS = line:match("%-s") ~= nil
|
||||
local hasP = (line:match("%-p%s*" .. p .. "%f[^%d]") ~= nil) or (line:match("%-p" .. p .. "%f[^%d]") ~= nil)
|
||||
if hasS and hasP then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local function has_delay_pending_proc(port)
|
||||
local p = tostring(port)
|
||||
for line in ps:gmatch("[^\r\n]+") do
|
||||
-- 匹配 sh -c "sleep N; exec iperf3 -s -p <port> ..."
|
||||
if (line:find("/bin/sh", 1, true) or line:find("sh -c", 1, true))
|
||||
and line:find("sleep", 1, true)
|
||||
and line:find("iperf3", 1, true)
|
||||
and (line:match("%-p%s*" .. p .. "%f[^%d]") or line:match("%-p" .. p .. "%f[^%d]"))
|
||||
then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
uci:foreach("iperf3-server", "servers", function(s)
|
||||
local port = tonumber(s.port)
|
||||
local enabled = (s.enable_server == "1")
|
||||
|
||||
if not port then
|
||||
return
|
||||
end
|
||||
|
||||
local listen = is_listening(port)
|
||||
local iperf3_proc = has_iperf3_server_proc(port)
|
||||
local delay_pending = has_delay_pending_proc(port)
|
||||
|
||||
local state, detail
|
||||
if not enabled then
|
||||
state = "disabled"
|
||||
detail = "disabled in config"
|
||||
else
|
||||
if delay_pending and not listen then
|
||||
state = "delay"
|
||||
detail = "delay pending (sleep)"
|
||||
elseif listen and iperf3_proc then
|
||||
state = "running"
|
||||
detail = "listening and iperf3 process found"
|
||||
result.running = true
|
||||
elseif listen and not iperf3_proc then
|
||||
state = "conflict"
|
||||
detail = "port is listening but not iperf3 (occupied)"
|
||||
else
|
||||
state = "stopped"
|
||||
detail = "not listening"
|
||||
end
|
||||
end
|
||||
|
||||
result.servers[#result.servers + 1] = {
|
||||
port = port,
|
||||
enable = enabled,
|
||||
listen = listen,
|
||||
state = state,
|
||||
detail = detail
|
||||
}
|
||||
end)
|
||||
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(result)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function act_start()
|
||||
local rc = luci.sys.call("/etc/init.d/iperf3-server start >/dev/null 2>&1")
|
||||
json_ok({ ok = (rc == 0) })
|
||||
end
|
||||
|
||||
function act_stop()
|
||||
local rc = luci.sys.call("/etc/init.d/iperf3-server stop >/dev/null 2>&1")
|
||||
json_ok({ ok = (rc == 0) })
|
||||
end
|
||||
|
||||
function act_restart()
|
||||
local rc = luci.sys.call("/etc/init.d/iperf3-server restart >/dev/null 2>&1")
|
||||
json_ok({ ok = (rc == 0) })
|
||||
end
|
||||
|
||||
@@ -1,36 +1,95 @@
|
||||
m = Map("iperf3-server", translate("iPerf3 Server"), translate("iPerf3 - The ultimate speed test tool for TCP, UDP and SCTP"))
|
||||
m = Map("iperf3-server",
|
||||
translate("iPerf3 Server"),
|
||||
translate("iPerf3 - The ultimate speed test tool for TCP, UDP and SCTP")
|
||||
)
|
||||
|
||||
m:section(SimpleSection).template = "iperf3-server/iperf3-server_status"
|
||||
|
||||
s = m:section(TypedSection, "iperf3-server", "")
|
||||
s.addremove = false
|
||||
s.anonymous = true
|
||||
-- 主配置段
|
||||
local g = m:section(TypedSection, "iperf3-server", "")
|
||||
g.addremove = false
|
||||
g.anonymous = true
|
||||
|
||||
main_enable = s:option(Flag, "main_enable", translate("Enable"), translate("Enable iPerf3 Servers"))
|
||||
local main_enable = g:option(Flag, "main_enable",
|
||||
translate("Enable"),
|
||||
translate("Enable iPerf3 Servers")
|
||||
)
|
||||
main_enable.default = "0"
|
||||
main_enable.rmempty = false
|
||||
|
||||
s = m:section(TypedSection, "servers", translate("Server Settings"), translate("Set up Multi-iPerf3 Servers"))
|
||||
-- servers 表格
|
||||
local s = m:section(TypedSection, "servers",
|
||||
translate("Server Settings"),
|
||||
translate("Set up Multi-iPerf3 Servers")
|
||||
)
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.template = "cbi/tblsection"
|
||||
s.template = "cbi/tblsection"
|
||||
|
||||
enable_server = s:option(Flag, "enable_server", translate("Enable"))
|
||||
local enable_server = s:option(Flag, "enable_server", translate("Enable"))
|
||||
enable_server.default = "1"
|
||||
enable_server.rmempty = false
|
||||
|
||||
port = s:option(Value, "port", translate("Port"))
|
||||
local port = s:option(Value, "port", translate("Port"))
|
||||
port.datatype = "port"
|
||||
port.default = "5201"
|
||||
port.rmempty = false
|
||||
port.default = "5201"
|
||||
port.rmempty = true -- 关键:允许新增行先空着,避免“点添加无反应”
|
||||
|
||||
delay = s:option(Value, "delay", translate("Start delay (Seconds)"))
|
||||
delay.default = "0"
|
||||
function port.validate(self, value, section)
|
||||
-- 新增行刚创建时可能是空值:先放行,让它能显示出来
|
||||
if value == nil or value == "" then
|
||||
return value
|
||||
end
|
||||
|
||||
local v = tonumber(value)
|
||||
if not v or v < 1 or v > 65535 then
|
||||
return nil, translate("Invalid port.")
|
||||
end
|
||||
|
||||
-- 端口去重:仅在有值时检查
|
||||
local dup = false
|
||||
m.uci:foreach("iperf3-server", "servers", function(s2)
|
||||
if s2[".name"] ~= section and s2.port and tonumber(s2.port) == v then
|
||||
dup = true
|
||||
end
|
||||
end)
|
||||
|
||||
if dup then
|
||||
return nil, translate("Port must be unique.")
|
||||
end
|
||||
|
||||
return tostring(v)
|
||||
end
|
||||
|
||||
local delay = s:option(Value, "delay", translate("Start delay (Seconds)"))
|
||||
delay.default = "0"
|
||||
delay.datatype = "uinteger"
|
||||
delay.rmempty = false
|
||||
delay.rmempty = true -- 同理:新增行先允许空
|
||||
|
||||
extra_options = s:option(Value, "extra_options", translate("Extra Options"))
|
||||
extra_options.rmempty = true
|
||||
extra_options.password= false
|
||||
function delay.validate(self, value, section)
|
||||
if value == nil or value == "" then
|
||||
return value
|
||||
end
|
||||
local v = tonumber(value)
|
||||
if v == nil or v < 0 or v > 3600 then
|
||||
return nil, translate("Delay must be between 0 and 3600 seconds.")
|
||||
end
|
||||
return tostring(v)
|
||||
end
|
||||
|
||||
local extra_options = s:option(Value, "extra_options", translate("Extra Options"))
|
||||
extra_options.rmempty = true
|
||||
extra_options.password = false
|
||||
|
||||
function extra_options.validate(self, value, section)
|
||||
if not value or value == "" then
|
||||
return value
|
||||
end
|
||||
-- 简单拦截 shell 元字符(安全兜底)
|
||||
if value:match("[;&|`$()<>\"']") then
|
||||
return nil, translate("Invalid characters in Extra Options.")
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
return m
|
||||
|
||||
@@ -1,22 +1,165 @@
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
XHR.poll(3, '<%=url([[admin]], [[services]], [[iperf3-server]], [[status]])%>', null,
|
||||
function(x, data) {
|
||||
var tb = document.getElementById('iperf3-server_status');
|
||||
if (data && tb) {
|
||||
if (data.running) {
|
||||
var links = '<em><b><font color=green>iPerf3 Server <%:RUNNING%></font></b></em>';
|
||||
tb.innerHTML = links;
|
||||
} else {
|
||||
tb.innerHTML = '<em><b><font color=red>iPerf3 Server <%:NOT RUNNING%></font></b></em>';
|
||||
<fieldset class="cbi-section">
|
||||
<legend><%:Status%></legend>
|
||||
|
||||
<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
|
||||
<div id="iperf3_status">-</div>
|
||||
|
||||
<button class="btn cbi-button cbi-button-apply" id="btn_start" type="button"><%:Start%></button>
|
||||
<button class="btn cbi-button cbi-button-reset" id="btn_stop" type="button"><%:Stop%></button>
|
||||
<button class="btn cbi-button cbi-button-action" id="btn_restart" type="button"><%:Restart%></button>
|
||||
|
||||
<span id="iperf3_busy" style="display:none; color:#999;">...</span>
|
||||
</div>
|
||||
|
||||
<div id="iperf3_list" style="margin-top:8px;"></div>
|
||||
</fieldset>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
(function () {
|
||||
var token = '<%=token%>';
|
||||
|
||||
function esc(s) {
|
||||
if (s === null || s === undefined) return '';
|
||||
return String(s).replace(/[&<>"']/g, function (c) {
|
||||
return ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]);
|
||||
});
|
||||
}
|
||||
|
||||
function setBusy(b) {
|
||||
document.getElementById('iperf3_busy').style.display = b ? '' : 'none';
|
||||
document.getElementById('btn_start').disabled = b;
|
||||
document.getElementById('btn_stop').disabled = b;
|
||||
document.getElementById('btn_restart').disabled = b;
|
||||
}
|
||||
|
||||
function badge(text, color) {
|
||||
return '<span style="color:' + color + '">' + text + '</span>';
|
||||
}
|
||||
|
||||
function stateBadge(it) {
|
||||
// 兼容旧字段:it.running/it.pid
|
||||
// 新字段优先:it.state
|
||||
var st = it.state;
|
||||
|
||||
if (!st) {
|
||||
if (it.enable === false) st = "disabled";
|
||||
else st = it.running ? "running" : "stopped";
|
||||
}
|
||||
|
||||
if (st === "running") return badge('<%:Running%>', 'green');
|
||||
if (st === "delay") return badge('<%:Delay%>', '#d67f00'); // 橙色
|
||||
if (st === "conflict") return badge('<%:Conflict%>', 'red');
|
||||
if (st === "disabled") return badge('<%:Disabled%>', '#999');
|
||||
return badge('<%:Stopped%>', '#999');
|
||||
}
|
||||
|
||||
function overallBadge(data) {
|
||||
// controller 返回 data.running=true 表示至少一个 running
|
||||
// 如果没有 running,但存在 delay/conflict,也给更准确的总体状态
|
||||
if (data && data.running) {
|
||||
return badge('<%:Running%>', 'green');
|
||||
}
|
||||
|
||||
var hasDelay = false, hasConflict = false;
|
||||
if (data && data.servers && data.servers.length) {
|
||||
for (var i = 0; i < data.servers.length; i++) {
|
||||
var st = data.servers[i].state;
|
||||
if (st === "delay") hasDelay = true;
|
||||
else if (st === "conflict") hasConflict = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConflict) return badge('<%:Conflict%>', 'red');
|
||||
if (hasDelay) return badge('<%:Delay%>', '#d67f00');
|
||||
return badge('<%:Not running%>', 'red');
|
||||
}
|
||||
);
|
||||
|
||||
function renderStatus(data) {
|
||||
var s = document.getElementById('iperf3_status');
|
||||
s.innerHTML = overallBadge(data);
|
||||
|
||||
var list = document.getElementById('iperf3_list');
|
||||
var html = '';
|
||||
|
||||
if (data && data.servers && data.servers.length) {
|
||||
html += '<ul>';
|
||||
|
||||
for (var i = 0; i < data.servers.length; i++) {
|
||||
var it = data.servers[i];
|
||||
|
||||
var port = esc(it.port);
|
||||
var en = (it.enable !== false) && (it.enable !== "0"); // 兼容 true/false/字符串
|
||||
var detail = it.detail ? (' <span style="color:#999">(' + esc(it.detail) + ')</span>') : '';
|
||||
|
||||
// 兼容旧字段 pid
|
||||
var pidInfo = '';
|
||||
if (it.pid) {
|
||||
pidInfo = ' <span style="color:#999">PID ' + esc(it.pid) + '</span>';
|
||||
}
|
||||
|
||||
// 监听信息(新字段 listen)
|
||||
var listenInfo = '';
|
||||
if (typeof it.listen === "boolean") {
|
||||
listenInfo = it.listen ? ' <span style="color:#999">[LISTEN]</span>' : ' <span style="color:#999">[NO LISTEN]</span>';
|
||||
}
|
||||
|
||||
html += '<li>'
|
||||
+ 'Port ' + port + ' : '
|
||||
+ stateBadge(it)
|
||||
+ pidInfo
|
||||
+ listenInfo
|
||||
+ detail
|
||||
+ (en ? '' : ' <span style="color:#999">(disabled)</span>')
|
||||
+ '</li>';
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
|
||||
// 状态说明(简短)
|
||||
html += '<div style="margin-top:6px; color:#999;">'
|
||||
+ '<span><%:Delay%></span>: <%:starting after configured delay%> '
|
||||
+ '<span><%:Conflict%></span>: <%:port is occupied%>'
|
||||
+ '</div>';
|
||||
}
|
||||
|
||||
list.innerHTML = html;
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin/services/iperf3-server/status")%>', null,
|
||||
function (x, data) { renderStatus(data || {}); }
|
||||
);
|
||||
}
|
||||
|
||||
function doAction(url, confirmText) {
|
||||
if (confirmText && !window.confirm(confirmText)) return;
|
||||
|
||||
setBusy(true);
|
||||
XHR.post(url, { token: token }, function (x, data) {
|
||||
setBusy(false);
|
||||
// restart 有 delay 的情况下,立即 refresh 可能还是 delay/stopped;轮询会继续更新
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('btn_start').addEventListener('click', function () {
|
||||
doAction('<%=luci.dispatcher.build_url("admin/services/iperf3-server/start")%>', '<%:Start iPerf3 servers?%>');
|
||||
});
|
||||
document.getElementById('btn_stop').addEventListener('click', function () {
|
||||
doAction('<%=luci.dispatcher.build_url("admin/services/iperf3-server/stop")%>', '<%:Stop iPerf3 servers?%>');
|
||||
});
|
||||
document.getElementById('btn_restart').addEventListener('click', function () {
|
||||
doAction('<%=luci.dispatcher.build_url("admin/services/iperf3-server/restart")%>', '<%:Restart iPerf3 servers?%>');
|
||||
});
|
||||
|
||||
// 轮询刷新
|
||||
XHR.poll(2, '<%=luci.dispatcher.build_url("admin/services/iperf3-server/status")%>', null,
|
||||
function (x, data) { renderStatus(data || {}); }
|
||||
);
|
||||
|
||||
// 首次刷新
|
||||
refresh();
|
||||
})();
|
||||
//]]>
|
||||
</script>
|
||||
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
|
||||
<fieldset class="cbi-section">
|
||||
<p id="iperf3-server_status">
|
||||
<em><%:Collecting data...%></em>
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
@@ -24,3 +24,75 @@ msgstr "iPerf3 服务端监听端口"
|
||||
|
||||
msgid "Extra Options"
|
||||
msgstr "额外参数"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "状态"
|
||||
|
||||
msgid "Running"
|
||||
msgstr "运行中"
|
||||
|
||||
msgid "Not running"
|
||||
msgstr "未运行"
|
||||
|
||||
msgid "Stopped"
|
||||
msgstr "已停止"
|
||||
|
||||
msgid "Start"
|
||||
msgstr "启动"
|
||||
|
||||
msgid "Stop"
|
||||
msgstr "停止"
|
||||
|
||||
msgid "Restart"
|
||||
msgstr "重启"
|
||||
|
||||
msgid "Delay"
|
||||
msgstr "延迟中"
|
||||
|
||||
msgid "Conflict"
|
||||
msgstr "端口冲突"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "已禁用"
|
||||
|
||||
msgid "starting after configured delay"
|
||||
msgstr "将在设定的延迟时间后启动"
|
||||
|
||||
msgid "port is occupied"
|
||||
msgstr "端口已被其他服务占用"
|
||||
|
||||
msgid "port is listening but not iperf3 (occupied)"
|
||||
msgstr "端口正在监听,但不是 iPerf3(被占用)"
|
||||
|
||||
msgid "Delay pending (sleep)"
|
||||
msgstr "延迟启动中"
|
||||
|
||||
msgid "listening and iperf3 process found"
|
||||
msgstr "正在监听,且 iPerf3 进程已启动"
|
||||
|
||||
msgid "not listening"
|
||||
msgstr "未监听端口"
|
||||
|
||||
msgid "disabled in config"
|
||||
msgstr "已在配置中禁用"
|
||||
|
||||
msgid "PID"
|
||||
msgstr "进程号"
|
||||
|
||||
msgid "LISTEN"
|
||||
msgstr "监听中"
|
||||
|
||||
msgid "NO LISTEN"
|
||||
msgstr "未监听"
|
||||
|
||||
msgid "port is listening"
|
||||
msgstr "端口正在监听"
|
||||
|
||||
msgid "Start iPerf3 servers?"
|
||||
msgstr "确认启动 iPerf3 服务?"
|
||||
|
||||
msgid "Stop iPerf3 servers?"
|
||||
msgstr "确认停止 iPerf3 服务?"
|
||||
|
||||
msgid "Restart iPerf3 servers?"
|
||||
msgstr "确认重启 iPerf3 服务?"
|
||||
|
||||
@@ -24,3 +24,75 @@ msgstr "iPerf3 服务端监听端口"
|
||||
|
||||
msgid "Extra Options"
|
||||
msgstr "额外参数"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "状态"
|
||||
|
||||
msgid "Running"
|
||||
msgstr "运行中"
|
||||
|
||||
msgid "Not running"
|
||||
msgstr "未运行"
|
||||
|
||||
msgid "Stopped"
|
||||
msgstr "已停止"
|
||||
|
||||
msgid "Start"
|
||||
msgstr "启动"
|
||||
|
||||
msgid "Stop"
|
||||
msgstr "停止"
|
||||
|
||||
msgid "Restart"
|
||||
msgstr "重启"
|
||||
|
||||
msgid "Delay"
|
||||
msgstr "延迟中"
|
||||
|
||||
msgid "Conflict"
|
||||
msgstr "端口冲突"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "已禁用"
|
||||
|
||||
msgid "starting after configured delay"
|
||||
msgstr "将在设定的延迟时间后启动"
|
||||
|
||||
msgid "port is occupied"
|
||||
msgstr "端口已被其他服务占用"
|
||||
|
||||
msgid "port is listening but not iperf3 (occupied)"
|
||||
msgstr "端口正在监听,但不是 iPerf3(被占用)"
|
||||
|
||||
msgid "Delay pending (sleep)"
|
||||
msgstr "延迟启动中"
|
||||
|
||||
msgid "listening and iperf3 process found"
|
||||
msgstr "正在监听,且 iPerf3 进程已启动"
|
||||
|
||||
msgid "not listening"
|
||||
msgstr "未监听端口"
|
||||
|
||||
msgid "disabled in config"
|
||||
msgstr "已在配置中禁用"
|
||||
|
||||
msgid "PID"
|
||||
msgstr "进程号"
|
||||
|
||||
msgid "LISTEN"
|
||||
msgstr "监听中"
|
||||
|
||||
msgid "NO LISTEN"
|
||||
msgstr "未监听"
|
||||
|
||||
msgid "port is listening"
|
||||
msgstr "端口正在监听"
|
||||
|
||||
msgid "Start iPerf3 servers?"
|
||||
msgstr "确认启动 iPerf3 服务?"
|
||||
|
||||
msgid "Stop iPerf3 servers?"
|
||||
msgstr "确认停止 iPerf3 服务?"
|
||||
|
||||
msgid "Restart iPerf3 servers?"
|
||||
msgstr "确认重启 iPerf3 服务?"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
config iperf3-server
|
||||
option enable '0'
|
||||
option port '5201'
|
||||
config iperf3-server 'main'
|
||||
option main_enable '0'
|
||||
|
||||
@@ -1,55 +1,184 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# iPerf3 Server init (OpenWrt 23.05) - procd multi-instance (UCI-only fetch)
|
||||
# UCI: /etc/config/iperf3-server
|
||||
#
|
||||
# config iperf3-server
|
||||
# option main_enable '1'
|
||||
#
|
||||
# config servers
|
||||
# option enable_server '1'
|
||||
# option port '5201'
|
||||
# option delay '0'
|
||||
# option extra_options ''
|
||||
|
||||
USE_PROCD=1
|
||||
START=99
|
||||
LOGGER="logger -t [iPerf3-Server]"
|
||||
|
||||
start() {
|
||||
local basic_list="main_enable"
|
||||
local server_list="port delay extra_options enable_server"
|
||||
for i in $(echo $basic_list)
|
||||
do
|
||||
local eval $i="$(uci_get_by_type iperf3-server 0 $i)"
|
||||
done ; unset i
|
||||
if [ "$main_enable" == 1 ]
|
||||
then
|
||||
server_number=$(uci show iperf3-server 2> /dev/null | egrep '@servers\[[0-9]\]+=servers' | wc -l)
|
||||
# server_number=$(uci show iperf3-server 2> /dev/null | egrep -o '@servers\[[0-9]\]+=servers' | awk 'END {print}' | egrep -o "[0-9]")
|
||||
for u in $(seq 0 $((${server_number} - 1)))
|
||||
do
|
||||
for i in $server_list
|
||||
do
|
||||
eval ${i}=$(uci_get_by_type servers $u $i)
|
||||
done ; unset i
|
||||
if [ "$enable_server" == 1 ]
|
||||
then
|
||||
sleep $delay
|
||||
$LOGGER "Starting iPerf3 Server [$u] with Port [$port] ..."
|
||||
old_process="$(ps -efww | grep 'iperf3 -s -D -p $port' | grep -v 'grep' | awk '{print $1}')"
|
||||
[ "$old_process" ] && kill -9 "$old_process" 2> /dev/null
|
||||
$(command -v iperf3) -s -D -p $port $extra_options
|
||||
#procd_open_instance
|
||||
#procd_set_param command sleep $delay ; $(command -v iperf3) -s -D -p $port $extra_options
|
||||
#procd_set_param respawn 3000 3 10
|
||||
#procd_close_instance
|
||||
fi
|
||||
unset enable_server delay
|
||||
done ; unset u
|
||||
else
|
||||
$LOGGER "iPerf3 Server is disabled ..."
|
||||
stop_service
|
||||
fi
|
||||
PROG="/usr/bin/iperf3"
|
||||
CONFIG="iperf3-server"
|
||||
LOGGER="logger -t iPerf3-Server"
|
||||
|
||||
# 与 CBI 的 extra_options 校验保持一致:拦截明显 shell 元字符
|
||||
is_safe_extra_opts() {
|
||||
[ -z "$1" ] && return 0
|
||||
case "$1" in
|
||||
*";"*|*"&"*|*"|"*|*"\`"*|*"\$"*|*"<"*|*">"*|*"\""*|*"'"* )
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
stop() {
|
||||
# 停止某端口:既杀 iperf3 server,也杀 delay 阶段的 sh/sleep
|
||||
kill_by_port() {
|
||||
local port="$1"
|
||||
local pids
|
||||
|
||||
# 1) 已经起来的 iperf3 server
|
||||
pids="$(ps w | grep '[i]perf3' | grep ' -s' | grep " -p $port" | awk '{print $1}')"
|
||||
[ -n "$pids" ] && kill $pids 2>/dev/null
|
||||
|
||||
# 2) delay 阶段:/bin/sh -c "sleep N; exec iperf3 -s -p PORT ..."
|
||||
pids="$(ps w | grep '[s]leep' | grep '[i]perf3' | grep " -p $port" | awk '{print $1}')"
|
||||
[ -n "$pids" ] && kill $pids 2>/dev/null
|
||||
|
||||
sleep 1
|
||||
|
||||
# hard kill 兜底
|
||||
pids="$(ps w | grep '[i]perf3' | grep ' -s' | grep " -p $port" | awk '{print $1}')"
|
||||
[ -n "$pids" ] && kill -9 $pids 2>/dev/null
|
||||
|
||||
pids="$(ps w | grep '[s]leep' | grep '[i]perf3' | grep " -p $port" | awk '{print $1}')"
|
||||
[ -n "$pids" ] && kill -9 $pids 2>/dev/null
|
||||
}
|
||||
|
||||
# 获取 servers 索引列表(0 1 2 ...)
|
||||
get_server_indexes() {
|
||||
# 从 uci show 里提取 @servers[NUM]
|
||||
uci -q show "$CONFIG" 2>/dev/null \
|
||||
| sed -n "s/^$CONFIG\.@servers\[\([0-9]\+\)\]=servers$/\1/p"
|
||||
}
|
||||
|
||||
start_one_idx() {
|
||||
local idx="$1"
|
||||
|
||||
local enable_server port delay extra_options
|
||||
enable_server="$(uci -q get $CONFIG.@servers[$idx].enable_server)"
|
||||
port="$(uci -q get $CONFIG.@servers[$idx].port)"
|
||||
delay="$(uci -q get $CONFIG.@servers[$idx].delay)"
|
||||
extra_options="$(uci -q get $CONFIG.@servers[$idx].extra_options)"
|
||||
|
||||
# 默认值
|
||||
[ -n "$enable_server" ] || enable_server="1"
|
||||
[ -n "$port" ] || port="5201"
|
||||
[ -n "$delay" ] || delay="0"
|
||||
|
||||
[ "$enable_server" = "1" ] || return 0
|
||||
|
||||
# 端口合法性兜底
|
||||
case "$port" in
|
||||
*[!0-9]*|'') $LOGGER "Skip invalid port in @servers[$idx]"; return 0 ;;
|
||||
esac
|
||||
[ "$port" -ge 1 ] 2>/dev/null && [ "$port" -le 65535 ] 2>/dev/null || {
|
||||
$LOGGER "Skip invalid port [$port] in @servers[$idx]"
|
||||
return 0
|
||||
}
|
||||
|
||||
# delay 合法性兜底
|
||||
case "$delay" in
|
||||
*[!0-9]*|'') delay=0 ;;
|
||||
esac
|
||||
|
||||
# extra_options 安全兜底
|
||||
if [ -n "$extra_options" ] && ! is_safe_extra_opts "$extra_options"; then
|
||||
$LOGGER "Rejected unsafe extra_options on port [$port] (@servers[$idx])"
|
||||
extra_options=""
|
||||
fi
|
||||
|
||||
$LOGGER "Starting @servers[$idx] port=[$port] delay=[$delay] ..."
|
||||
|
||||
# procd 实例名按端口
|
||||
procd_open_instance "iperf3_${port}"
|
||||
|
||||
# 不用 -D;delay 用 sh -c 不阻塞
|
||||
if [ "$delay" -gt 0 ] 2>/dev/null; then
|
||||
if [ -n "$extra_options" ]; then
|
||||
procd_set_param command /bin/sh -c "sleep $delay; exec $PROG -s -p $port $extra_options"
|
||||
else
|
||||
procd_set_param command /bin/sh -c "sleep $delay; exec $PROG -s -p $port"
|
||||
fi
|
||||
else
|
||||
if [ -n "$extra_options" ]; then
|
||||
procd_set_param command "$PROG" -s -p "$port" $extra_options
|
||||
else
|
||||
procd_set_param command "$PROG" -s -p "$port"
|
||||
fi
|
||||
fi
|
||||
|
||||
procd_set_param respawn 3600 5 5
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
start_service() {
|
||||
[ -x "$PROG" ] || { $LOGGER "iperf3 not found: $PROG"; return 1; }
|
||||
|
||||
# 主开关:直接用 uci 读取匿名段
|
||||
local main_enable
|
||||
main_enable="$(uci -q get $CONFIG.@iperf3-server[0].main_enable)"
|
||||
[ -n "$main_enable" ] || main_enable="0"
|
||||
|
||||
[ "$main_enable" = "1" ] || {
|
||||
$LOGGER "iPerf3 Server is disabled ..."
|
||||
return 0
|
||||
}
|
||||
|
||||
# 遍历 servers 索引
|
||||
local idx
|
||||
for idx in $(get_server_indexes); do
|
||||
start_one_idx "$idx"
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
stop_one_idx() {
|
||||
local idx="$1"
|
||||
local enable_server port
|
||||
|
||||
enable_server="$(uci -q get $CONFIG.@servers[$idx].enable_server)"
|
||||
[ -n "$enable_server" ] || enable_server="1"
|
||||
[ "$enable_server" = "1" ] || return 0
|
||||
|
||||
port="$(uci -q get $CONFIG.@servers[$idx].port)"
|
||||
[ -n "$port" ] || port="5201"
|
||||
|
||||
case "$port" in
|
||||
*[!0-9]*|'') return 0 ;;
|
||||
esac
|
||||
|
||||
kill_by_port "$port"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
$LOGGER "Stopping iPerf3 Server ..."
|
||||
ps -efww | grep 'iperf3 -s -D' | grep -v 'grep' | awk '{print $1}' | xargs kill -9
|
||||
|
||||
# 按 servers 列表逐个端口清理(不误杀其它 iperf3)
|
||||
local idx
|
||||
for idx in $(get_server_indexes); do
|
||||
stop_one_idx "$idx"
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
# /etc/init.d/iperf3-server reload
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "iperf3-server"
|
||||
}
|
||||
|
||||
uci_get_by_type() {
|
||||
local ret=$(uci get iperf3-server.@$1[$2].$3 2>/dev/null)
|
||||
echo ${ret:=$4}
|
||||
procd_add_reload_trigger "$CONFIG"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@iperf3-server[-1]
|
||||
# 已存在就不重复写
|
||||
if uci -q show ucitrack | grep -q "=iperf3-server"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
uci -q batch <<'EOF' >/dev/null
|
||||
add ucitrack iperf3-server
|
||||
set ucitrack.@iperf3-server[-1].init=iperf3-server
|
||||
set ucitrack.@iperf3-server[-1].init='iperf3-server'
|
||||
commit ucitrack
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
exit 0
|
||||
|
||||
@@ -865,7 +865,7 @@ function gen_config(var)
|
||||
end
|
||||
table.insert(balancers, {
|
||||
tag = balancer_tag,
|
||||
selector = valid_nodes,
|
||||
selector = api.clone(valid_nodes),
|
||||
fallbackTag = fallback_node_tag,
|
||||
strategy = strategy
|
||||
})
|
||||
@@ -1198,6 +1198,21 @@ function gen_config(var)
|
||||
end
|
||||
end)
|
||||
|
||||
if default_outboundTag or default_balancerTag then
|
||||
local rule = {
|
||||
_flag = "default",
|
||||
type = "field",
|
||||
outboundTag = default_outboundTag,
|
||||
balancerTag = default_balancerTag
|
||||
}
|
||||
if node.domainStrategy == "IPIfNonMatch" then
|
||||
rule.ip = { "0.0.0.0/0", "::/0" }
|
||||
else
|
||||
rule.network = "tcp,udp"
|
||||
end
|
||||
table.insert(rules, rule)
|
||||
end
|
||||
|
||||
routing = {
|
||||
domainStrategy = node.domainStrategy or "AsIs",
|
||||
domainMatcher = node.domainMatcher or "hybrid",
|
||||
|
||||
@@ -19,7 +19,6 @@ PKG_BUILD_DIR:=$(BUILD_DIR)/qtbase-$(PKG_VERSION)
|
||||
|
||||
PKG_BUILD_DEPENDS:=qt6base/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_USE_MIPS16:=0
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
CMAKE_INSTALL:=1
|
||||
|
||||
@@ -127,6 +126,7 @@ CMAKE_OPTIONS+= \
|
||||
-DFEATURE_testlib=$(if $(CONFIG_PACKAGE_libQt6Test),ON,OFF) \
|
||||
-DFEATURE_itemmodeltester=OFF \
|
||||
-DFEATURE_widgets=$(if $(CONFIG_PACKAGE_libQt6Widgets),ON,OFF) \
|
||||
-DFEATURE_commandlinkbutton=OFF \
|
||||
-DFEATURE_xml=$(if $(CONFIG_PACKAGE_libQt6Xml),ON,OFF) \
|
||||
-DFEATURE_tuiotouch=$(if $(CONFIG_PACKAGE_qt6-plugin-libqtuiotouchplugin),ON,OFF)
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ PKG_BUILD_DIR:=$(BUILD_DIR)/libtorrent-$(PKG_VERSION)
|
||||
PKG_LICENSE:=BSD
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
PKG_USE_MIPS16:=0
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_INSTALL:=1
|
||||
@@ -47,9 +46,8 @@ CMAKE_OPTIONS += \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-Ddeprecated-functions=OFF \
|
||||
-Dlogging=OFF \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-Dpython-bindings=$(if $(CONFIG_PACKAGE_python3-libtorrent),ON,OFF) \
|
||||
-Dpython-egg-info=$(if $(CONFIG_PACKAGE_python3-libtorrent),ON,OFF)
|
||||
-Dpython-bindings=OFF \
|
||||
-Dpython-egg-info=OFF
|
||||
|
||||
define Build/InstallDev
|
||||
$(INSTALL_DIR) $(1)/usr/include
|
||||
|
||||
Reference in New Issue
Block a user