#[cfg(target_os = "windows")] use crate::utils::autostart as startup_shortcut; use crate::{ config::{Config, IVerge}, core::handle::Handle, singleton, }; use anyhow::Result; use clash_verge_logging::{Type, logging, logging_error}; #[cfg(not(target_os = "windows"))] use parking_lot::Mutex; use parking_lot::RwLock; use scopeguard::defer; use smartstring::alias::String; use std::{ sync::{ Arc, atomic::{AtomicBool, Ordering}, }, time::Duration, }; use sysproxy::{Autoproxy, GuardMonitor, GuardType, Sysproxy}; use tauri_plugin_autostart::ManagerExt as _; pub struct Sysopt { update_sysproxy: AtomicBool, reset_sysproxy: AtomicBool, #[cfg(not(target_os = "windows"))] sysproxy: Arc>, #[cfg(not(target_os = "windows"))] autoproxy: Arc>, guard: Arc>, } impl Default for Sysopt { fn default() -> Self { Self { update_sysproxy: AtomicBool::new(false), reset_sysproxy: AtomicBool::new(false), #[cfg(not(target_os = "windows"))] sysproxy: Arc::new(Mutex::new(Sysproxy::default())), #[cfg(not(target_os = "windows"))] autoproxy: Arc::new(Mutex::new(Autoproxy::default())), guard: Arc::new(RwLock::new(GuardMonitor::new( GuardType::None, Duration::from_secs(30), ))), } } } #[cfg(target_os = "windows")] static DEFAULT_BYPASS: &str = "localhost;127.*;192.168.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;"; #[cfg(target_os = "linux")] static DEFAULT_BYPASS: &str = "localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,::1"; #[cfg(target_os = "macos")] static DEFAULT_BYPASS: &str = "127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,localhost,*.local,*.crashlytics.com,"; async fn get_bypass() -> String { let use_default = Config::verge() .await .latest_arc() .use_default_bypass .unwrap_or(true); let res = { let verge = Config::verge().await; let verge = verge.latest_arc(); verge.system_proxy_bypass.clone() }; let custom_bypass = match res { Some(bypass) => bypass, None => "".into(), }; if custom_bypass.is_empty() { DEFAULT_BYPASS.into() } else if use_default { format!("{DEFAULT_BYPASS},{custom_bypass}").into() } else { custom_bypass } } // Uses tokio Command with CREATE_NO_WINDOW flag to avoid DLL initialization issues during shutdown #[cfg(target_os = "windows")] async fn execute_sysproxy_command(args: Vec) -> Result<()> { use crate::utils::dirs; use anyhow::bail; #[allow(unused_imports)] // Required for .creation_flags() method use std::os::windows::process::CommandExt as _; use tokio::process::Command; let binary_path = dirs::service_path()?; let sysproxy_exe = binary_path.with_file_name("sysproxy.exe"); if !sysproxy_exe.exists() { bail!("sysproxy.exe not found"); } let output = Command::new(sysproxy_exe) .args(args) .creation_flags(0x08000000) // CREATE_NO_WINDOW - 隐藏窗口 .output() .await?; if !output.status.success() { bail!("sysproxy exe run failed"); } Ok(()) } singleton!(Sysopt, SYSOPT); impl Sysopt { fn new() -> Self { Self::default() } fn access_guard(&self) -> Arc> { Arc::clone(&self.guard) } pub async fn refresh_guard(&self) { logging!(info, Type::Core, "Refreshing system proxy guard..."); let verge = Config::verge().await.latest_arc(); if !verge.enable_system_proxy.unwrap_or(false) { logging!(info, Type::Core, "System proxy is disabled."); self.access_guard().write().stop(); return; } if !verge.enable_proxy_guard.unwrap_or(false) { logging!(info, Type::Core, "System proxy guard is disabled."); return; } logging!( info, Type::Core, "Updating system proxy with duration: {} seconds", verge.proxy_guard_duration.unwrap_or(30) ); { let guard = self.access_guard(); guard.write().set_interval(Duration::from_secs( verge.proxy_guard_duration.unwrap_or(30), )); } logging!(info, Type::Core, "Starting system proxy guard..."); { let guard = self.access_guard(); guard.write().start(); } } /// init the sysproxy pub async fn update_sysproxy(&self) -> Result<()> { if self.update_sysproxy.load(Ordering::Acquire) { logging!(info, Type::Core, "Sysproxy update is already in progress."); return Ok(()); } if self .update_sysproxy .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) .is_err() { logging!(info, Type::Core, "Sysproxy update is already in progress."); return Ok(()); } defer! { logging!(info, Type::Core, "Sysproxy update completed."); self.update_sysproxy.store(false, Ordering::Release); } let verge = Config::verge().await.latest_arc(); let port = { let verge_port = verge.verge_mixed_port; match verge_port { Some(port) => port, None => Config::clash().await.latest_arc().get_mixed_port(), } }; let pac_port = IVerge::get_singleton_port(); let (sys_enable, pac_enable, proxy_host) = { ( verge.enable_system_proxy.unwrap_or(false), verge.proxy_auto_config.unwrap_or(false), verge .proxy_host .clone() .unwrap_or_else(|| String::from("127.0.0.1")), ) }; #[cfg(not(target_os = "windows"))] { // 先 await, 避免持有锁导致的 Send 问题 let bypass = get_bypass().await; let mut sys = self.sysproxy.lock(); sys.enable = false; sys.host = proxy_host.clone().into(); sys.port = port; sys.bypass = bypass.into(); let mut auto = self.autoproxy.lock(); auto.enable = false; auto.url = format!("http://{proxy_host}:{pac_port}/commands/pac"); if !sys_enable { sys.set_system_proxy()?; auto.set_auto_proxy()?; self.access_guard() .write() .set_guard_type(GuardType::Sysproxy(sys.clone())); return Ok(()); } if pac_enable { sys.enable = false; auto.enable = true; sys.set_system_proxy()?; auto.set_auto_proxy()?; self.access_guard() .write() .set_guard_type(GuardType::Autoproxy(auto.clone())); return Ok(()); } if sys_enable { auto.enable = false; sys.enable = true; auto.set_auto_proxy()?; sys.set_system_proxy()?; drop(auto); self.access_guard() .write() .set_guard_type(GuardType::Sysproxy(sys.clone())); drop(sys); return Ok(()); } } #[cfg(target_os = "windows")] { if !sys_enable { self.access_guard().write().set_guard_type(GuardType::None); return self.reset_sysproxy().await; } let (args, guard_type): (Vec, GuardType) = if pac_enable { let address = format!("http://{proxy_host}:{pac_port}/commands/pac"); ( vec!["pac".into(), address.clone()], GuardType::Autoproxy(Autoproxy { enable: true, url: address, }), ) } else { let address = format!("{proxy_host}:{port}"); let bypass = get_bypass().await; let bypass_for_guard = bypass.as_str().to_owned(); ( vec!["global".into(), address.clone(), bypass.into()], GuardType::Sysproxy(Sysproxy { enable: true, host: proxy_host.clone().into(), port, bypass: bypass_for_guard, }), ) }; execute_sysproxy_command(args).await?; self.access_guard().write().set_guard_type(guard_type); } Ok(()) } /// reset the sysproxy #[allow(clippy::unused_async)] pub async fn reset_sysproxy(&self) -> Result<()> { if self .reset_sysproxy .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) .is_err() { return Ok(()); } defer! { self.reset_sysproxy.store(false, Ordering::SeqCst); } //直接关闭所有代理 #[cfg(not(target_os = "windows"))] { let mut sysproxy = self.sysproxy.lock(); sysproxy.enable = false; sysproxy.set_system_proxy()?; drop(sysproxy); let mut autoproxy = self.autoproxy.lock(); autoproxy.enable = false; autoproxy.set_auto_proxy()?; } #[cfg(target_os = "windows")] { execute_sysproxy_command(vec!["set".into(), "1".into()]).await?; } Ok(()) } /// update the startup pub async fn update_launch(&self) -> Result<()> { let enable_auto_launch = { Config::verge().await.latest_arc().enable_auto_launch }; let is_enable = enable_auto_launch.unwrap_or(false); logging!( info, Type::System, "Setting auto-launch state to: {:?}", is_enable ); // 首先尝试使用快捷方式方法 #[cfg(target_os = "windows")] { if is_enable { if let Err(e) = startup_shortcut::create_shortcut().await { logging!(error, Type::Setup, "创建启动快捷方式失败: {e}"); // 如果快捷方式创建失败,回退到原来的方法 self.try_original_autostart_method(is_enable); } else { return Ok(()); } } else if let Err(e) = startup_shortcut::remove_shortcut().await { logging!(error, Type::Setup, "删除启动快捷方式失败: {e}"); self.try_original_autostart_method(is_enable); } else { return Ok(()); } } #[cfg(not(target_os = "windows"))] { // 非Windows平台使用原来的方法 self.try_original_autostart_method(is_enable); } Ok(()) } /// 尝试使用原来的自启动方法 fn try_original_autostart_method(&self, is_enable: bool) { let app_handle = Handle::app_handle(); let autostart_manager = app_handle.autolaunch(); if is_enable { logging_error!(Type::System, "{:?}", autostart_manager.enable()); } else { logging_error!(Type::System, "{:?}", autostart_manager.disable()); } } /// 获取当前自启动的实际状态 pub fn get_launch_status(&self) -> Result { // 首先尝试检查快捷方式是否存在 #[cfg(target_os = "windows")] { match startup_shortcut::is_shortcut_enabled() { Ok(enabled) => { logging!(info, Type::System, "快捷方式自启动状态: {enabled}"); return Ok(enabled); } Err(e) => { logging!(error, Type::System, "检查快捷方式失败,尝试原来的方法: {e}"); } } } // 回退到原来的方法 let app_handle = Handle::app_handle(); let autostart_manager = app_handle.autolaunch(); match autostart_manager.is_enabled() { Ok(status) => { logging!(info, Type::System, "Auto launch status: {status}"); Ok(status) } Err(e) => { logging!(error, Type::System, "Failed to get auto launch status: {e}"); Err(anyhow::anyhow!("Failed to get auto launch status: {}", e)) } } } }