Fix clashoff stopping runtime

This commit is contained in:
Arvin
2026-06-25 10:31:49 +08:00
parent dd0bcb3999
commit 12b465c02e
2 changed files with 214 additions and 7 deletions
+39 -2
View File
@@ -296,15 +296,21 @@ cmd_on() {
}
cmd_off() {
local system_proxy_rc
local system_proxy_rc stop_rc
local system_proxy_state_before system_proxy_cleanup_blocked="false"
prepare
system_proxy_state_before="$(system_proxy_status 2>/dev/null || echo off)"
if system_proxy_disable; then
:
else
system_proxy_rc=$?
write_runtime_value "RUNTIME_BOOT_PROXY_KEEP" "false" 2>/dev/null || true
if [ "$system_proxy_state_before" = "on" ]; then
system_proxy_cleanup_blocked="true"
if [ "$system_proxy_rc" -eq 2 ]; then
ui_warn "当前环境不支持清理系统代理持久块,已继续关闭运行时"
else
@@ -312,11 +318,42 @@ cmd_off() {
ui_next "clashctl doctor"
fi
fi
fi
if status_is_running 2>/dev/null; then
stop_rc=0
service_stop || stop_rc=$?
if [ "$stop_rc" -ne 0 ]; then
die_state "关闭运行时失败:运行后端 stop 返回 ${stop_rc}" "clashctl logs"
fi
if ! wait_runtime_stopped 8; then
die_state "关闭运行时失败:代理内核仍在运行" "clashctl logs"
fi
fi
if [ "$system_proxy_cleanup_blocked" = "true" ]; then
die_state "系统代理持久块未清理:$(system_proxy_env_file)" "请使用有权限的用户执行 clashctl off,或手动清理该文件中的 clash-for-linux 代理块"
fi
ui_blank
echo "🧹 系统代理已关闭"
echo "🧹 代理已关闭"
ui_blank
}
wait_runtime_stopped() {
local timeout="${1:-8}"
local elapsed=0
while status_is_running 2>/dev/null; do
[ "$elapsed" -lt "$timeout" ] || return 1
sleep 1
elapsed=$((elapsed + 1))
done
return 0
}
ui_internal_url() {
local controller host port
controller="${1:-}"
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
source_clashctl_for_tests() {
set -- ""
# Source the real functions; suppress the no-arg usage printed by the command dispatcher.
source "$PROJECT_DIR/scripts/core/clashctl.sh" >/dev/null
}
source_clashctl_for_tests
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
prepare() { :; }
ui_warn() { printf 'warn: %s\n' "$*" >> "$tmp_dir/output"; }
ui_next() { printf 'next: %s\n' "$*" >> "$tmp_dir/output"; }
ui_blank() { :; }
system_proxy_env_file() { printf '%s\n' "$tmp_dir/environment"; }
write_runtime_value() { :; }
die_state() {
printf 'die: %s\nnext: %s\n' "$1" "$2" >> "$tmp_dir/output"
exit 1
}
run_case() {
local name="$1"
local proxy_rc="$2"
local stop_calls_file="$tmp_dir/stop-calls"
local output_file="$tmp_dir/output"
: > "$stop_calls_file"
: > "$output_file"
system_proxy_disable() { return "$proxy_rc"; }
service_stop() { printf 'stop\n' >> "$stop_calls_file"; }
status_is_running() {
[ ! -s "$stop_calls_file" ]
}
cmd_off > "$output_file"
if [ "$(wc -l < "$stop_calls_file" | tr -d ' ')" != "1" ]; then
echo "not ok - $name: service_stop was not called exactly once" >&2
return 1
fi
if ! grep -Fq "代理已关闭" "$output_file"; then
echo "not ok - $name: success feedback missing" >&2
sed 's/^/ /' "$output_file" >&2
return 1
fi
echo "ok - $name"
}
run_case "stops runtime after disabling system proxy" 0
run_case "still stops runtime when system proxy cleanup is unsupported" 2
run_stop_failure_case() {
local stop_calls_file="$tmp_dir/stop-failure-calls"
local output_file="$tmp_dir/output"
: > "$stop_calls_file"
: > "$output_file"
system_proxy_disable() { return 0; }
service_stop() {
printf 'stop\n' >> "$stop_calls_file"
return 7
}
status_is_running() { return 0; }
if ( cmd_off ) > "$output_file"; then
echo "not ok - stop failure: cmd_off unexpectedly succeeded" >&2
return 1
fi
if ! grep -Fq "运行后端 stop 返回 7" "$output_file"; then
echo "not ok - stop failure: missing stop failure detail" >&2
sed 's/^/ /' "$output_file" >&2
return 1
fi
if grep -Fq "代理已关闭" "$output_file"; then
echo "not ok - stop failure: printed success after stop failure" >&2
sed 's/^/ /' "$output_file" >&2
return 1
fi
echo "ok - stop failure returns non-zero"
}
run_persistent_cleanup_blocked_case() {
local stop_calls_file="$tmp_dir/persistent-cleanup-blocked-calls"
local output_file="$tmp_dir/output"
: > "$stop_calls_file"
: > "$output_file"
system_proxy_status() { printf 'on\n'; }
system_proxy_disable() { return 2; }
service_stop() { printf 'stop\n' >> "$stop_calls_file"; }
status_is_running() {
[ ! -s "$stop_calls_file" ]
}
if ( cmd_off ) > "$output_file"; then
echo "not ok - persistent cleanup blocked: cmd_off unexpectedly succeeded" >&2
return 1
fi
if [ "$(wc -l < "$stop_calls_file" | tr -d ' ')" != "1" ]; then
echo "not ok - persistent cleanup blocked: service_stop was not called exactly once" >&2
return 1
fi
if ! grep -Fq "系统代理持久块未清理" "$output_file"; then
echo "not ok - persistent cleanup blocked: missing cleanup failure detail" >&2
sed 's/^/ /' "$output_file" >&2
return 1
fi
if grep -Fq "代理已关闭" "$output_file"; then
echo "not ok - persistent cleanup blocked: printed success while persistent proxy remained" >&2
sed 's/^/ /' "$output_file" >&2
return 1
fi
echo "ok - persistent cleanup failure returns non-zero after stopping runtime"
}
run_still_running_case() {
local stop_calls_file="$tmp_dir/still-running-calls"
local output_file="$tmp_dir/output"
: > "$stop_calls_file"
: > "$output_file"
system_proxy_disable() { return 0; }
service_stop() { printf 'stop\n' >> "$stop_calls_file"; }
status_is_running() { return 0; }
wait_runtime_stopped() { return 1; }
if ( cmd_off ) > "$output_file"; then
echo "not ok - still running: cmd_off unexpectedly succeeded" >&2
return 1
fi
if ! grep -Fq "代理内核仍在运行" "$output_file"; then
echo "not ok - still running: missing still-running detail" >&2
sed 's/^/ /' "$output_file" >&2
return 1
fi
if grep -Fq "代理已关闭" "$output_file"; then
echo "not ok - still running: printed success while runtime stayed up" >&2
sed 's/^/ /' "$output_file" >&2
return 1
fi
echo "ok - still running returns non-zero"
}
run_stop_failure_case
run_persistent_cleanup_blocked_case
run_still_running_case