From 53867bc3a9c513134c82b606918ddc03acb89db5 Mon Sep 17 00:00:00 2001 From: Tunglies Date: Sat, 31 Jan 2026 18:09:50 +0800 Subject: [PATCH] fix: enhance error reporting during service installation (#6161) * fix: enhance error reporting during service installation * fix: correct variable name in install_service function for clarity * fix(windows): improve output handling in install_service function --- Changelog.md | 2 + src-tauri/src/core/service.rs | 88 ++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/Changelog.md b/Changelog.md index 11f7071a5..be9924a9f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,4 +20,6 @@
🚀 优化改进 +- 安装服务失败时报告更详细的错误 +
diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index ed866f864..23cf8c731 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -9,6 +9,7 @@ use clash_verge_service_ipc::CoreConfig; use compact_str::CompactString; use once_cell::sync::Lazy; use std::{ + borrow::Cow, env::current_exe, path::{Path, PathBuf}, process::Command as StdCommand, @@ -64,6 +65,7 @@ fn uninstall_service() -> Result<()> { #[cfg(target_os = "windows")] fn install_service() -> Result<()> { + use std::process::Output; logging!(info, Type::Service, "install service"); use deelevate::{PrivilegeLevel, Token}; @@ -79,13 +81,30 @@ fn install_service() -> Result<()> { let token = Token::with_current_process()?; let level = token.privilege_level()?; - let status = match level { - PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).show(false).status()?, - _ => StdCommand::new(install_path).creation_flags(0x08000000).status()?, + let output = match level { + PrivilegeLevel::NotPrivileged => { + let status = RunasCommand::new(&install_path).show(false).status()?; + Output { + status, + stdout: Vec::new(), + stderr: Vec::new(), + } + } + _ => { + // StdCommand returns Output directly + StdCommand::new(&install_path).creation_flags(0x08000000).output()? + } }; - if !status.success() { - bail!("failed to install service with status {}", status.code().unwrap_or(-1)); + if let Some((code, err)) = check_output_error(&output) { + logging!( + error, + Type::Service, + "failed to install service code: {}, details: {}", + code, + err + ); + bail!("failed to install service code: {}, details: {}", code, err); } Ok(()) @@ -160,41 +179,42 @@ fn install_service() -> Result<()> { let install_shell: String = install_path.to_string_lossy().replace(" ", "\\ "); let elevator = crate::utils::help::linux_elevator(); - let status = if linux_running_as_root() { - StdCommand::new(&install_path).status()? + let output = if linux_running_as_root() { + StdCommand::new(&install_path).output()? } else { let result = StdCommand::new(&elevator) .arg("sh") .arg("-c") .arg(&install_shell) - .status()?; + .output()?; // 如果 pkexec 执行失败,回退到 sudo - if !result.success() && elevator.contains("pkexec") { + if !result.status.success() && elevator.contains("pkexec") { logging!( warn, Type::Service, "pkexec failed with code {}, falling back to sudo", - result.code().unwrap_or(-1) + result.status.code().unwrap_or(-1) ); StdCommand::new("sudo") .arg("sh") .arg("-c") .arg(&install_shell) - .status()? + .output()? } else { result } }; - logging!( - info, - Type::Service, - "install status code:{}", - status.code().unwrap_or(-1) - ); - if !status.success() { - bail!("failed to install service with status {}", status.code().unwrap_or(-1)); + if let Some((code, err)) = check_output_error(&output) { + logging!( + error, + Type::Service, + "failed to install service code: {}, details: {}", + code, + err + ); + bail!("failed to install service code: {}, details: {}", code, err); } Ok(()) @@ -262,15 +282,37 @@ fn install_service() -> Result<()> { r#"do shell script "sudo CLASH_VERGE_SERVICE_GID={gid} '{install_shell}'" with administrator privileges with prompt "{prompt}""# ); - let status = StdCommand::new("osascript").args(vec!["-e", &command]).status()?; - - if !status.success() { - bail!("failed to install service with status {}", status.code().unwrap_or(-1)); + let output = StdCommand::new("osascript").args(vec!["-e", &command]).output()?; + if let Some((code, err)) = check_output_error(&output) { + logging!( + error, + Type::Service, + "failed to install service code: {}, details: {}", + code, + err + ); + bail!("failed to install service code: {}, details: {}", code, err); } Ok(()) } +fn check_output_error(output: &std::process::Output) -> Option<(i32, Cow<'_, str>)> { + if output.status.success() { + return None; + } + let code = output.status.code().unwrap_or(-1); + let stderr = String::from_utf8_lossy(&output.stderr); + if !stderr.is_empty() { + return Some((code, stderr)); + } + let stdout = String::from_utf8_lossy(&output.stdout); + if !stdout.is_empty() { + return Some((code, stdout)); + } + Some((code, Cow::Borrowed("Unknown error"))) +} + fn reinstall_service() -> Result<()> { logging!(info, Type::Service, "reinstall service");