2022-09-24 18:11:12 +00:00
|
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
use anyhow::{anyhow, Result};
|
2022-09-29 07:34:55 +00:00
|
|
|
use std::{ffi::OsString, thread, time::Duration};
|
2022-09-24 18:11:12 +00:00
|
|
|
use windows_service::{
|
|
|
|
define_windows_service,
|
|
|
|
service::{
|
|
|
|
ServiceAccess, ServiceControl, ServiceControlAccept, ServiceErrorControl, ServiceExitCode,
|
|
|
|
ServiceInfo, ServiceStartType, ServiceState, ServiceStatus, ServiceType,
|
|
|
|
},
|
|
|
|
service_control_handler::{self, ServiceControlHandlerResult},
|
|
|
|
service_dispatcher,
|
|
|
|
service_manager::{ServiceManager, ServiceManagerAccess},
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::config::Config;
|
|
|
|
use crate::server::server_executor;
|
|
|
|
|
|
|
|
const SERVICE_NAME: &str = "socks5ws_srv";
|
|
|
|
const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;
|
|
|
|
const SERVICE_DISPLAY: &str = "socks5ws proxy";
|
|
|
|
const SERVICE_DESCRIPTION: &str = "SOCKS5 proxy windows service";
|
|
|
|
|
2022-09-28 12:01:48 +00:00
|
|
|
trait ServiceStatusEx {
|
|
|
|
fn running() -> ServiceStatus;
|
|
|
|
fn stopped() -> ServiceStatus;
|
|
|
|
fn stopped_with_error(code: u32) -> ServiceStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ServiceStatusEx for ServiceStatus {
|
|
|
|
fn running() -> ServiceStatus {
|
|
|
|
ServiceStatus {
|
|
|
|
service_type: SERVICE_TYPE,
|
|
|
|
current_state: ServiceState::Running,
|
|
|
|
controls_accepted: ServiceControlAccept::STOP,
|
|
|
|
exit_code: ServiceExitCode::Win32(0),
|
|
|
|
checkpoint: 0,
|
|
|
|
wait_hint: Duration::default(),
|
|
|
|
process_id: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn stopped() -> ServiceStatus {
|
|
|
|
ServiceStatus {
|
|
|
|
current_state: ServiceState::Stopped,
|
|
|
|
controls_accepted: ServiceControlAccept::empty(),
|
|
|
|
..Self::running()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn stopped_with_error(code: u32) -> ServiceStatus {
|
|
|
|
ServiceStatus {
|
|
|
|
exit_code: ServiceExitCode::ServiceSpecific(code),
|
|
|
|
..Self::stopped()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
pub fn install() -> Result<()> {
|
2022-09-24 18:11:12 +00:00
|
|
|
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
|
|
|
|
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
let service_binary_path = std::env::current_exe()?;
|
2022-09-24 18:11:12 +00:00
|
|
|
|
|
|
|
let service_info = ServiceInfo {
|
|
|
|
name: SERVICE_NAME.into(),
|
|
|
|
display_name: OsString::from(SERVICE_DISPLAY),
|
|
|
|
service_type: ServiceType::OWN_PROCESS,
|
|
|
|
start_type: ServiceStartType::OnDemand,
|
|
|
|
error_control: ServiceErrorControl::Normal,
|
|
|
|
executable_path: service_binary_path,
|
|
|
|
launch_arguments: vec!["run".into()],
|
|
|
|
dependencies: vec![],
|
|
|
|
account_name: Some(OsString::from(r#"NT AUTHORITY\NetworkService"#)),
|
|
|
|
account_password: None,
|
|
|
|
};
|
|
|
|
let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
|
|
|
|
service.set_description(SERVICE_DESCRIPTION)?;
|
|
|
|
log::info!("service installed");
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
pub fn uninstall() -> Result<()> {
|
2022-09-24 18:11:12 +00:00
|
|
|
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(SERVICE_NAME, service_access)?;
|
|
|
|
|
|
|
|
let service_status = service.query_status()?;
|
|
|
|
if service_status.current_state != ServiceState::Stopped {
|
|
|
|
log::warn!("stopping service");
|
|
|
|
service.stop()?;
|
|
|
|
// Wait for service to stop
|
|
|
|
thread::sleep(Duration::from_secs(5));
|
|
|
|
}
|
|
|
|
|
|
|
|
service.delete()?;
|
|
|
|
log::warn!("service deleted");
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
pub fn stop() -> Result<()> {
|
2022-09-24 18:11:12 +00:00
|
|
|
let manager_access = ServiceManagerAccess::CONNECT;
|
|
|
|
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
|
|
|
|
|
|
|
let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP;
|
|
|
|
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
|
|
|
|
|
|
|
|
let service_status = service.query_status()?;
|
|
|
|
if service_status.current_state != ServiceState::Stopped {
|
|
|
|
log::info!("stopping service");
|
|
|
|
service.stop()?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
pub fn start() -> Result<()> {
|
2022-09-24 18:11:12 +00:00
|
|
|
let manager_access = ServiceManagerAccess::CONNECT;
|
|
|
|
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
|
|
|
|
|
|
|
let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::START;
|
|
|
|
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
|
|
|
|
|
|
|
|
let service_status = service.query_status()?;
|
|
|
|
if service_status.current_state != ServiceState::Running {
|
|
|
|
log::info!("start service");
|
|
|
|
service.start(Vec::<&str>::new().as_slice())?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run() -> Result<()> {
|
|
|
|
// Register generated `ffi_service_main` with the system and start the service, blocking
|
|
|
|
// this thread until the service is stopped.
|
|
|
|
log::info!("service run");
|
2023-08-17 20:09:44 +00:00
|
|
|
service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
|
|
|
|
|
|
|
|
Ok(())
|
2022-09-24 18:11:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the windows service boilerplate.
|
|
|
|
// The boilerplate contains the low-level service entry function (ffi_service_main) that parses
|
|
|
|
// incoming service arguments into Vec<OsString> and passes them to user defined service
|
|
|
|
// entry (my_service_main).
|
|
|
|
define_windows_service!(ffi_service_main, my_service_main);
|
|
|
|
|
|
|
|
// Service entry function which is called on background thread by the system with service
|
|
|
|
// parameters. There is no stdout or stderr at this point so make sure to configure the log
|
|
|
|
// output to file if needed.
|
|
|
|
pub fn my_service_main(_arguments: Vec<OsString>) {
|
|
|
|
if let Err(e) = run_service() {
|
2023-08-17 20:09:44 +00:00
|
|
|
log::error!("error: {}", e);
|
2022-09-24 18:11:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
pub fn run_service() -> Result<()> {
|
2022-09-29 07:34:55 +00:00
|
|
|
// Create a cancellation token to be able to cancell server
|
|
|
|
let control_token = CancellationToken::new();
|
|
|
|
let server_token = control_token.child_token();
|
2022-09-28 12:01:48 +00:00
|
|
|
|
2022-09-24 18:11:12 +00:00
|
|
|
// Define system service event handler that will be receiving service events.
|
|
|
|
let event_handler = move |control_event| -> ServiceControlHandlerResult {
|
|
|
|
match control_event {
|
|
|
|
// Notifies a service to report its current status information to the service
|
|
|
|
// control manager. Always return NoError even if not implemented.
|
|
|
|
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
|
|
|
|
|
|
|
|
// Handle stop
|
|
|
|
ServiceControl::Stop => {
|
2022-09-29 07:34:55 +00:00
|
|
|
log::info!("service stop event received");
|
|
|
|
control_token.cancel();
|
2022-09-24 18:11:12 +00:00
|
|
|
ServiceControlHandlerResult::NoError
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => ServiceControlHandlerResult::NotImplemented,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Register system service event handler.
|
|
|
|
// The returned status handle should be used to report service status changes to the system.
|
2023-08-17 20:09:44 +00:00
|
|
|
let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
|
2022-09-24 18:11:12 +00:00
|
|
|
|
|
|
|
// Tell the system that service is running
|
2023-08-17 20:09:44 +00:00
|
|
|
status_handle.set_service_status(ServiceStatus::running())?;
|
2022-09-24 18:11:12 +00:00
|
|
|
|
|
|
|
let cfg = Config::get();
|
|
|
|
log::info!("start with config: {:#?}", cfg);
|
|
|
|
|
2022-09-29 07:34:55 +00:00
|
|
|
let result = std::thread::spawn(move || server_executor(cfg, server_token)).join();
|
2022-09-24 18:11:12 +00:00
|
|
|
|
2022-09-29 07:34:55 +00:00
|
|
|
log::info!("server thread stoped");
|
2022-09-24 18:11:12 +00:00
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
// join() => Err(), when thread panic
|
2022-09-28 12:01:48 +00:00
|
|
|
if let Err(e) = result {
|
|
|
|
log::error!("server panic: {:#?}", e);
|
2023-08-17 20:09:44 +00:00
|
|
|
status_handle.set_service_status(ServiceStatus::stopped_with_error(1))?;
|
|
|
|
return Err(anyhow!("server panic"));
|
2022-09-28 12:01:48 +00:00
|
|
|
}
|
2022-09-24 18:11:12 +00:00
|
|
|
|
2023-08-17 20:09:44 +00:00
|
|
|
// join() => Ok(Err()), when server executor error
|
2022-09-29 07:34:55 +00:00
|
|
|
if let Err(e) = result.unwrap() {
|
2022-09-28 12:01:48 +00:00
|
|
|
log::error!("server error: {:#?}", e);
|
2023-08-17 20:09:44 +00:00
|
|
|
status_handle.set_service_status(ServiceStatus::stopped_with_error(2))?;
|
|
|
|
return Err(anyhow!("server error"));
|
2022-09-28 12:01:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Tell the system that service has stopped.
|
2023-08-17 20:09:44 +00:00
|
|
|
status_handle.set_service_status(ServiceStatus::stopped())?;
|
2022-09-29 07:34:55 +00:00
|
|
|
|
|
|
|
log::info!("service stoped");
|
2022-09-24 18:11:12 +00:00
|
|
|
Ok(())
|
|
|
|
}
|