wip: windows service mode

This commit is contained in:
GyDi
2022-04-23 17:26:32 +08:00
parent c733bda6c3
commit 321963be83
4 changed files with 407 additions and 135 deletions

View File

@@ -57,61 +57,6 @@ impl Service {
self.start()
}
#[cfg(windows)]
pub fn install(&mut self) -> Result<()> {
use std::{env::current_exe, ffi::OsString};
use windows_service::{
service::{ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceType},
service_manager::{ServiceManager, ServiceManagerAccess},
};
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_binary_path = current_exe().unwrap().with_file_name("clash.exe");
let service_info = ServiceInfo {
name: OsString::from("clash_verge_core"),
display_name: OsString::from("Clash Verge Core"),
service_type: ServiceType::OWN_PROCESS,
start_type: ServiceStartType::OnDemand,
error_control: ServiceErrorControl::Normal,
executable_path: service_binary_path,
launch_arguments: vec![],
dependencies: vec![],
account_name: None, // run as System
account_password: None,
};
let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
service.set_description("Clash Core Service installed by Clash Verge")?;
Ok(())
}
#[cfg(windows)]
pub fn check_status(&mut self) -> Result<String> {
use windows_service::{
service::{ServiceAccess, ServiceState},
service_manager::{ServiceManager, ServiceManagerAccess},
};
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_STATUS; // | ServiceAccess::STOP | ServiceAccess::DELETE;
let service = service_manager.open_service("clash_verge_core", service_access)?;
let service_status = service.query_status()?;
Ok(format!("{:?}", service_status.current_state))
}
#[cfg(windows)]
pub fn start_service(&mut self) -> Result<()> {
Ok(())
}
/// update clash config
/// using PUT methods
pub fn set_config(&self, info: ClashInfo, config: Mapping, notice: Notice) -> Result<()> {
@@ -176,3 +121,91 @@ impl Drop for Service {
log_if_err!(self.stop());
}
}
/// ### Service Mode
///
#[cfg(windows)]
mod win_service {
use super::*;
use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommond;
use std::process::Command as StdCommond;
const SERVICE_NAME: &str = "clash_verge_service";
impl Service {
/// install the Clash Verge Service (windows only)
pub fn install_service(&mut self) -> Result<()> {
let binary_path = dirs::service_path();
let arg = format!("binpath={}", binary_path.as_os_str().to_string_lossy());
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
tauri::async_runtime::spawn(async move {
let args = [
"create",
SERVICE_NAME,
arg.as_str(),
"type=own",
"start=AUTO",
"displayname=Clash Verge Service",
];
let status = match level {
PrivilegeLevel::NotPrivileged => RunasCommond::new("sc").args(&args).status(),
_ => StdCommond::new("sc").args(&args).status(),
};
match status {
Ok(status) => {
if status.success() {
log::info!("install clash verge service successfully");
} else if status.code() == Some(1073i32) {
log::info!("clash verge service is installed");
} else {
log::error!(
"failed to install service with status {}",
status.code().unwrap()
);
}
}
Err(err) => log::error!("failed to install service for {err}"),
}
});
Ok(())
}
/// uninstall
pub fn uninstall_service(&mut self) -> Result<()> {
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
tauri::async_runtime::spawn(async move {
let args = ["delete", SERVICE_NAME];
let status = match level {
PrivilegeLevel::NotPrivileged => RunasCommond::new("sc").args(&args).status(),
_ => StdCommond::new("sc").args(&args).status(),
};
match status {
Ok(status) => {
if status.success() {
log::info!("uninstall clash verge service successfully");
} else {
log::error!(
"failed to uninstall service with status {}",
status.code().unwrap()
);
}
}
Err(err) => log::error!("failed to uninstall service for {err}"),
}
});
Ok(())
}
}
}

View File

@@ -15,6 +15,9 @@ static VERGE_CONFIG: &str = "verge.yaml";
static PROFILE_YAML: &str = "profiles.yaml";
static PROFILE_TEMP: &str = "clash-verge-runtime.yaml";
#[cfg(windows)]
static mut RESOURCE_DIR: Option<PathBuf> = None;
/// portable flag
#[allow(unused)]
static mut PORTABLE_FLAG: bool = false;
@@ -58,9 +61,16 @@ pub fn app_home_dir() -> PathBuf {
/// get the resources dir
pub fn app_resources_dir(package_info: &PackageInfo) -> PathBuf {
resource_dir(package_info, &Env::default())
let res_dir = resource_dir(package_info, &Env::default())
.unwrap()
.join("resources")
.join("resources");
#[cfg(windows)]
unsafe {
RESOURCE_DIR = Some(res_dir.clone());
}
res_dir
}
/// profiles dir
@@ -92,3 +102,14 @@ pub fn profiles_temp_path() -> PathBuf {
#[cfg(feature = "debug-yml")]
return app_home_dir().join(PROFILE_TEMP);
}
#[cfg(windows)]
static SERVICE_PATH: &str = "clash-verge-service.exe";
#[cfg(windows)]
pub fn service_path() -> PathBuf {
unsafe {
let res_dir = RESOURCE_DIR.clone().unwrap();
res_dir.join(SERVICE_PATH)
}
}