init
This commit is contained in:
		
						commit
						5e65b44adf
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					/target
 | 
				
			||||||
							
								
								
									
										1031
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1031
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										15
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "windows-service-test"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					tracing = "0.1"
 | 
				
			||||||
 | 
					tracing-subscriber = "0.3"
 | 
				
			||||||
 | 
					tracing-appender = "0.2"
 | 
				
			||||||
 | 
					windows-service = "0.5"
 | 
				
			||||||
 | 
					tokio = { version = "1", features = ["full"] }
 | 
				
			||||||
 | 
					error-stack = "0.2"
 | 
				
			||||||
 | 
					axum = "0.6"
 | 
				
			||||||
							
								
								
									
										31
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					use tracing::error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::main]
 | 
				
			||||||
 | 
					async fn main() {
 | 
				
			||||||
 | 
					    let file_appender = tracing_appender::rolling::hourly(
 | 
				
			||||||
 | 
					        &std::env::current_exe()
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					            .parent()
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					            .display()
 | 
				
			||||||
 | 
					            .to_string(),
 | 
				
			||||||
 | 
					        "prefix.log",
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tracing_subscriber::fmt()
 | 
				
			||||||
 | 
					        .with_max_level(tracing::Level::DEBUG)
 | 
				
			||||||
 | 
					        .with_target(false)
 | 
				
			||||||
 | 
					        .with_writer(non_blocking)
 | 
				
			||||||
 | 
					        // .with_timer(timer)
 | 
				
			||||||
 | 
					        .pretty()
 | 
				
			||||||
 | 
					        .init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if let Err(e) = service::run_service().await {
 | 
				
			||||||
 | 
					        error!("{e:?}");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    println!("end of running");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/service/execute.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/service/execute.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					use axum::{routing::get, Router};
 | 
				
			||||||
 | 
					use error_stack::Report;
 | 
				
			||||||
 | 
					use tokio::sync::mpsc::Receiver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::ServiceError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub(super) async fn execute(mut stop_signal: Receiver<()>) -> Result<(), Report<ServiceError>> {
 | 
				
			||||||
 | 
					    // build our application with a single route
 | 
				
			||||||
 | 
					    let app = Router::new().route("/", get(|| async { "Hello, World!" }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // run it with hyper on localhost:3000
 | 
				
			||||||
 | 
					    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
 | 
				
			||||||
 | 
					        .serve(app.into_make_service())
 | 
				
			||||||
 | 
					        .with_graceful_shutdown(async {
 | 
				
			||||||
 | 
					            stop_signal.recv().await;
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										125
									
								
								src/service/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/service/mod.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,125 @@
 | 
				
			|||||||
 | 
					mod execute;
 | 
				
			||||||
 | 
					#[cfg(windows)]
 | 
				
			||||||
 | 
					mod windows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(windows)]
 | 
				
			||||||
 | 
					use windows::service_main;
 | 
				
			||||||
 | 
					#[cfg(windows)]
 | 
				
			||||||
 | 
					use windows_service::define_windows_service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use error_stack::{Context, Report};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::service::execute::execute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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).
 | 
				
			||||||
 | 
					#[cfg(windows)]
 | 
				
			||||||
 | 
					define_windows_service!(ffi_service_main, service_main);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub enum ServiceError {
 | 
				
			||||||
 | 
					    Starting,
 | 
				
			||||||
 | 
					    Installing,
 | 
				
			||||||
 | 
					    Uninstalling,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					impl fmt::Display for ServiceError {
 | 
				
			||||||
 | 
					    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        fmt.write_str(&match self {
 | 
				
			||||||
 | 
					            Self::Starting => format!("Could not start {} service", env!("CARGO_PKG_NAME")),
 | 
				
			||||||
 | 
					            Self::Installing => format!(
 | 
				
			||||||
 | 
					                "Error on installing {} as a windows service",
 | 
				
			||||||
 | 
					                env!("CARGO_PKG_NAME")
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Self::Uninstalling => format!(
 | 
				
			||||||
 | 
					                "Error on installing {} as a windows service",
 | 
				
			||||||
 | 
					                env!("CARGO_PKG_NAME")
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					impl Context for ServiceError {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(not(windows))]
 | 
				
			||||||
 | 
					pub async fn run_service() -> Result<(), Report<ServiceStartingError>> {
 | 
				
			||||||
 | 
					    // Create a channel to be able to poll a stop event from the service worker loop.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use std::sync::mpsc;
 | 
				
			||||||
 | 
					    let (_shutdown_tx, shutdown_rx) = mpsc::channel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // run service - blocking thread
 | 
				
			||||||
 | 
					    execute(shutdown_rx).await
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(windows)]
 | 
				
			||||||
 | 
					pub async fn run_service() -> Result<(), Report<ServiceError>> {
 | 
				
			||||||
 | 
					    // Get command line parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use error_stack::{IntoReport, ResultExt};
 | 
				
			||||||
 | 
					    use windows_service::service_dispatcher;
 | 
				
			||||||
 | 
					    let args: Vec<String> = std::env::args().collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let usage_output = format!(
 | 
				
			||||||
 | 
					        "
 | 
				
			||||||
 | 
					DaRL - Data and Runtime Logger\n
 | 
				
			||||||
 | 
					Usage: {}.exe service [OPTIONS]\n
 | 
				
			||||||
 | 
					OPTIONS:
 | 
				
			||||||
 | 
					install   \tCreate windows service
 | 
				
			||||||
 | 
					uninstall \tDelete windows service
 | 
				
			||||||
 | 
					",
 | 
				
			||||||
 | 
					        env!("CARGO_PKG_NAME")
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    match args.get(1).map(|s| s.as_str()) {
 | 
				
			||||||
 | 
					        Some("service") => match args.get(2).map(|s| s.as_str()) {
 | 
				
			||||||
 | 
					            Some("install") => match windows::install() {
 | 
				
			||||||
 | 
					                Ok(_) => println!("Successfully installed as service"),
 | 
				
			||||||
 | 
					                Err(e) => println!("Error installing service: {e:?}"),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Some("uninstall") => match windows::uninstall() {
 | 
				
			||||||
 | 
					                Ok(_) => println!("Successfully uninstalled"),
 | 
				
			||||||
 | 
					                Err(e) => println!("Error uninstalling service: {e:?}"),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Some(_) => println!("{usage_output}"),
 | 
				
			||||||
 | 
					            None => println!("{usage_output}"),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        Some("--serviced") => {
 | 
				
			||||||
 | 
					            // Register generated `ffi_service_main` with the system and start the service, blocking
 | 
				
			||||||
 | 
					            // this thread until the service is stopped.
 | 
				
			||||||
 | 
					            service_dispatcher::start(env!("CARGO_PKG_NAME"), ffi_service_main)
 | 
				
			||||||
 | 
					                .into_report()
 | 
				
			||||||
 | 
					                .change_context(ServiceError::Starting)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Some(_) => println!("{usage_output}"),
 | 
				
			||||||
 | 
					        None => {
 | 
				
			||||||
 | 
					            use tokio::sync::mpsc;
 | 
				
			||||||
 | 
					            let (_shutdown_tx, shutdown_rx) = mpsc::channel(1);
 | 
				
			||||||
 | 
					            execute(shutdown_rx).await?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod print_tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn print() {
 | 
				
			||||||
 | 
					        let usage_output = format!(
 | 
				
			||||||
 | 
					            "
 | 
				
			||||||
 | 
					DaRL - Data and Runtime Logger\n
 | 
				
			||||||
 | 
					Usage: {}.exe service [OPTIONS]\n
 | 
				
			||||||
 | 
					OPTIONS:
 | 
				
			||||||
 | 
					  install   \tCreate windows service
 | 
				
			||||||
 | 
					  uninstall \tDelete windows service
 | 
				
			||||||
 | 
					",
 | 
				
			||||||
 | 
					            env!("CARGO_PKG_NAME")
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        println!("{usage_output}");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								src/service/windows/install.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/service/windows/install.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					use std::ffi::OsString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use error_stack::{IntoReport, Report, ResultExt};
 | 
				
			||||||
 | 
					use windows_service::{
 | 
				
			||||||
 | 
					    service::{ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceType},
 | 
				
			||||||
 | 
					    service_manager::{ServiceManager, ServiceManagerAccess},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::service::ServiceError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn install() -> Result<(), Report<ServiceError>> {
 | 
				
			||||||
 | 
					    let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
 | 
				
			||||||
 | 
					    let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Installing)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This example installs the service defined in `examples/ping_service.rs`.
 | 
				
			||||||
 | 
					    // In the real world code you would set the executable path to point to your own binary
 | 
				
			||||||
 | 
					    // that implements windows service.
 | 
				
			||||||
 | 
					    let service_binary_path = ::std::env::current_exe()
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Installing)?
 | 
				
			||||||
 | 
					        .with_file_name(format!("{}.exe", env!("CARGO_PKG_NAME")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let service_info = ServiceInfo {
 | 
				
			||||||
 | 
					        name: OsString::from(env!("CARGO_PKG_NAME")),
 | 
				
			||||||
 | 
					        display_name: OsString::from("DaRL"),
 | 
				
			||||||
 | 
					        service_type: ServiceType::OWN_PROCESS,
 | 
				
			||||||
 | 
					        start_type: ServiceStartType::AutoStart,
 | 
				
			||||||
 | 
					        error_control: ServiceErrorControl::Normal,
 | 
				
			||||||
 | 
					        executable_path: service_binary_path,
 | 
				
			||||||
 | 
					        launch_arguments: vec!["--serviced".into()],
 | 
				
			||||||
 | 
					        dependencies: vec![],
 | 
				
			||||||
 | 
					        account_name: None, // run as System
 | 
				
			||||||
 | 
					        account_password: None,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let service = service_manager
 | 
				
			||||||
 | 
					        .create_service(&service_info, ServiceAccess::CHANGE_CONFIG)
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Installing)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    service
 | 
				
			||||||
 | 
					        .set_description("DaRL - Data and Runtime Logger service")
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Installing)?;
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										97
									
								
								src/service/windows/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/service/windows/mod.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					mod install;
 | 
				
			||||||
 | 
					mod uninstall;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::{ffi::OsString, time::Duration};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use error_stack::{IntoReport, Report, ResultExt};
 | 
				
			||||||
 | 
					pub(super) use install::install;
 | 
				
			||||||
 | 
					use tracing::error;
 | 
				
			||||||
 | 
					pub(super) use uninstall::uninstall;
 | 
				
			||||||
 | 
					use windows_service::{
 | 
				
			||||||
 | 
					    service::{
 | 
				
			||||||
 | 
					        ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
 | 
				
			||||||
 | 
					        ServiceType,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    service_control_handler::{self, ServiceControlHandlerResult},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::{execute, ServiceError};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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 service_main(_arguments: Vec<OsString>) {
 | 
				
			||||||
 | 
					    if let Err(_e) = run_service() {
 | 
				
			||||||
 | 
					        // Handle the error, by logging or something.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn run_service() -> Result<(), Report<ServiceError>> {
 | 
				
			||||||
 | 
					    // Create a channel to be able to poll a stop event from the service worker loop.
 | 
				
			||||||
 | 
					    let (shutdown_tx, shutdown_rx) = tokio::sync::mpsc::channel(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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 => {
 | 
				
			||||||
 | 
					                shutdown_tx.try_send(()).unwrap();
 | 
				
			||||||
 | 
					                ServiceControlHandlerResult::NoError
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _ => ServiceControlHandlerResult::NotImplemented,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Register system service event handler.
 | 
				
			||||||
 | 
					    // The returned status handle should be used to report service status changes to the system.
 | 
				
			||||||
 | 
					    let status_handle = service_control_handler::register(env!("CARGO_PKG_NAME"), event_handler)
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Starting)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Tell the system that service is running
 | 
				
			||||||
 | 
					    status_handle
 | 
				
			||||||
 | 
					        .set_service_status(ServiceStatus {
 | 
				
			||||||
 | 
					            service_type: ServiceType::OWN_PROCESS,
 | 
				
			||||||
 | 
					            current_state: ServiceState::Running,
 | 
				
			||||||
 | 
					            controls_accepted: ServiceControlAccept::STOP,
 | 
				
			||||||
 | 
					            exit_code: ServiceExitCode::Win32(0),
 | 
				
			||||||
 | 
					            checkpoint: 0,
 | 
				
			||||||
 | 
					            wait_hint: Duration::default(),
 | 
				
			||||||
 | 
					            process_id: None,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Starting)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // run service - blocking thread
 | 
				
			||||||
 | 
					    let rt = tokio::runtime::Builder::new_current_thread()
 | 
				
			||||||
 | 
					        .enable_all()
 | 
				
			||||||
 | 
					        .build()
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rt.block_on(async move {
 | 
				
			||||||
 | 
					        if let Err(e) = execute(shutdown_rx).await {
 | 
				
			||||||
 | 
					            error!("{e:?}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Tell the system that service has stopped.
 | 
				
			||||||
 | 
					    status_handle
 | 
				
			||||||
 | 
					        .set_service_status(ServiceStatus {
 | 
				
			||||||
 | 
					            service_type: ServiceType::OWN_PROCESS,
 | 
				
			||||||
 | 
					            current_state: ServiceState::Stopped,
 | 
				
			||||||
 | 
					            controls_accepted: ServiceControlAccept::empty(),
 | 
				
			||||||
 | 
					            exit_code: ServiceExitCode::Win32(0),
 | 
				
			||||||
 | 
					            checkpoint: 0,
 | 
				
			||||||
 | 
					            wait_hint: Duration::default(),
 | 
				
			||||||
 | 
					            process_id: None,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Starting)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/service/windows/uninstall.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/service/windows/uninstall.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					use std::{thread, time::Duration};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use error_stack::{IntoReport, Report, ResultExt};
 | 
				
			||||||
 | 
					use windows_service::{
 | 
				
			||||||
 | 
					    service::{ServiceAccess, ServiceState},
 | 
				
			||||||
 | 
					    service_manager::{ServiceManager, ServiceManagerAccess},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::service::ServiceError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn uninstall() -> Result<(), Report<ServiceError>> {
 | 
				
			||||||
 | 
					    let manager_access = ServiceManagerAccess::CONNECT;
 | 
				
			||||||
 | 
					    let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Uninstalling)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
 | 
				
			||||||
 | 
					    let service = service_manager
 | 
				
			||||||
 | 
					        .open_service(env!("CARGO_PKG_NAME"), service_access)
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Uninstalling)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let service_status = service
 | 
				
			||||||
 | 
					        .query_status()
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Uninstalling)?;
 | 
				
			||||||
 | 
					    if service_status.current_state != ServiceState::Stopped {
 | 
				
			||||||
 | 
					        service
 | 
				
			||||||
 | 
					            .stop()
 | 
				
			||||||
 | 
					            .into_report()
 | 
				
			||||||
 | 
					            .change_context(ServiceError::Uninstalling)?;
 | 
				
			||||||
 | 
					        // Wait for service to stop
 | 
				
			||||||
 | 
					        thread::sleep(Duration::from_secs(1));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    service
 | 
				
			||||||
 | 
					        .delete()
 | 
				
			||||||
 | 
					        .into_report()
 | 
				
			||||||
 | 
					        .change_context(ServiceError::Uninstalling)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user