fix: handle proxy cleanup failures during app exit

This commit is contained in:
wonfen
2026-08-19 20:04:21 +08:00
parent fc5fdbfea6
commit 9b9feb9a39
6 changed files with 70 additions and 41 deletions
Generated
+25 -25
View File
@@ -1257,8 +1257,8 @@ dependencies = [
[[package]]
name = "clash_verge_service_ipc"
version = "2.6.2"
source = "git+https://github.com/clash-verge-rev/clash-verge-service-ipc.git?tag=v2.6.2#edf7cb57811d4e572f1e2d607472cb87bd421ef4"
version = "2.6.3"
source = "git+https://github.com/clash-verge-rev/clash-verge-service-ipc.git?tag=v2.6.3#322bc6fad12538195aeaeb3711231d7523c88e6f"
dependencies = [
"anyhow",
"compact_str",
@@ -1363,7 +1363,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -2044,7 +2044,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -2324,7 +2324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -3397,7 +3397,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.6.5",
"socket2 0.5.10",
"system-configuration",
"tokio",
"tower-service",
@@ -3417,7 +3417,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.62.2",
"windows-core 0.61.2",
]
[[package]]
@@ -4241,7 +4241,7 @@ dependencies = [
"png 0.18.1",
"serde",
"thiserror 2.0.19",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -4410,7 +4410,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -4830,7 +4830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.45.0",
]
[[package]]
@@ -5504,7 +5504,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
"socket2 0.6.5",
"socket2 0.5.10",
"thiserror 2.0.19",
"tokio",
"tracing",
@@ -5543,9 +5543,9 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.6.5",
"socket2 0.5.10",
"tracing",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -6093,7 +6093,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -6106,7 +6106,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.12.1",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -6165,7 +6165,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -6796,7 +6796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -7009,8 +7009,8 @@ dependencies = [
[[package]]
name = "sysproxy"
version = "0.6.0"
source = "git+https://github.com/clash-verge-rev/sysproxy-rs?branch=main#16500c3e91a4fa8d541108fb603c5ee4258004a6"
version = "0.6.1"
source = "git+https://github.com/clash-verge-rev/sysproxy-rs?branch=main#c63788cf88e3deac1087d759c16543235d66b921"
dependencies = [
"iptools",
"log",
@@ -7683,10 +7683,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.3",
"getrandom 0.3.4",
"once_cell",
"rustix 1.1.4",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -8405,7 +8405,7 @@ dependencies = [
"png 0.18.1",
"serde",
"thiserror 2.0.19",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -8508,7 +8508,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset",
"tempfile",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -9093,7 +9093,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.48.0",
]
[[package]]
@@ -9697,7 +9697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10"
dependencies = [
"cfg-if",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
+1 -1
View File
@@ -96,7 +96,7 @@ tauri-plugin-devtools = { version = "2.1.0" }
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", branch = "refactor-use-reqwest-ipc" }
clash_verge_logger = { git = "https://github.com/clash-verge-rev/clash-verge-logger" }
async-trait = "0.1.92"
clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.6.2", version = "2.6.2", features = [
clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.6.3", version = "2.6.3", features = [
"client",
] }
arc-swap = "1.9.2"
+18 -5
View File
@@ -170,12 +170,25 @@ where
first_failure([("global proxy", global), ("PAC", pac)])
}
/// Treats a missing network service as a completed disable: with nothing to write to, the
/// requested "no proxy" state already holds. Turning a proxy on must still fail.
fn tolerate_missing_network_service(enabling: bool, outcome: sysproxy::Result<()>) -> Result<()> {
match outcome {
#[cfg(target_os = "macos")]
Err(sysproxy::Error::NoActiveNetworkService) if !enabling => {
logging!(warn, Type::Core, "no active network service, nothing to turn off");
Ok(())
}
other => other.map_err(anyhow::Error::from),
}
}
/// Force both proxy kinds off, in one blocking hop.
async fn disable_all_proxies(sys: Sysproxy, auto: Autoproxy) -> Result<()> {
tokio::task::spawn_blocking(move || {
disable_both(
|| sys.set_system_proxy().map_err(anyhow::Error::from),
|| auto.set_auto_proxy().map_err(anyhow::Error::from),
|| tolerate_missing_network_service(sys.enable, sys.set_system_proxy()),
|| tolerate_missing_network_service(auto.enable, auto.set_auto_proxy()),
)
})
.await?
@@ -472,11 +485,11 @@ impl Sysopt {
let applied = tokio::task::spawn_blocking(move || {
for (index, step) in apply_steps.into_iter().enumerate() {
let written = match step {
ProxyApplyStep::Autoproxy => auto.set_auto_proxy(),
ProxyApplyStep::Sysproxy => sys.set_system_proxy(),
ProxyApplyStep::Autoproxy => tolerate_missing_network_service(auto.enable, auto.set_auto_proxy()),
ProxyApplyStep::Sysproxy => tolerate_missing_network_service(sys.enable, sys.set_system_proxy()),
};
if let Err(error) = written {
return Err((index > 0, anyhow::Error::from(error)));
return Err((index > 0, error));
}
}
Ok(())
+4 -1
View File
@@ -54,7 +54,10 @@ pub async fn restart_app() {
if !cleanup_result.core_stopped {
handle::Handle::global().clear_is_exiting();
handle::Handle::notice_message("app_restart::core_stop_failed", "");
handle::Handle::notice_message(
"app_restart::core_stop_failed",
cleanup_result.stop_error.unwrap_or_default(),
);
return;
}
+14 -7
View File
@@ -4,14 +4,17 @@ use crate::module::lightweight;
use crate::utils;
use crate::utils::window_manager::WindowManager;
use clash_verge_logging::{Type, logging};
use parking_lot::Mutex;
use tokio::time::Duration;
#[cfg(target_os = "macos")]
use tokio::time::timeout;
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Default)]
pub struct CleanupResult {
pub all_success: bool,
pub core_stopped: bool,
/// Why the stop failed, for the cancelled-exit notice.
pub stop_error: Option<String>,
}
const fn should_abort_exit_after_cleanup(core_stopped: bool) -> bool {
@@ -29,14 +32,12 @@ where
AncillaryFuture: std::future::Future<Output = bool>,
{
if !stop_core().await {
return CleanupResult {
all_success: false,
core_stopped: false,
};
return CleanupResult::default();
}
CleanupResult {
all_success: ancillary_cleanup().await,
core_stopped: true,
stop_error: None,
}
}
@@ -128,7 +129,10 @@ pub async fn quit() -> clash_verge_signal::ShutdownOutcome {
if should_abort_exit_after_cleanup(cleanup_result.core_stopped) {
handle::Handle::global().clear_is_exiting();
handle::Handle::notice_message("app_quit::core_stop_failed", "");
handle::Handle::notice_message(
"app_quit::core_stop_failed",
cleanup_result.stop_error.unwrap_or_default(),
);
return clash_verge_signal::ShutdownOutcome::Canceled;
}
@@ -145,7 +149,8 @@ pub async fn clean_async() -> CleanupResult {
"Starting interactive cleanup; controlled core stop will be awaited to completion"
);
let result = run_interactive_cleanup_transition(
let stop_error = Mutex::new(None);
let mut result = run_interactive_cleanup_transition(
|| async {
logging!(info, Type::System, "Stopping core for interactive quit or restart");
match CoreManager::global().stop_core().await {
@@ -159,6 +164,7 @@ pub async fn clean_async() -> CleanupResult {
Type::Window,
"Controlled core stop failed; interactive quit or restart must remain cancelled: {error:#}"
);
*stop_error.lock() = Some(format!("{error:#}"));
false
}
}
@@ -166,6 +172,7 @@ pub async fn clean_async() -> CleanupResult {
restore_dns_after_core_stop,
)
.await;
result.stop_error = stop_error.into_inner();
logging!(
info,
@@ -33,9 +33,15 @@ export const handleNoticeMessage = (
'settings.sections.system.notifications.tunMode.autoDisableFailed',
),
'app_restart::core_stop_failed': () =>
showNotice.error('layout.feedback.errors.restartCoreStopFailed'),
showNotice.error(
'layout.feedback.errors.restartCoreStopFailed',
msg || undefined,
),
'app_quit::core_stop_failed': () =>
showNotice.error('layout.feedback.errors.quitCoreStopFailed'),
showNotice.error(
'layout.feedback.errors.quitCoreStopFailed',
msg || undefined,
),
update_with_clash_proxy: () =>
showNotice.success(
'settings.feedback.notifications.updater.withClashProxySuccess',