feat: Try support service mode for MacOS

This commit is contained in:
MystiPanda
2024-03-31 21:44:34 +08:00
parent ddab131102
commit 1c583d2ea9
11 changed files with 158 additions and 177 deletions

View File

@@ -331,7 +331,6 @@ pub fn exit_app(app_handle: tauri::AppHandle) {
std::process::exit(0);
}
#[cfg(any(windows, target_os = "linux"))]
pub mod service {
use super::*;
use crate::core::service;
@@ -352,24 +351,6 @@ pub mod service {
}
}
#[cfg(not(any(windows, target_os = "linux")))]
pub mod service {
use super::*;
#[tauri::command]
pub async fn check_service() -> CmdResult {
Ok(())
}
#[tauri::command]
pub async fn install_service() -> CmdResult {
Ok(())
}
#[tauri::command]
pub async fn uninstall_service() -> CmdResult {
Ok(())
}
}
#[cfg(not(windows))]
pub mod uwp {
use super::*;

View File

@@ -1,3 +1,4 @@
use super::service;
use super::{clash_api, logger::Logger};
use crate::log_err;
use crate::{config::*, utils::dirs};
@@ -93,10 +94,9 @@ impl CoreManager {
None => false,
};
#[cfg(any(target_os = "windows", target_os = "linux"))]
if *self.use_service_mode.lock() {
log::debug!(target: "app", "stop the core by service");
log_err!(super::service::stop_core_by_service().await);
log_err!(service::stop_core_by_service().await);
should_kill = true;
}
@@ -105,32 +105,27 @@ impl CoreManager {
sleep(Duration::from_millis(500)).await;
}
#[cfg(any(target_os = "windows", target_os = "linux"))]
{
use super::service;
// 服务模式
let enable = { Config::verge().latest().enable_service_mode };
let enable = enable.unwrap_or(false);
// 服务模式
let enable = { Config::verge().latest().enable_service_mode };
let enable = enable.unwrap_or(false);
*self.use_service_mode.lock() = enable;
*self.use_service_mode.lock() = enable;
if enable {
// 服务模式启动失败就直接运行sidecar
log::debug!(target: "app", "try to run core in service mode");
if enable {
// 服务模式启动失败就直接运行sidecar
log::debug!(target: "app", "try to run core in service mode");
match (|| async {
service::check_service().await?;
service::run_core_by_service(&config_path).await
})()
.await
{
Ok(_) => return Ok(()),
Err(err) => {
// 修改这个值免得stop出错
*self.use_service_mode.lock() = false;
log::error!(target: "app", "{err}");
}
match (|| async {
service::check_service().await?;
service::run_core_by_service(&config_path).await
})()
.await
{
Ok(_) => return Ok(()),
Err(err) => {
// 修改这个值免得stop出错
*self.use_service_mode.lock() = false;
log::error!(target: "app", "{err}");
}
}
}
@@ -205,7 +200,6 @@ impl CoreManager {
/// 重启内核
pub fn recover_core(&'static self) -> Result<()> {
// 服务模式不管
#[cfg(any(target_os = "windows", target_os = "linux"))]
if *self.use_service_mode.lock() {
return Ok(());
}
@@ -238,11 +232,10 @@ impl CoreManager {
/// 停止核心运行
pub fn stop_core(&self) -> Result<()> {
#[cfg(any(target_os = "windows", target_os = "linux"))]
if *self.use_service_mode.lock() {
log::debug!(target: "app", "stop the core by service");
tauri::async_runtime::block_on(async move {
log_err!(super::service::stop_core_by_service().await);
log_err!(service::stop_core_by_service().await);
});
return Ok(());
}

View File

@@ -1,5 +1,3 @@
#![cfg(any(target_os = "windows", target_os = "linux"))]
use crate::config::Config;
use crate::utils::dirs;
use anyhow::{bail, Context, Result};
@@ -31,14 +29,13 @@ pub struct JsonResponse {
/// Install the Clash Verge Service
/// 该函数应该在协程或者线程中执行避免UAC弹窗阻塞主线程
///
///
#[cfg(target_os = "windows")]
pub async fn install_service() -> Result<()> {
use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand;
use std::os::windows::process::CommandExt;
let binary_path = dirs::service_path()?;
let install_path = binary_path.with_file_name("install-service.exe");
@@ -80,7 +77,11 @@ pub async fn install_service() -> Result<()> {
let elevator = crate::utils::unix_helper::linux_elevator();
let status = match get_effective_uid() {
0 => StdCommand::new(installer_path).status()?,
_ => StdCommand::new(elevator).arg("sh").arg("-c").arg(installer_path).status()?,
_ => StdCommand::new(elevator)
.arg("sh")
.arg("-c")
.arg(installer_path)
.status()?,
};
if !status.success() {
@@ -93,6 +94,30 @@ pub async fn install_service() -> Result<()> {
Ok(())
}
#[cfg(target_os = "macos")]
pub async fn install_service() -> Result<()> {
let binary_path = dirs::service_path()?;
let installer_path = binary_path.with_file_name("install-service");
if !installer_path.exists() {
bail!("installer not found");
}
let shell = installer_path.to_string_lossy();
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
let status = StdCommand::new("osascript")
.args(vec!["-e", &command])
.status()?;
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
);
}
Ok(())
}
/// Uninstall the Clash Verge Service
/// 该函数应该在协程或者线程中执行避免UAC弹窗阻塞主线程
#[cfg(target_os = "windows")]
@@ -101,7 +126,6 @@ pub async fn uninstall_service() -> Result<()> {
use runas::Command as RunasCommand;
use std::os::windows::process::CommandExt;
let binary_path = dirs::service_path()?;
let uninstall_path = binary_path.with_file_name("uninstall-service.exe");
@@ -143,7 +167,11 @@ pub async fn uninstall_service() -> Result<()> {
let elevator = crate::utils::unix_helper::linux_elevator();
let status = match get_effective_uid() {
0 => StdCommand::new(uninstaller_path).status()?,
_ => StdCommand::new(elevator).arg("sh").arg("-c").arg(uninstaller_path).status()?,
_ => StdCommand::new(elevator)
.arg("sh")
.arg("-c")
.arg(uninstaller_path)
.status()?,
};
if !status.success() {
@@ -156,6 +184,32 @@ pub async fn uninstall_service() -> Result<()> {
Ok(())
}
#[cfg(target_os = "macos")]
pub async fn uninstall_service() -> Result<()> {
let binary_path = dirs::service_path()?;
let uninstaller_path = binary_path.with_file_name("uninstall-service");
if !uninstaller_path.exists() {
bail!("uninstaller not found");
}
let shell = uninstaller_path.to_string_lossy();
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
let status = StdCommand::new("osascript")
.args(vec!["-e", &command])
.status()?;
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
);
}
Ok(())
}
/// check the windows service status
pub async fn check_service() -> Result<JsonResponse> {
let url = format!("{SERVICE_URL}/get_clash");
@@ -185,7 +239,7 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
let clash_core = { Config::verge().latest().clash_core.clone() };
let clash_core = clash_core.unwrap_or("clash".into());
let bin_ext = if cfg!(windows) {".exe"} else {""};
let bin_ext = if cfg!(windows) { ".exe" } else { "" };
let clash_bin = format!("{clash_core}{bin_ext}");
let bin_path = current_exe()?.with_file_name(clash_bin);
let bin_path = dirs::path_to_str(&bin_path)?;

View File

@@ -92,17 +92,16 @@ pub fn clash_pid_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join("clash.pid"))
}
#[cfg(target_os = "linux")]
#[cfg(not(target_os = "windows"))]
pub fn service_path() -> Result<PathBuf> {
Ok(app_resources_dir()?.join("clash-verge-service"))
}
}
#[cfg(windows)]
pub fn service_path() -> Result<PathBuf> {
Ok(app_resources_dir()?.join("clash-verge-service.exe"))
}
#[cfg(any(windows, target_os = "linux"))]
pub fn service_log_file() -> Result<PathBuf> {
use chrono::Local;

View File

@@ -135,13 +135,12 @@ pub fn delete_log() -> Result<()> {
for file in fs::read_dir(&log_dir)?.flatten() {
let _ = process_file(file);
}
#[cfg(target_os = "windows")]
{
let service_log_dir = log_dir.join("service");
for file in fs::read_dir(&service_log_dir)?.flatten() {
let _ = process_file(file);
}
let service_log_dir = log_dir.join("service");
for file in fs::read_dir(&service_log_dir)?.flatten() {
let _ = process_file(file);
}
Ok(())
}

View File

@@ -1,7 +1,6 @@
use std::process::Command;
#[cfg(target_os = "linux")]
pub fn linux_elevator() -> &'static str {
use std::process::Command;
match Command::new("which").arg("pkexec").output() {
Ok(output) => {
if output.stdout.is_empty() {