#!/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 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 } # 停止某端口:既杀 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 ..." # 按 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 "$CONFIG" }