mirror of
https://github.com/fscarmen/sing-box.git
synced 2026-09-02 09:20:42 +08:00
fix: unblock IPv6-only installs and Alpine BusyBox compatibility
- probe raw and github releases download domains in parallel in check_cdn, direct only when both reachable, verify proxies against both via wait -n - add --tries=2 --timeout=10 to background binary and template downloads so blackholed connections fail fast instead of blocking the bare wait forever - replace grep -oP in nginx_stop with grep -oE + cut so uninstall can stop nginx on Alpine - drop grep --exclude in favor of awk FILENAME filtering - replace pgrep with ps | grep in OpenRC template and clash_api reload guard fix: 修复 IPv6-only 安装卡死与 Alpine BusyBox 兼容 - check_cdn 并行双域探测 raw 与 github releases 下载域,双域均可达才直连,代理经 wait -n 校验双域可用 - 后台二进制与订阅模板下载统一加 --tries=2 --timeout=10,黑洞连接快速失败,避免裸 wait 永久阻塞 - nginx_stop 的 grep -oP 改为 grep -oE + cut,修复 Alpine 卸载时无法停止 nginx - grep --exclude 改用 awk FILENAME 排除 - OpenRC 模板与 clash_api 热更守卫的 pgrep 改用 ps | grep
This commit is contained in:
+41
-30
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 当前脚本版本号
|
||||
VERSION='v1.3.23 (2026.08.14)'
|
||||
VERSION='v1.3.23 (2026.08.15)'
|
||||
|
||||
# Github 反代加速代理
|
||||
GITHUB_PROXY=('https://hub.glowp.xyz/' 'https://proxy.vvvv.ee/')
|
||||
@@ -456,12 +456,15 @@ calc_install_steps() {
|
||||
TOTAL_STEPS=$STEP_TOTAL
|
||||
}
|
||||
|
||||
# 检测是否需要启用 Github CDN,如能直接连通 api.github.com,则不使用
|
||||
# 检测是否需要启用 Github CDN,raw 与 releases 两个下载域都直连可达才不使用
|
||||
check_cdn() {
|
||||
local PROXY CODE PID CMD
|
||||
local WAIT_COUNT=40
|
||||
local PROXY CODE PID CMD _url P1 P2 CODE_RAW CODE_REL
|
||||
local PIDS=()
|
||||
local API_URL='https://api.github.com/repos/SagerNet/sing-box/releases'
|
||||
# 探测点1:raw 域(脚本及订阅模板文件下载路径)
|
||||
local RAW_URL='https://raw.githubusercontent.com/fscarmen/sing-box/main/sing-box.sh'
|
||||
# 探测点2:github releases 域(sing-box / jq / cloudflared 等二进制真实下载路径,
|
||||
# latest 指向永远有效)。修复 IPv6-only / 部分封锁场景:raw 域可达 ≠ releases 域可达,任一域失败都必须走代理
|
||||
local REL_URL='https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64'
|
||||
|
||||
# 确定下载工具:优先 wget,次选 curl
|
||||
if command -v wget >/dev/null 2>&1; then
|
||||
@@ -483,27 +486,35 @@ check_cdn() {
|
||||
fi
|
||||
}
|
||||
|
||||
# 直连检测
|
||||
CODE=$(get_code "$API_URL")
|
||||
if [ "$CODE" = '200' ]; then
|
||||
# 直连检测:raw 与 releases 两个下载域并行 HEAD 探测,必须全部可达才可直连
|
||||
get_code "$RAW_URL" > "${TEMP_DIR}/cdn_raw" &
|
||||
P1=$!
|
||||
get_code "$REL_URL" > "${TEMP_DIR}/cdn_rel" &
|
||||
P2=$!
|
||||
wait "$P1" "$P2" 2>/dev/null
|
||||
CODE_RAW=$(cat "${TEMP_DIR}/cdn_raw" 2>/dev/null)
|
||||
CODE_REL=$(cat "${TEMP_DIR}/cdn_rel" 2>/dev/null)
|
||||
rm -f "${TEMP_DIR}/cdn_raw" "${TEMP_DIR}/cdn_rel"
|
||||
if [ "$CODE_RAW" = '200' ] && [ "$CODE_REL" = '200' ]; then
|
||||
GH_PROXY=''
|
||||
return
|
||||
fi
|
||||
|
||||
# 并发探测代理
|
||||
# 直连失败 → 并发探测代理镜像(对两个下载域都要求可达,与直连判定标准一致)
|
||||
for PROXY in "${GITHUB_PROXY[@]}"; do
|
||||
{
|
||||
CODE=$(get_code "${PROXY}${API_URL}")
|
||||
# 两个下载域都经代理可达才认定该镜像可用(Bash 异步子壳中不能用 return,用短路逻辑)
|
||||
for _url in "$RAW_URL" "$REL_URL"; do
|
||||
CODE=$(get_code "${PROXY}${_url}")
|
||||
[ "$CODE" = '200' ] || { CODE=; break; }
|
||||
done
|
||||
[ "$CODE" = '200' ] && [ ! -e "${TEMP_DIR}/cdn_proxy" ] && printf '%s' "$PROXY" > "${TEMP_DIR}/cdn_proxy"
|
||||
} &
|
||||
PIDS+=("$!")
|
||||
done
|
||||
|
||||
# 等第一个返回 200 的代理,超时则回退为直连,避免无限等待卡死
|
||||
while [ ! -e "${TEMP_DIR}/cdn_proxy" ] && [ "$WAIT_COUNT" -gt 0 ]; do
|
||||
sleep 0.05
|
||||
(( WAIT_COUNT-- )) || true
|
||||
done
|
||||
# wait -n 等首个返回 200 的代理(Bash 4.3+),老版本兜底 sleep 1,避免无限等待卡死
|
||||
[ "${#PIDS[@]}" -gt 0 ] && { wait -n 2>/dev/null || sleep 1; }
|
||||
|
||||
[ -e "${TEMP_DIR}/cdn_proxy" ] && GH_PROXY=$(cat "${TEMP_DIR}/cdn_proxy") || GH_PROXY=''
|
||||
|
||||
@@ -1021,7 +1032,7 @@ change_config() {
|
||||
fi
|
||||
check_arch
|
||||
[ ! -e "${WORK_DIR}/qrencode" ] && \
|
||||
wget --no-check-certificate --continue -qO ${WORK_DIR}/qrencode \
|
||||
wget --no-check-certificate --continue --tries=2 --timeout=10 -qO ${WORK_DIR}/qrencode \
|
||||
${GH_PROXY}https://github.com/fscarmen/client_template/raw/main/qrencode-go/qrencode-go-linux-$QRENCODE_ARCH 2>/dev/null \
|
||||
&& chmod +x ${WORK_DIR}/qrencode
|
||||
fetch_nodes_value
|
||||
@@ -2204,9 +2215,9 @@ check_install() {
|
||||
|
||||
# 并发下载订阅模板 (clash, clash2, sing-box-template),在新安装和更换协议时会用到
|
||||
{
|
||||
wget --no-check-certificate --continue -qO $TEMP_DIR/clash ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/clash 2>/dev/null &
|
||||
wget --no-check-certificate --continue -qO $TEMP_DIR/clash2 ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/clash2 2>/dev/null &
|
||||
wget --no-check-certificate --continue -qO $TEMP_DIR/sing-box-template ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/sing-box 2>/dev/null &
|
||||
wget --no-check-certificate --continue --tries=2 --timeout=10 -qO $TEMP_DIR/clash ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/clash 2>/dev/null &
|
||||
wget --no-check-certificate --continue --tries=2 --timeout=10 -qO $TEMP_DIR/clash2 ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/clash2 2>/dev/null &
|
||||
wget --no-check-certificate --continue --tries=2 --timeout=10 -qO $TEMP_DIR/sing-box-template ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/sing-box 2>/dev/null &
|
||||
wait
|
||||
} &
|
||||
|
||||
@@ -2217,7 +2228,7 @@ check_install() {
|
||||
local ONLINE=$(get_sing_box_version)
|
||||
local SB_DIR="$TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH"
|
||||
local SB_BIN="$SB_DIR/sing-box"
|
||||
wget --no-check-certificate --continue \
|
||||
wget --no-check-certificate --continue --tries=2 --timeout=10 \
|
||||
${GH_PROXY}https://github.com/SagerNet/sing-box/releases/download/v$ONLINE/sing-box-$ONLINE-linux-$SING_BOX_ARCH.tar.gz \
|
||||
-qO- | tar xz -C $TEMP_DIR 2>/dev/null
|
||||
[ -s "$SB_BIN" ] && [ -x "$SB_BIN" ] && mv "$SB_BIN" "$TEMP_DIR/sing-box" && chmod +x "$TEMP_DIR/sing-box"
|
||||
@@ -2225,14 +2236,14 @@ check_install() {
|
||||
|
||||
# 任务 2: 下载 jq
|
||||
{
|
||||
wget --no-check-certificate --continue -qO $TEMP_DIR/jq \
|
||||
wget --no-check-certificate --continue --tries=2 --timeout=10 -qO $TEMP_DIR/jq \
|
||||
${GH_PROXY}https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$JQ_ARCH 2>/dev/null \
|
||||
&& chmod +x $TEMP_DIR/jq
|
||||
} &
|
||||
|
||||
# 任务 3: 下载 qrencode
|
||||
{
|
||||
wget --no-check-certificate --continue -qO $TEMP_DIR/qrencode \
|
||||
wget --no-check-certificate --continue --tries=2 --timeout=10 -qO $TEMP_DIR/qrencode \
|
||||
${GH_PROXY}https://github.com/fscarmen/client_template/raw/main/qrencode-go/qrencode-go-linux-$QRENCODE_ARCH 2>/dev/null \
|
||||
&& chmod +x $TEMP_DIR/qrencode
|
||||
} &
|
||||
@@ -2286,7 +2297,7 @@ check_install() {
|
||||
# 如果有需要,后台静默下载 cloudflared
|
||||
if [[ "${STATUS[1]}" = "$(text 26)" || "$NONINTERACTIVE_INSTALL" = 'noninteractive_install' ]] && [ ! -s ${WORK_DIR}/cloudflared ]; then
|
||||
{
|
||||
wget --no-check-certificate -qO $TEMP_DIR/cloudflared ${GH_PROXY}https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-$ARGO_ARCH >/dev/null 2>&1 && chmod +x $TEMP_DIR/cloudflared >/dev/null 2>&1
|
||||
wget --no-check-certificate --tries=2 --timeout=10 -qO $TEMP_DIR/cloudflared ${GH_PROXY}https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-$ARGO_ARCH >/dev/null 2>&1 && chmod +x $TEMP_DIR/cloudflared >/dev/null 2>&1
|
||||
}&
|
||||
elif [ "${STATUS[1]}" != "$(text 26)" ]; then
|
||||
# 查 Argo 进程号,运行时长和内存占用
|
||||
@@ -2319,7 +2330,7 @@ nginx_run() {
|
||||
|
||||
nginx_stop() {
|
||||
local NGINX_PID=$(ps -eo pid,args | awk -v work_dir="$WORK_DIR" '$0~(work_dir"/nginx.conf"){print $1;exit}')
|
||||
ss -nltp | sed -n "/pid=$NGINX_PID,/ s/,/ /gp" | grep -oP 'pid=\K\S+' | sort -u | xargs kill -9 >/dev/null 2>&1
|
||||
ss -nltp | sed -n "/pid=$NGINX_PID,/ s/,/ /gp" | grep -oE 'pid=[0-9]+' | cut -d= -f2 | sort -u | xargs kill -9 >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# 让 nginx 进程与最终配置状态保持一致:需要则启动/热重载,不需要则停止
|
||||
@@ -4791,7 +4802,7 @@ start_pre() {
|
||||
|
||||
# 存在 nginx.conf 时自管 nginx(与 xray 守护一致):已运行则跳过,未运行才启动,失败不阻塞服务启动
|
||||
[ -s ${WORK_DIR}/nginx.conf ] && OPENRC_SERVICE+="
|
||||
if command -v /usr/sbin/nginx >/dev/null 2>&1 && ! pgrep -f \"nginx.*${WORK_DIR}/nginx.conf\" >/dev/null 2>&1; then
|
||||
if command -v /usr/sbin/nginx >/dev/null 2>&1 && ! ps -eo pid,args | grep -q \"[n]ginx.*${WORK_DIR}/nginx.conf\"; then
|
||||
/usr/sbin/nginx -c ${WORK_DIR}/nginx.conf
|
||||
fi"
|
||||
|
||||
@@ -4809,7 +4820,7 @@ stop_post() {
|
||||
/usr/sbin/nginx -s quit -c ${WORK_DIR}/nginx.conf 2>/dev/null
|
||||
sleep 1 # 等待优雅关闭
|
||||
# 如果仍运行,用 SIGKILL
|
||||
local NGINX_MASTER=\$(pgrep -f \"nginx: master process /usr/sbin/nginx -c ${WORK_DIR}/nginx.conf\")
|
||||
local NGINX_MASTER=\$(ps -eo pid,args | grep \"nginx: master process.*${WORK_DIR}/nginx.conf\" | awk '{print \$1; exit}')
|
||||
if [ -n \"\$NGINX_MASTER\" ]; then
|
||||
kill -KILL \$NGINX_MASTER 2>/dev/null
|
||||
fi
|
||||
@@ -5016,7 +5027,7 @@ fetch_nodes_value() {
|
||||
}' > ${WORK_DIR}/conf/04_experimental.json
|
||||
# 补全后立即热加载(SIGHUP)使 clash_api 监听马上生效;
|
||||
# 前台同步执行确保信号送达(SIGHUP reload 不断连 SSH);仅当 sing-box 运行中才 reload
|
||||
if pgrep -x sing-box >/dev/null 2>&1; then
|
||||
if ps -eo pid,args | grep -q '[s]ing-box run'; then
|
||||
cmd_systemctl reload sing-box
|
||||
fi
|
||||
fi
|
||||
@@ -5692,7 +5703,7 @@ naive+quic://${UUID[22]}:${UUID[22]}@${SERVER_IP_1}:${PORT_NAIVE}?congestion_con
|
||||
|
||||
{
|
||||
# 生成 sing-box SFM SFA SFI 订阅文件
|
||||
[ ! -s "$TEMP_DIR/sing-box-template" ] && wget --no-check-certificate --continue -qO "$TEMP_DIR/sing-box-template" "${GH_PROXY}${SUBSCRIBE_TEMPLATE}/sing-box" 2>/dev/null
|
||||
[ ! -s "$TEMP_DIR/sing-box-template" ] && wget --no-check-certificate --continue --tries=2 --timeout=10 -qO "$TEMP_DIR/sing-box-template" "${GH_PROXY}${SUBSCRIBE_TEMPLATE}/sing-box" 2>/dev/null
|
||||
cat $TEMP_DIR/sing-box-template | sed "s#\"<OUTBOUND_REPLACE>\",#$OUTBOUND_REPLACE#; s#\"<NODE_REPLACE>\"#${NODE_REPLACE%,}#g" | ${WORK_DIR}/jq > ${WORK_DIR}/subscribe/sing-box
|
||||
rm -f $TEMP_DIR/sing-box-template
|
||||
} &>/dev/null
|
||||
@@ -6279,7 +6290,7 @@ version() {
|
||||
|
||||
if [ "${UPDATE,,}" = 'y' ]; then
|
||||
check_system_info
|
||||
wget --no-check-certificate --continue ${GH_PROXY}https://github.com/SagerNet/sing-box/releases/download/v$ONLINE/sing-box-$ONLINE-linux-$SING_BOX_ARCH.tar.gz -qO- | tar xz -C $TEMP_DIR sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box
|
||||
wget --no-check-certificate --continue --tries=2 --timeout=10 ${GH_PROXY}https://github.com/SagerNet/sing-box/releases/download/v$ONLINE/sing-box-$ONLINE-linux-$SING_BOX_ARCH.tar.gz -qO- | tar xz -C $TEMP_DIR sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box
|
||||
|
||||
[ -s $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box ] || error "\n $(text 42) \n"
|
||||
if ! $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box check -C ${WORK_DIR}/conf >/dev/null; then
|
||||
@@ -6287,7 +6298,7 @@ version() {
|
||||
[ "${UPDATE_CONFIG,,}" = 'n' ] && exit 1
|
||||
|
||||
# 设置基础配置参数 dns.servers.prefer_go 和 dns.strategy
|
||||
local STRATEGY=$(grep -E --exclude="03_route.json" 'ipv4_only|ipv6_only|prefer_ipv4|prefer_ipv6' ${WORK_DIR}/conf/0*.json | awk -F '"' '{print $(NF-1); exit}')
|
||||
local STRATEGY=$(awk -F '"' '/ipv4_only|ipv6_only|prefer_ipv4|prefer_ipv6/ && FILENAME !~ /03_route\.json/{print $(NF-1); exit}' ${WORK_DIR}/conf/0*.json)
|
||||
STRATEGY=${STRATEGY:-prefer_ipv4}
|
||||
command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet systemd-resolved && local IS_PREFER_GO=false || local IS_PREFER_GO=true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user