diff --git a/Changelog.md b/Changelog.md index 3058f54f4..215dbc5f1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,10 @@ - 修复 DNS 覆写:空内容报错、初始值覆盖已有设置、已填写字段残留原配置、空字段在高级编辑器中缺失 - 修复首次以管理员身份启动后,系统服务因数据目录所有权不符而拒绝启动内核的问题 +**🐧 Linux** + +- Linux 修复安装或修复服务后仍无法启动内核、开启 TUN 模式的问题 +
diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index 9245c1ce2..19e8f9cfc 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -583,11 +583,11 @@ fn install_service() -> Result<()> { .iter() .map(|core| service_core_path(core, ".exe")) .collect::>>()?; - install_windows_service_with_cores(&cores, run_windows_service_installer) + install_service_with_cores(&cores, run_windows_service_installer) } -#[cfg(target_os = "windows")] -fn install_windows_service_with_cores( +#[cfg(any(target_os = "windows", target_os = "linux", test))] +fn install_service_with_cores( cores: &[PathBuf], mut run_installer: impl FnMut(&[std::ffi::OsString]) -> Result<()>, ) -> Result<()> { @@ -712,7 +712,15 @@ fn uninstall_service() -> Result<()> { #[cfg(target_os = "linux")] fn install_service() -> Result<()> { logging!(info, Type::Service, "install service"); + let cores = crate::config::IVerge::VALID_CLASH_CORES + .iter() + .map(|core| service_core_path(core, "")) + .collect::>>()?; + install_service_with_cores(&cores, run_linux_service_installer) +} +#[cfg(target_os = "linux")] +fn run_linux_service_installer(arguments: &[std::ffi::OsString]) -> Result<()> { let install_path = packaged_service_tool_path("clash-verge-service-install", || { Ok(tauri::utils::platform::current_exe()?.with_file_name("clash-verge-service-install")) })?; @@ -723,12 +731,13 @@ fn install_service() -> Result<()> { let elevator = crate::utils::help::linux_elevator(); let output = if linux_running_as_root() { - StdCommand::new(&install_path).output()? + StdCommand::new(&install_path).args(arguments).output()? } else { - let result = StdCommand::new(&elevator) - .arg("--disable-internal-agent") - .arg(&install_path) - .output()?; + let mut elevated = StdCommand::new(&elevator); + if elevator.contains("pkexec") { + elevated.arg("--disable-internal-agent"); + } + let result = elevated.arg(&install_path).args(arguments).output()?; // 如果 pkexec 执行失败,回退到 sudo if !result.status.success() && elevator.contains("pkexec") { @@ -738,7 +747,7 @@ fn install_service() -> Result<()> { "pkexec failed with code {}, falling back to sudo", result.status.code().unwrap_or(-1) ); - StdCommand::new("sudo").arg(&install_path).output()? + StdCommand::new("sudo").arg(&install_path).args(arguments).output()? } else { result } @@ -2243,20 +2252,19 @@ mod tests { } } - #[cfg(target_os = "windows")] #[test] - fn windows_service_install_combines_service_and_attested_cores() -> anyhow::Result<()> { + fn service_install_combines_service_and_attested_cores() -> anyhow::Result<()> { use std::ffi::OsString; - let root = TestDirectory::new("scoop install")?; + let root = TestDirectory::new("service install")?; let cores: Vec<_> = crate::config::IVerge::VALID_CLASH_CORES .iter() - .map(|name| root.path().join(format!("{name}.exe"))) + .map(|name| root.path().join(format!("{name}{}", std::env::consts::EXE_SUFFIX))) .collect(); for core in &cores { std::fs::write(core, b"abc")?; } let mut calls = Vec::new(); - super::install_windows_service_with_cores(&cores, |arguments| { + super::install_service_with_cores(&cores, |arguments| { calls.push(arguments.to_vec()); Ok(()) })?; @@ -2275,16 +2283,19 @@ mod tests { Ok(()) } - #[cfg(target_os = "windows")] #[test] - fn windows_service_install_skips_unavailable_cores() -> anyhow::Result<()> { + fn service_install_skips_unavailable_cores() -> anyhow::Result<()> { let root = TestDirectory::new("missing-core")?; - let core = root.path().join("verge-mihomo.exe"); - let missing = root.path().join("verge-mihomo-alpha.exe"); + let core = root + .path() + .join(format!("verge-mihomo{}", std::env::consts::EXE_SUFFIX)); + let missing = root + .path() + .join(format!("verge-mihomo-alpha{}", std::env::consts::EXE_SUFFIX)); std::fs::write(&core, b"abc")?; let cores = [core.clone(), missing.clone(), root.path().to_path_buf()]; let mut calls = Vec::new(); - super::install_windows_service_with_cores(&cores, |arguments| { + super::install_service_with_cores(&cores, |arguments| { calls.push(arguments.to_vec()); Ok(()) })?; @@ -2293,7 +2304,7 @@ mod tests { assert_eq!(calls[0][0], "--install-service"); assert_eq!(calls[0][2], core.as_os_str()); - let error = super::install_windows_service_with_cores(&[missing, root.path().to_path_buf()], |_| { + let error = super::install_service_with_cores(&[missing, root.path().to_path_buf()], |_| { panic!("no available core must fail before invoking the installer") }) .expect_err("installation requires at least one available core"); @@ -2301,14 +2312,15 @@ mod tests { Ok(()) } - #[cfg(target_os = "windows")] #[test] - fn windows_service_install_propagates_installer_failure() -> anyhow::Result<()> { + fn service_install_propagates_installer_failure() -> anyhow::Result<()> { let root = TestDirectory::new("install-failure")?; - let core = root.path().join("verge-mihomo.exe"); + let core = root + .path() + .join(format!("verge-mihomo{}", std::env::consts::EXE_SUFFIX)); std::fs::write(&core, b"abc")?; let mut calls = 0; - let result = super::install_windows_service_with_cores(std::slice::from_ref(&core), |_| { + let result = super::install_service_with_cores(std::slice::from_ref(&core), |_| { calls += 1; bail!("installer failed with exit code 1") });