From c0f9920531d6d1c847fd154f06fc4130d9a60180 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Sat, 1 Nov 2025 22:10:12 +0800 Subject: [PATCH 1/2] refactor: remove orphaned process cleanup functionality It might breaks mihomo starting. Due to potentiall process name processing, permissions verifing, permissions and safty FORCE KILL, find process faillure. --- src-tauri/src/core/manager/mod.rs | 2 - src-tauri/src/core/manager/process.rs | 244 -------------------------- 2 files changed, 246 deletions(-) delete mode 100644 src-tauri/src/core/manager/process.rs diff --git a/src-tauri/src/core/manager/mod.rs b/src-tauri/src/core/manager/mod.rs index 4ef122ae..05220f9e 100644 --- a/src-tauri/src/core/manager/mod.rs +++ b/src-tauri/src/core/manager/mod.rs @@ -1,6 +1,5 @@ mod config; mod lifecycle; -mod process; mod state; use anyhow::Result; @@ -74,7 +73,6 @@ impl CoreManager { } pub async fn init(&self) -> Result<()> { - self.cleanup_orphaned_processes().await?; self.start_core().await?; Ok(()) } diff --git a/src-tauri/src/core/manager/process.rs b/src-tauri/src/core/manager/process.rs deleted file mode 100644 index ccb32961..00000000 --- a/src-tauri/src/core/manager/process.rs +++ /dev/null @@ -1,244 +0,0 @@ -use super::CoreManager; -#[cfg(windows)] -use crate::process::AsyncHandler; -use crate::{ - constants::{process, timing}, - logging, - utils::logging::Type, -}; -use anyhow::Result; -#[cfg(windows)] -use anyhow::anyhow; - -impl CoreManager { - pub async fn cleanup_orphaned_processes(&self) -> Result<()> { - logging!(info, Type::Core, "Cleaning orphaned mihomo processes"); - - let current_pid = self - .state - .lock() - .child_sidecar - .as_ref() - .and_then(|c| c.pid()); - let target_processes = process::process_names(); - - let process_futures = target_processes.iter().map(|&name| { - let process_name = process::with_extension(name); - self.find_processes_by_name(process_name, name) - }); - - let process_results = futures::future::join_all(process_futures).await; - - let pids_to_kill: Vec<_> = process_results - .into_iter() - .filter_map(Result::ok) - .flat_map(|(pids, name)| { - pids.into_iter() - .filter(move |&pid| Some(pid) != current_pid) - .map(move |pid| (pid, name.clone())) - }) - .collect(); - - if pids_to_kill.is_empty() { - return Ok(()); - } - - let kill_futures = pids_to_kill - .iter() - .map(|(pid, name)| self.kill_process_verified(*pid, name.clone())); - - let killed_count = futures::future::join_all(kill_futures) - .await - .into_iter() - .filter(|&success| success) - .count(); - - if killed_count > 0 { - logging!( - info, - Type::Core, - "Cleaned {} orphaned processes", - killed_count - ); - } - - Ok(()) - } - - async fn find_processes_by_name( - &self, - process_name: String, - _target: &str, - ) -> Result<(Vec, String)> { - #[cfg(windows)] - { - use std::mem; - use winapi::um::{ - handleapi::CloseHandle, - tlhelp32::{ - CreateToolhelp32Snapshot, PROCESSENTRY32W, Process32FirstW, Process32NextW, - TH32CS_SNAPPROCESS, - }, - }; - - let process_name_clone = process_name.clone(); - let pids = AsyncHandler::spawn_blocking(move || -> Result> { - let mut pids = Vec::new(); - - unsafe { - let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if snapshot == winapi::um::handleapi::INVALID_HANDLE_VALUE { - return Err(anyhow!("Failed to create process snapshot")); - } - - let mut pe32: PROCESSENTRY32W = mem::zeroed(); - pe32.dwSize = mem::size_of::() as u32; - - if Process32FirstW(snapshot, &mut pe32) != 0 { - loop { - let end_pos = pe32 - .szExeFile - .iter() - .position(|&x| x == 0) - .unwrap_or(pe32.szExeFile.len()); - - let exe_file = String::from_utf16_lossy(&pe32.szExeFile[..end_pos]); - if exe_file.eq_ignore_ascii_case(&process_name_clone) { - pids.push(pe32.th32ProcessID); - } - - if Process32NextW(snapshot, &mut pe32) == 0 { - break; - } - } - } - - CloseHandle(snapshot); - } - - Ok(pids) - }) - .await??; - - Ok((pids, process_name)) - } - - #[cfg(not(windows))] - { - let cmd = if cfg!(target_os = "macos") { - "pgrep" - } else { - "pidof" - }; - let output = tokio::process::Command::new(cmd) - .arg(&process_name) - .output() - .await?; - - if !output.status.success() { - return Ok((Vec::new(), process_name)); - } - - let stdout = String::from_utf8_lossy(&output.stdout); - let pids: Vec = stdout - .split_whitespace() - .filter_map(|s| s.parse().ok()) - .collect(); - - Ok((pids, process_name)) - } - } - - async fn kill_process_verified(&self, pid: u32, process_name: String) -> bool { - #[cfg(windows)] - let success = { - use winapi::um::{ - handleapi::CloseHandle, - processthreadsapi::{OpenProcess, TerminateProcess}, - winnt::{HANDLE, PROCESS_TERMINATE}, - }; - - AsyncHandler::spawn_blocking(move || unsafe { - let handle: HANDLE = OpenProcess(PROCESS_TERMINATE, 0, pid); - if handle.is_null() { - return false; - } - let result = TerminateProcess(handle, 1) != 0; - CloseHandle(handle); - result - }) - .await - .unwrap_or(false) - }; - - #[cfg(not(windows))] - let success = tokio::process::Command::new("kill") - .args(["-9", &pid.to_string()]) - .output() - .await - .map(|output| output.status.success()) - .unwrap_or(false); - - if !success { - return false; - } - - tokio::time::sleep(timing::PROCESS_VERIFY_DELAY).await; - - if self.is_process_running(pid).await.unwrap_or(false) { - logging!( - warn, - Type::Core, - "Process {} (PID: {}) still running after termination", - process_name, - pid - ); - false - } else { - logging!( - info, - Type::Core, - "Terminated process {} (PID: {})", - process_name, - pid - ); - true - } - } - - async fn is_process_running(&self, pid: u32) -> Result { - #[cfg(windows)] - { - use winapi::{ - shared::minwindef::DWORD, - um::{ - handleapi::CloseHandle, - processthreadsapi::{GetExitCodeProcess, OpenProcess}, - winnt::{HANDLE, PROCESS_QUERY_INFORMATION}, - }, - }; - - AsyncHandler::spawn_blocking(move || unsafe { - let handle: HANDLE = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid); - if handle.is_null() { - return Ok(false); - } - let mut exit_code: DWORD = 0; - let result = GetExitCodeProcess(handle, &mut exit_code); - CloseHandle(handle); - Ok(result != 0 && exit_code == 259) - }) - .await? - } - - #[cfg(not(windows))] - { - let output = tokio::process::Command::new("ps") - .args(["-p", &pid.to_string()]) - .output() - .await?; - - Ok(output.status.success() && !output.stdout.is_empty()) - } - } -} From 4a7859bdae3adf4b395630e3518f916a0a54b09d Mon Sep 17 00:00:00 2001 From: Tunglies Date: Sat, 1 Nov 2025 22:50:19 +0800 Subject: [PATCH 2/2] refactor: replace hardcoded DNS config filename with constant reference (#5280) * refactor: replace hardcoded DNS config filename with constant reference * refactor: remove redundant import of constants in IClashTemp template method * refactor: add conditional compilation for DEFAULT_REDIR based on OS * refactor: simplify default TPROXY port handling and remove unused trace_err macro * refactor: simplify default TPROXY port fallback logic --- src-tauri/src/cmd/clash.rs | 14 +++++++------- src-tauri/src/config/clash.rs | 7 +++---- src-tauri/src/constants.rs | 33 ++------------------------------- src-tauri/src/core/backup.rs | 5 +++-- src-tauri/src/enhance/mod.rs | 3 ++- src-tauri/src/lib.rs | 6 +++--- src-tauri/src/utils/dirs.rs | 1 - src-tauri/src/utils/init.rs | 3 ++- src-tauri/src/utils/logging.rs | 9 --------- 9 files changed, 22 insertions(+), 59 deletions(-) diff --git a/src-tauri/src/cmd/clash.rs b/src-tauri/src/cmd/clash.rs index b6dbc1fa..1bcadea9 100644 --- a/src-tauri/src/cmd/clash.rs +++ b/src-tauri/src/cmd/clash.rs @@ -1,7 +1,9 @@ use super::CmdResult; +use crate::utils::dirs; use crate::{ cmd::StringifyErr, config::Config, + constants, core::{CoreManager, handle, validate::CoreConfigValidator}, }; use crate::{config::*, feat, logging, utils::logging::Type}; @@ -126,7 +128,7 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult { // 获取DNS配置文件路径 let dns_path = dirs::app_home_dir() .stringify_err()? - .join("dns_config.yaml"); + .join(constants::files::DNS_CONFIG); // 保存DNS配置到文件 let yaml_str = serde_yaml_ng::to_string(&dns_config).stringify_err()?; @@ -149,7 +151,7 @@ pub async fn apply_dns_config(apply: bool) -> CmdResult { // 读取DNS配置文件 let dns_path = dirs::app_home_dir() .stringify_err()? - .join("dns_config.yaml"); + .join(constants::files::DNS_CONFIG); if !dns_path.exists() { logging!(warn, Type::Config, "DNS config file not found"); @@ -227,7 +229,7 @@ pub fn check_dns_config_exists() -> CmdResult { let dns_path = dirs::app_home_dir() .stringify_err()? - .join("dns_config.yaml"); + .join(constants::files::DNS_CONFIG); Ok(dns_path.exists()) } @@ -240,7 +242,7 @@ pub async fn get_dns_config_content() -> CmdResult { let dns_path = dirs::app_home_dir() .stringify_err()? - .join("dns_config.yaml"); + .join(constants::files::DNS_CONFIG); if !fs::try_exists(&dns_path).await.stringify_err()? { return Err("DNS config file not found".into()); @@ -253,10 +255,8 @@ pub async fn get_dns_config_content() -> CmdResult { /// 验证DNS配置文件 #[tauri::command] pub async fn validate_dns_config() -> CmdResult<(bool, String)> { - use crate::utils::dirs; - let app_dir = dirs::app_home_dir().stringify_err()?; - let dns_path = app_dir.join("dns_config.yaml"); + let dns_path = app_dir.join(constants::files::DNS_CONFIG); let dns_path_str = dns_path.to_str().unwrap_or_default(); if !dns_path.exists() { diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index 9ca6276a..8b1c072a 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -1,4 +1,5 @@ use crate::config::Config; +use crate::constants::{network, tun as tun_const}; use crate::utils::dirs::{ipc_path, path_to_str}; use crate::utils::{dirs, help}; use crate::{logging, utils::logging::Type}; @@ -48,8 +49,6 @@ impl IClashTemp { } pub fn template() -> Self { - use crate::constants::{network, tun as tun_const}; - let mut map = Mapping::new(); let mut tun_config = Mapping::new(); let mut cors_map = Mapping::new(); @@ -215,9 +214,9 @@ impl IClashTemp { Value::Number(val_num) => val_num.as_u64().map(|u| u as u16), _ => None, }) - .unwrap_or(7896); + .unwrap_or(network::ports::DEFAULT_TPROXY); if port == 0 { - port = 7896; + port = network::ports::DEFAULT_TPROXY; } port } diff --git a/src-tauri/src/constants.rs b/src-tauri/src/constants.rs index e3ea41fd..3195ea40 100644 --- a/src-tauri/src/constants.rs +++ b/src-tauri/src/constants.rs @@ -5,15 +5,13 @@ pub mod network { pub const DEFAULT_EXTERNAL_CONTROLLER: &str = "127.0.0.1:9097"; pub mod ports { - #[allow(dead_code)] + #[cfg(not(target_os = "windows"))] pub const DEFAULT_REDIR: u16 = 7895; - #[allow(dead_code)] + #[cfg(target_os = "linux")] pub const DEFAULT_TPROXY: u16 = 7896; pub const DEFAULT_MIXED: u16 = 7897; pub const DEFAULT_SOCKS: u16 = 7898; pub const DEFAULT_HTTP: u16 = 7899; - #[allow(dead_code)] - pub const DEFAULT_EXTERNAL_CONTROLLER: u16 = 9097; #[cfg(not(feature = "verge-dev"))] pub const SINGLETON_SERVER: u16 = 33331; @@ -39,11 +37,8 @@ pub mod timing { pub const CONFIG_UPDATE_DEBOUNCE: Duration = Duration::from_millis(500); pub const CONFIG_RELOAD_DELAY: Duration = Duration::from_millis(300); - pub const PROCESS_VERIFY_DELAY: Duration = Duration::from_millis(100); - #[allow(dead_code)] pub const EVENT_EMIT_DELAY: Duration = Duration::from_millis(20); pub const STARTUP_ERROR_DELAY: Duration = Duration::from_secs(2); - #[allow(dead_code)] pub const ERROR_BATCH_DELAY: Duration = Duration::from_millis(300); #[cfg(target_os = "windows")] @@ -53,40 +48,16 @@ pub mod timing { } pub mod retry { - #[allow(dead_code)] pub const EVENT_EMIT_THRESHOLD: u64 = 10; - #[allow(dead_code)] - pub const SWR_ERROR_RETRY: usize = 2; } pub mod files { pub const RUNTIME_CONFIG: &str = "clash-verge.yaml"; pub const CHECK_CONFIG: &str = "clash-verge-check.yaml"; - #[allow(dead_code)] pub const DNS_CONFIG: &str = "dns_config.yaml"; - #[allow(dead_code)] pub const WINDOW_STATE: &str = "window_state.json"; } -pub mod process { - pub const VERGE_MIHOMO: &str = "verge-mihomo"; - pub const VERGE_MIHOMO_ALPHA: &str = "verge-mihomo-alpha"; - - pub fn process_names() -> [&'static str; 2] { - [VERGE_MIHOMO, VERGE_MIHOMO_ALPHA] - } - - #[cfg(windows)] - pub fn with_extension(name: &str) -> String { - format!("{}.exe", name) - } - - #[cfg(not(windows))] - pub fn with_extension(name: &str) -> String { - name.to_string() - } -} - pub mod error_patterns { pub const CONNECTION_ERRORS: &[&str] = &[ "Failed to create connection", diff --git a/src-tauri/src/core/backup.rs b/src-tauri/src/core/backup.rs index 167fbdd1..cf90bdd1 100644 --- a/src-tauri/src/core/backup.rs +++ b/src-tauri/src/core/backup.rs @@ -1,3 +1,4 @@ +use crate::constants::files::DNS_CONFIG; use crate::{ config::Config, logging, @@ -306,9 +307,9 @@ pub async fn create_backup() -> Result<(String, PathBuf), Error> { zip.start_file(dirs::VERGE_CONFIG, options)?; zip.write_all(serde_yaml_ng::to_string(&verge_config)?.as_bytes())?; - let dns_config_path = dirs::app_home_dir()?.join(dirs::DNS_CONFIG); + let dns_config_path = dirs::app_home_dir()?.join(DNS_CONFIG); if dns_config_path.exists() { - zip.start_file(dirs::DNS_CONFIG, options)?; + zip.start_file(DNS_CONFIG, options)?; zip.write_all(fs::read(&dns_config_path).await?.as_slice())?; } diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index 1deb49e5..e9183170 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -6,6 +6,7 @@ pub mod seq; mod tun; use self::{chain::*, field::*, merge::*, script::*, seq::*, tun::*}; +use crate::constants; use crate::utils::dirs; use crate::{config::Config, utils::tmpl}; use crate::{logging, utils::logging::Type}; @@ -442,7 +443,7 @@ fn apply_builtin_scripts( async fn apply_dns_settings(mut config: Mapping, enable_dns_settings: bool) -> Mapping { if enable_dns_settings && let Ok(app_dir) = dirs::app_home_dir() { - let dns_path = app_dir.join("dns_config.yaml"); + let dns_path = app_dir.join(constants::files::DNS_CONFIG); if dns_path.exists() && let Ok(dns_yaml) = fs::read_to_string(&dns_path).await diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 84cb533a..c6db41c0 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -10,6 +10,7 @@ mod feat; mod module; mod process; pub mod utils; +use crate::constants::files; #[cfg(target_os = "macos")] use crate::module::lightweight; #[cfg(target_os = "linux")] @@ -21,6 +22,7 @@ use crate::{ process::AsyncHandler, utils::{resolve, server}, }; +use anyhow::Result; use config::Config; use once_cell::sync::OnceCell; use tauri::{AppHandle, Manager}; @@ -32,8 +34,6 @@ use utils::logging::Type; pub static APP_HANDLE: OnceCell = OnceCell::new(); /// Application initialization helper functions mod app_init { - use anyhow::Result; - use super::*; /// Initialize singleton monitoring for other instances @@ -126,7 +126,7 @@ mod app_init { pub fn setup_window_state(app: &tauri::App) -> Result<(), Box> { logging!(info, Type::Setup, "初始化窗口状态管理..."); let window_state_plugin = tauri_plugin_window_state::Builder::new() - .with_filename("window_state.json") + .with_filename(files::WINDOW_STATE) .with_state_flags(tauri_plugin_window_state::StateFlags::default()) .build(); app.handle().plugin(window_state_plugin)?; diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index b16d3d8e..18ea6fac 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -24,7 +24,6 @@ pub static PORTABLE_FLAG: OnceCell = OnceCell::new(); pub static CLASH_CONFIG: &str = "config.yaml"; pub static VERGE_CONFIG: &str = "verge.yaml"; pub static PROFILE_YAML: &str = "profiles.yaml"; -pub static DNS_CONFIG: &str = "dns_config.yaml"; /// init portable flag pub fn init_portable_flag() -> Result<()> { diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index 72f19b08..61d4288c 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -3,6 +3,7 @@ use crate::utils::logging::NoModuleFilter; use crate::{ config::*, + constants, core::handle, logging, process::AsyncHandler, @@ -304,7 +305,7 @@ async fn init_dns_config() -> Result<()> { // 检查DNS配置文件是否存在 let app_dir = dirs::app_home_dir()?; - let dns_path = app_dir.join("dns_config.yaml"); + let dns_path = app_dir.join(constants::files::DNS_CONFIG); if !dns_path.exists() { logging!(info, Type::Setup, "Creating default DNS config file"); diff --git a/src-tauri/src/utils/logging.rs b/src-tauri/src/utils/logging.rs index a73b18eb..9f73c634 100644 --- a/src-tauri/src/utils/logging.rs +++ b/src-tauri/src/utils/logging.rs @@ -80,15 +80,6 @@ macro_rules! log_err { }; } -#[macro_export] -macro_rules! trace_err { - ($result: expr, $err_str: expr) => { - if let Err(err) = $result { - log::trace!(target: "app", "{}, err {}", $err_str, err); - } - } -} - /// wrap the anyhow error /// transform the error to String #[macro_export]