Skip to content

Commit

Permalink
feat: Try support service mode for MacOS
Browse files Browse the repository at this point in the history
  • Loading branch information
MystiPanda committed Mar 31, 2024
1 parent ddab131 commit 1c583d2
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 180 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ dist-ssr
update.json
scripts/_env.sh
.vscode
.tool-version
.tool-versions
80 changes: 28 additions & 52 deletions scripts/check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,8 @@ const resolvePlugin = async () => {
}
};

// Linux service chmod
const resolveLinuxServicePermission = async () => {
// service chmod
const resolveServicePermission = async () => {
const serviceExecutables = [
"clash-verge-service",
"install-service",
Expand All @@ -376,37 +376,30 @@ const resolveLinuxServicePermission = async () => {

const SERVICE_URL = `https://github.com/clash-verge-rev/clash-verge-service/releases/download/${SIDECAR_HOST}`;

const resolveService = () =>
const resolveService = () => {
let ext = platform === "win32" ? ".exe" : "";
resolveResource({
file: "clash-verge-service.exe",
downloadURL: `${SERVICE_URL}/clash-verge-service.exe`,
});
const resolveLinuxService = () => {
resolveResource({
file: "clash-verge-service",
downloadURL: `${SERVICE_URL}/clash-verge-service`,
file: "clash-verge-service" + ext,
downloadURL: `${SERVICE_URL}/clash-verge-service${ext}`,
});
};
const resolveInstall = () =>
resolveResource({
file: "install-service.exe",
downloadURL: `${SERVICE_URL}/install-service.exe`,
});
const resolveLinuxInstall = () =>
resolveResource({
file: "install-service",
downloadURL: `${SERVICE_URL}/install-service`,
});
const resolveUninstall = () =>

const resolveInstall = () => {
let ext = platform === "win32" ? ".exe" : "";
resolveResource({
file: "uninstall-service.exe",
downloadURL: `${SERVICE_URL}/uninstall-service.exe`,
file: "install-service" + ext,
downloadURL: `${SERVICE_URL}/install-service.exe${ext}`,
});
const resolveLinuxUninstall = () =>
};

const resolveUninstall = () => {
let ext = platform === "win32" ? ".exe" : "";
resolveResource({
file: "uninstall-service",
downloadURL: `${SERVICE_URL}/uninstall-service`,
file: "uninstall-service" + ext,
downloadURL: `${SERVICE_URL}/uninstall-service${ext}`,
});
};

const resolveSetDnsScript = () =>
resolveResource({
file: "set_dns.sh",
Expand Down Expand Up @@ -453,27 +446,9 @@ const tasks = [
retry: 5,
},
{ name: "plugin", func: resolvePlugin, retry: 5, winOnly: true },
{ name: "service", func: resolveService, retry: 5, winOnly: true },
{
name: "linux_service",
func: resolveLinuxService,
retry: 5,
linuxOnly: true,
},
{ name: "install", func: resolveInstall, retry: 5, winOnly: true },
{
name: "linux_install",
func: resolveLinuxInstall,
retry: 5,
linuxOnly: true,
},
{ name: "uninstall", func: resolveUninstall, retry: 5, winOnly: true },
{
name: "linux_uninstall",
func: resolveLinuxUninstall,
retry: 5,
linuxOnly: true,
},
{ name: "service", func: resolveService, retry: 5 },
{ name: "install", func: resolveInstall, retry: 5 },
{ name: "uninstall", func: resolveUninstall, retry: 5 },
{ name: "set_dns_script", func: resolveSetDnsScript, retry: 5 },
{ name: "unset_dns_script", func: resolveUnSetDnsScript, retry: 5 },
{ name: "mmdb", func: resolveMmdb, retry: 5 },
Expand All @@ -486,18 +461,19 @@ const tasks = [
winOnly: true,
},
{
name: "linux_service_chmod",
func: resolveLinuxServicePermission,
name: "service_chmod",
func: resolveServicePermission,
retry: 1,
linuxOnly: true,
unixOnly: true,
},
];

async function runTask() {
const task = tasks.shift();
if (!task) return;
if (task.winOnly && process.platform !== "win32") return runTask();
if (task.linuxOnly && process.platform !== "linux") return runTask();
if (task.winOnly && platform !== "win32") return runTask();
if (task.linuxOnly && platform !== "linux") return runTask();
if (task.unixOnly && platform === "win32") return runTask();

for (let i = 0; i < task.retry; i++) {
try {
Expand Down
19 changes: 0 additions & 19 deletions src-tauri/src/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::*;
Expand Down
55 changes: 24 additions & 31 deletions src-tauri/src/core/core.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::service;
use super::{clash_api, logger::Logger};
use crate::log_err;
use crate::{config::*, utils::dirs};
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);

*self.use_service_mode.lock() = enable;

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}");
}
// 服务模式
let enable = { Config::verge().latest().enable_service_mode };
let enable = enable.unwrap_or(false);

*self.use_service_mode.lock() = enable;

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}");
}
}
}
Expand Down Expand Up @@ -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(());
}
Expand Down Expand Up @@ -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(());
}
Expand Down
70 changes: 62 additions & 8 deletions src-tauri/src/core/service.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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() {
Expand All @@ -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")]
Expand All @@ -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");

Expand Down Expand Up @@ -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() {
Expand All @@ -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");
Expand Down Expand Up @@ -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)?;
Expand Down
Loading

0 comments on commit 1c583d2

Please sign in to comment.