diff --git a/alist/Makefile b/alist/Makefile index 3037b7bc2..8e63fa803 100644 --- a/alist/Makefile +++ b/alist/Makefile @@ -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 diff --git a/autoupdate/files/bin/autoupdate b/autoupdate/files/bin/autoupdate index 86c6fff2f..9d6c867b2 100755 --- a/autoupdate/files/bin/autoupdate +++ b/autoupdate/files/bin/autoupdate @@ -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 使用指定的下载器 * --decompress 解压 img.gz 固件后再更新固件 * --skip-verify 跳过固件 SHA256 校验 ${Red}(危险)${White} * --path 固件下载到用户提供的绝对路径 * 更新程序: - -x 更新 autoupdate 程序 - -x -path 更新 autoupdate 程序 (保存程序到用户提供的绝对路径 ) * - -x -url 更新 autoupdate 程序 (使用提供的地址 更新程序) * + -x 自动更新 autoupdate 程序 + -x -path 自动更新 autoupdate 程序 (保存程序到用户提供的绝对路径 ) * + -x -url 手动更新 autoupdate 程序 (使用用户提供的地址 更新 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:-}] 路径不存在,可用存储空间: [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 } diff --git a/luci-app-iperf3-server/Makefile b/luci-app-iperf3-server/Makefile index ad850e14d..15e75f4b7 100755 --- a/luci-app-iperf3-server/Makefile +++ b/luci-app-iperf3-server/Makefile @@ -1,12 +1,12 @@ -# Copyright (C) 2020-2023 Hyy2001X +# Copyright (C) 2020-2026 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 diff --git a/luci-app-iperf3-server/luasrc/controller/iperf3-server.lua b/luci-app-iperf3-server/luasrc/controller/iperf3-server.lua index 02cef90c4..afc7bc35e 100755 --- a/luci-app-iperf3-server/luasrc/controller/iperf3-server.lua +++ b/luci-app-iperf3-server/luasrc/controller/iperf3-server.lua @@ -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 ..." + 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 diff --git a/luci-app-iperf3-server/luasrc/model/cbi/iperf3-server.lua b/luci-app-iperf3-server/luasrc/model/cbi/iperf3-server.lua index e8417dc34..3d8d48d10 100755 --- a/luci-app-iperf3-server/luasrc/model/cbi/iperf3-server.lua +++ b/luci-app-iperf3-server/luasrc/model/cbi/iperf3-server.lua @@ -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 diff --git a/luci-app-iperf3-server/luasrc/view/iperf3-server/iperf3-server_status.htm b/luci-app-iperf3-server/luasrc/view/iperf3-server/iperf3-server_status.htm index c6a8378a2..5d02a1381 100755 --- a/luci-app-iperf3-server/luasrc/view/iperf3-server/iperf3-server_status.htm +++ b/luci-app-iperf3-server/luasrc/view/iperf3-server/iperf3-server_status.htm @@ -1,22 +1,165 @@ - - -
-

- <%:Collecting data...%> -

-
diff --git a/luci-app-iperf3-server/po/zh-cn/iperf3-server.po b/luci-app-iperf3-server/po/zh-cn/iperf3-server.po index bfbd1f951..752dab9f1 100755 --- a/luci-app-iperf3-server/po/zh-cn/iperf3-server.po +++ b/luci-app-iperf3-server/po/zh-cn/iperf3-server.po @@ -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 服务?" diff --git a/luci-app-iperf3-server/po/zh_Hans/iperf3-server.po b/luci-app-iperf3-server/po/zh_Hans/iperf3-server.po index bfbd1f951..752dab9f1 100755 --- a/luci-app-iperf3-server/po/zh_Hans/iperf3-server.po +++ b/luci-app-iperf3-server/po/zh_Hans/iperf3-server.po @@ -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 服务?" diff --git a/luci-app-iperf3-server/root/etc/config/iperf3-server b/luci-app-iperf3-server/root/etc/config/iperf3-server index 5a752470c..448c27c2b 100755 --- a/luci-app-iperf3-server/root/etc/config/iperf3-server +++ b/luci-app-iperf3-server/root/etc/config/iperf3-server @@ -1,4 +1,3 @@ -config iperf3-server - option enable '0' - option port '5201' \ No newline at end of file +config iperf3-server 'main' + option main_enable '0' diff --git a/luci-app-iperf3-server/root/etc/init.d/iperf3-server b/luci-app-iperf3-server/root/etc/init.d/iperf3-server index 81252f182..aba8c2894 100755 --- a/luci-app-iperf3-server/root/etc/init.d/iperf3-server +++ b/luci-app-iperf3-server/root/etc/init.d/iperf3-server @@ -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" } diff --git a/luci-app-iperf3-server/root/etc/uci-defaults/iperf3-server b/luci-app-iperf3-server/root/etc/uci-defaults/iperf3-server index f461250fa..f3af81f00 100755 --- a/luci-app-iperf3-server/root/etc/uci-defaults/iperf3-server +++ b/luci-app-iperf3-server/root/etc/uci-defaults/iperf3-server @@ -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 \ No newline at end of file +exit 0 diff --git a/luci-app-passwall/luasrc/passwall/util_xray.lua b/luci-app-passwall/luasrc/passwall/util_xray.lua index c03239f5c..294ba9dbb 100644 --- a/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -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", diff --git a/qt6base/Makefile b/qt6base/Makefile index fcbb3bd25..2b61dca39 100644 --- a/qt6base/Makefile +++ b/qt6base/Makefile @@ -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) diff --git a/rblibtorrent/Makefile b/rblibtorrent/Makefile index 8b1813cc8..7d697c623 100644 --- a/rblibtorrent/Makefile +++ b/rblibtorrent/Makefile @@ -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