refactor(sysproxy): delegate macOS network monitoring to sysproxy-rs

This commit is contained in:
wonfen
2026-09-14 22:10:33 +08:00
parent 8a5548af10
commit d289a608d9
5 changed files with 34 additions and 53 deletions
+1 -4
View File
@@ -476,10 +476,7 @@ impl CoreManager {
// rather than fail — only if the watcher is actually live; a user toggling while
// offline still fails fast.
#[cfg(target_os = "macos")]
let watcher_armed = crate::core::network_watch::is_armed();
#[cfg(not(target_os = "macos"))]
let watcher_armed = false;
if watcher_armed
if crate::core::network_watch::is_armed()
&& !crate::utils::resolve::is_resolve_done()
&& Config::verge()
.await
+27 -38
View File
@@ -5,53 +5,35 @@
use crate::{config::Config, core::CoreManager, core::proxy_control, process::AsyncHandler};
use clash_verge_logging::{Type, logging};
use core_foundation::{
array::CFArray,
runloop::{CFRunLoop, kCFRunLoopCommonModes},
string::CFString,
};
use std::sync::atomic::{AtomicBool, Ordering};
use system_configuration::dynamic_store::{SCDynamicStore, SCDynamicStoreBuilder, SCDynamicStoreCallBackContext};
use parking_lot::Mutex;
use sysproxy::NetworkServiceMonitor;
/// Set once the subscription is live. Startup defers the proxy write to the watcher only then.
static ARMED: AtomicBool = AtomicBool::new(false);
static MONITOR: Mutex<Option<NetworkServiceMonitor>> = Mutex::new(None);
pub fn is_armed() -> bool {
ARMED.load(Ordering::Acquire)
MONITOR.lock().is_some()
}
/// Subscribe to the primary IPv4 service on the main run loop. Armed on return, so a service
/// appearing from here on is never missed.
pub fn start() {
let store = SCDynamicStoreBuilder::new("clash-verge-rev")
.callback_context(SCDynamicStoreCallBackContext {
callout: on_change,
info: (),
})
.build();
let keys = CFArray::from_CFTypes(&[CFString::from_static_string("State:/Network/Global/IPv4")]);
let Some(source) = store
.filter(|store| store.set_notification_keys(&keys, &CFArray::<CFString>::from_CFTypes(&[])))
.and_then(|store| store.create_run_loop_source())
else {
logging!(
match NetworkServiceMonitor::start(|| {
AsyncHandler::spawn(reapply);
}) {
Ok(monitor) => {
*MONITOR.lock() = Some(monitor);
logging!(
debug,
Type::Core,
"network watch armed; the system proxy is re-applied on network changes"
);
}
Err(error) => logging!(
warn,
Type::Core,
"could not watch the network; the proxy is not re-applied on changes"
);
return;
};
CFRunLoop::get_main().add_source(&source, unsafe { kCFRunLoopCommonModes });
ARMED.store(true, Ordering::Release);
logging!(
debug,
Type::Core,
"network watch armed; the system proxy is re-applied on network changes"
);
}
fn on_change(_: SCDynamicStore, _: CFArray<CFString>, (): &mut ()) {
AsyncHandler::spawn(reapply);
"could not watch the network; the proxy is not re-applied on changes: {error}"
),
}
}
async fn reapply() {
@@ -66,10 +48,17 @@ async fn reapply() {
.latest_arc()
.enable_system_proxy
.unwrap_or_default()
|| !proxy_control::has_network_service().await
{
return;
}
if !proxy_control::has_network_service().await {
logging!(
debug,
Type::Core,
"network changed without a service to write the proxy on; waiting for the next change"
);
return;
}
if let Err(error) = manager.apply_proxy_after_start().await {
logging!(
warn,
+4 -5
View File
@@ -385,12 +385,11 @@ async fn current_service_proxy_config(verge: &IVerge) -> Result<MacosProxyConfig
}
/// Whether macOS has a network service to write the proxy on right now.
#[cfg(target_os = "macos")]
pub async fn has_network_service() -> bool {
tokio::task::spawn_blocking(
|| !matches!(sysproxy::Sysproxy::get_system_proxy(), Err(error) if is_missing_network_service(&error)),
)
.await
.unwrap_or(true)
tokio::task::spawn_blocking(|| sysproxy::Sysproxy::has_network_service().unwrap_or(true))
.await
.unwrap_or(true)
}
pub fn is_reportable(error: &anyhow::Error) -> bool {