scmanager_windows_rs/
service_manager.rs

1//! This module provides functionalities for managing Windows services.
2//! It includes structs for service configuration and a `ServiceManager` for creating, retrieving, and managing services.
3
4use widestring::U16CString;
5use windows_sys::Win32::{
6    Security::SC_HANDLE,
7    System::Services::{
8        CloseServiceHandle, CreateServiceW, OpenSCManagerW, OpenServiceW, SC_MANAGER_ALL_ACCESS,
9        SERVICE_ALL_ACCESS,
10    },
11};
12
13use crate::{
14    common::{get_last_error, set_privilege},
15    error::{CreateServiceError, OpenServiceError, ServiceManagerError},
16    service::{ServiceErrorControl, ServiceHandle, ServiceStartType, ServiceType},
17};
18
19/// Configuration for a Windows service.
20#[derive(Default, Clone, Debug)]
21pub struct ServiceConfig {
22    pub service_name: String,
23    pub display_name: String,
24    pub binary_path: String,
25    pub service_type: ServiceType,
26    pub start_type: ServiceStartType,
27    pub error_control: ServiceErrorControl,
28}
29
30/// Manages Windows services, providing functionalities to create, retrieve, and control services.
31pub struct ServiceManager {
32    handle: Option<SC_HANDLE>,
33}
34
35impl Drop for ServiceManager {
36    fn drop(&mut self) {
37        if let Some(handle) = self.handle {
38            unsafe {
39                CloseServiceHandle(handle);
40            }
41        }
42    }
43}
44/// # Examples
45///
46/// ```rust
47/// let service_manager = ServiceManager::new()?;
48
49/// let service_name = "test";
50/// let service_path = r"C:\Windows\system32\test.sys";
51
52/// let service_handle = service_manager.create_or_get(ServiceConfig {
53///     service_name: service_name.to_string(),
54///     display_name: service_name.to_string(),
55///     binary_path: "invalid path test".to_string(),
56///     start_type: ServiceStartType::DemandStart,
57///     service_type: ServiceType::KernelDriver,
58///     ..Default::default()
59/// })?;
60
61/// assert_eq!(
62///     service_handle.get_start_type()?,
63///     ServiceStartType::DemandStart
64/// );
65
66/// service_handle.update_config(ServiceConfig {
67///     display_name: service_name.to_string(),
68///     binary_path: service_path.to_string(),
69///     service_type: ServiceType::KernelDriver,
70///     ..Default::default()
71/// })?;
72/// service_handle.start_blocking()?;
73
74/// assert_eq!(service_handle.state()?, ServiceState::Running);
75
76/// service_handle.stop_blocking()?;
77
78/// assert_eq!(service_handle.state()?, ServiceState::Stopped);
79
80/// std::thread::sleep(std::time::Duration::from_secs(2));
81
82/// service_handle.delete()?;
83/// ```
84impl ServiceManager {
85    /// Creates a new `ServiceManager` with access to the service control manager.
86    ///
87    /// # Errors
88    ///
89    /// This function will return an error if it can't open the service control manager.
90    pub fn new() -> Result<Self, ServiceManagerError> {
91        let handle =
92            unsafe { OpenSCManagerW(std::ptr::null(), std::ptr::null(), SC_MANAGER_ALL_ACCESS) };
93
94        if handle == 0 {
95            return Err(ServiceManagerError::from((
96                get_last_error(),
97                "[ServiceManager::new] handle == 0".to_string(),
98            )));
99        }
100
101        set_privilege("SeLoadDriverPrivilege".to_string()).map_err(|_| {
102            ServiceManagerError::AccessDenied(
103                get_last_error(),
104                "[ServiceManager::set_privilege] failed".to_string(),
105            )
106        })?;
107
108        Ok(Self {
109            handle: Some(handle),
110        })
111    }
112
113    /// Creates a new service with the specified configuration.
114    ///
115    /// # Errors
116    ///
117    /// This function will return an error if it can't create the service.
118    pub fn create_service(
119        &self,
120        options: ServiceConfig,
121    ) -> Result<ServiceHandle, CreateServiceError> {
122        let scm_handle = self.handle.ok_or(CreateServiceError::InvalidHandle(
123            get_last_error(),
124            "[create_service] invalid service manager handle".to_string(),
125        ))?;
126
127        let service_name = U16CString::from_str(options.service_name.clone()).map_err(|_| {
128            CreateServiceError::InvalidName(0, "[create_service] invalid service name".to_string())
129        })?;
130        let display_name = U16CString::from_str(options.display_name.clone()).map_err(|_| {
131            CreateServiceError::InvalidName(0, "[create_service] invalid display name".to_string())
132        })?;
133        let binary_path = U16CString::from_str(options.binary_path.clone()).map_err(|_| {
134            CreateServiceError::InvalidParameter(
135                0,
136                "[create_service] invalid binary path".to_string(),
137            )
138        })?;
139
140        let handle = unsafe {
141            CreateServiceW(
142                scm_handle,
143                service_name.as_ptr(),
144                display_name.as_ptr(),
145                SERVICE_ALL_ACCESS,
146                options.service_type as u32,
147                options.start_type as u32,
148                options.error_control as u32,
149                binary_path.as_ptr(),
150                std::ptr::null(),
151                std::ptr::null_mut(),
152                std::ptr::null(),
153                std::ptr::null(),
154                std::ptr::null(),
155            )
156        };
157
158        if handle == 0 {
159            return Err(CreateServiceError::from((
160                get_last_error(),
161                "[create_service] handle == 0".to_string(),
162            )));
163        }
164
165        Ok(ServiceHandle::new(handle))
166    }
167
168    /// Retrieves an existing service by name.
169    ///
170    /// # Errors
171    ///
172    /// This function will return an error if it can't retrieve the service.
173    pub fn get_service(&self, service_name: String) -> Result<ServiceHandle, OpenServiceError> {
174        let scm_handle = self.handle.ok_or(OpenServiceError::InvalidHandle(
175            get_last_error(),
176            "[get_service] invalid service manager handle".to_string(),
177        ))?;
178
179        let service_name = U16CString::from_str(service_name)
180            .map_err(|_| OpenServiceError::InvalidName(0, "invalid service named".to_string()))?;
181        let handle = unsafe { OpenServiceW(scm_handle, service_name.as_ptr(), SERVICE_ALL_ACCESS) };
182
183        if handle == 0 {
184            return Err(OpenServiceError::from((
185                get_last_error(),
186                "[get_service] handle == 0".to_string(),
187            )));
188        }
189
190        Ok(ServiceHandle::new(handle))
191    }
192
193    /// Creates a new service if it doesn't exist, otherwise retrieves the existing service.
194    ///
195    /// # Errors
196    ///
197    /// This function will return an error if it can't create or retrieve the service.
198    pub fn create_or_get(
199        &self,
200        options: ServiceConfig,
201    ) -> Result<ServiceHandle, CreateServiceError> {
202        if let Ok(service_handle) = self.get_service(options.service_name.clone()) {
203            return Ok(service_handle);
204        }
205
206        self.create_service(options)
207    }
208}