scmanager_windows_rs/
service.rs

1//! This module provides functionalities for controlling Windows services.
2//! It includes structs and enums for handling service configurations and states,
3//! and functions for starting, stopping, pausing, and querying services.
4
5use std::fmt::Display;
6use widestring::U16CString;
7use windows_sys::Win32::{
8    Foundation::{ERROR_INSUFFICIENT_BUFFER, FALSE},
9    Security::SC_HANDLE,
10    System::Services::{
11        ChangeServiceConfigW, CloseServiceHandle, ControlService, DeleteService,
12        QueryServiceConfigW, QueryServiceStatus, StartServiceW, QUERY_SERVICE_CONFIGW,
13        SERVICE_ADAPTER, SERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_CONTINUE_PENDING,
14        SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_STOP, SERVICE_DEMAND_START, SERVICE_DISABLED,
15        SERVICE_FILE_SYSTEM_DRIVER, SERVICE_KERNEL_DRIVER, SERVICE_PAUSED, SERVICE_PAUSE_PENDING,
16        SERVICE_RECOGNIZER_DRIVER, SERVICE_RUNNING, SERVICE_START_PENDING, SERVICE_STATUS,
17        SERVICE_STOPPED, SERVICE_STOP_PENDING, SERVICE_SYSTEM_START, SERVICE_WIN32_OWN_PROCESS,
18        SERVICE_WIN32_SHARE_PROCESS,
19    },
20};
21
22use crate::{
23    common::get_last_error,
24    error::{ControlServiceError, DeleteServiceError, QueryServiceError, UpdateServiceError},
25    service_manager::ServiceConfig,
26};
27
28/// Represents a handle to a Windows service.
29#[derive(Default, Debug)]
30pub struct ServiceHandle {
31    handle: Option<SC_HANDLE>,
32}
33
34/// Defines the error control levels for a Windows service.
35#[repr(u32)]
36#[derive(Default, Clone, Copy, Debug, PartialEq)]
37pub enum ServiceErrorControl {
38    ErrorCritical = 0x00000003,
39    ErrorIgnore = 0x00000000,
40    #[default]
41    ErrorNormal = 0x00000001,
42    ErrorSevere = 0x00000002,
43}
44
45/// Defines the start types for a Windows service.
46#[repr(u32)]
47#[derive(Default, Clone, Copy, Debug, PartialEq)]
48pub enum ServiceStartType {
49    AutoStart = 0x00000002,
50    BootStart = 0x00000000,
51    #[default]
52    DemandStart = 0x00000003,
53    Disabled = 0x00000004,
54    SystemStart = 0x00000001,
55}
56
57/// Defines the types of a Windows service.
58#[repr(u32)]
59#[derive(Default, Clone, Copy, Debug, PartialEq)]
60pub enum ServiceType {
61    Adapter = 0x00000004,
62    FileSystemDriver = 0x00000002,
63    #[default]
64    KernelDriver = 0x00000001,
65    RecognizerDriver = 0x00000008,
66    Win32OwnProcess = 0x00000010,
67    Win32ShareProcess = 0x00000020,
68}
69
70/// Represents the possible states of a Windows service.
71#[repr(u32)]
72#[derive(Debug, Clone, PartialEq)]
73pub enum ServiceState {
74    Stopped = 1,
75    StartPending,
76    StopPending,
77    Running,
78    ContinuePending,
79    PausePending,
80    Paused,
81}
82
83impl Display for ServiceType {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        match *self {
86            Self::Adapter => write!(f, "Adapter"),
87            Self::FileSystemDriver => write!(f, "FileSystemDriver"),
88            Self::KernelDriver => write!(f, "KernelDriver"),
89            Self::RecognizerDriver => write!(f, "RecognizerDriver"),
90            Self::Win32OwnProcess => write!(f, "Win32OwnProcess"),
91            Self::Win32ShareProcess => write!(f, "Win32ShareProcess"),
92        }
93    }
94}
95
96impl Display for ServiceState {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        match *self {
99            Self::Stopped => write!(f, "Stopped"),
100            Self::StartPending => write!(f, "StartPending"),
101            Self::StopPending => write!(f, "StopPending"),
102            Self::Running => write!(f, "Running"),
103            Self::ContinuePending => write!(f, "ContinuePending"),
104            Self::PausePending => write!(f, "PausePending"),
105            Self::Paused => write!(f, "Paused"),
106        }
107    }
108}
109
110impl Display for ServiceHandle {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        let _ = writeln!(f, "StartType: {:?}", self.get_start_type());
113        let _ = writeln!(f, "State: {:?}", self.state());
114        Ok(())
115    }
116}
117
118impl TryFrom<u32> for ServiceType {
119    type Error = QueryServiceError;
120
121    fn try_from(value: u32) -> Result<Self, Self::Error> {
122        match value {
123            SERVICE_ADAPTER => Ok(ServiceType::Adapter),
124            SERVICE_FILE_SYSTEM_DRIVER => Ok(ServiceType::FileSystemDriver),
125            SERVICE_KERNEL_DRIVER => Ok(ServiceType::KernelDriver),
126            SERVICE_RECOGNIZER_DRIVER => Ok(ServiceType::RecognizerDriver),
127            SERVICE_WIN32_OWN_PROCESS => Ok(ServiceType::Win32OwnProcess),
128            SERVICE_WIN32_SHARE_PROCESS => Ok(ServiceType::Win32ShareProcess),
129            _ => Err(QueryServiceError::from((
130                0,
131                "invalid service type".to_string(),
132            ))),
133        }
134    }
135}
136
137impl TryFrom<u32> for ServiceStartType {
138    type Error = QueryServiceError;
139
140    fn try_from(value: u32) -> Result<Self, Self::Error> {
141        match value {
142            SERVICE_AUTO_START => Ok(Self::AutoStart),
143            SERVICE_BOOT_START => Ok(Self::BootStart),
144            SERVICE_DEMAND_START => Ok(Self::DemandStart),
145            SERVICE_DISABLED => Ok(Self::Disabled),
146            SERVICE_SYSTEM_START => Ok(Self::SystemStart),
147            _ => Err(QueryServiceError::from((
148                0,
149                "invalid service start type".to_string(),
150            ))),
151        }
152    }
153}
154
155impl TryFrom<u32> for ServiceState {
156    type Error = QueryServiceError;
157
158    fn try_from(value: u32) -> Result<Self, Self::Error> {
159        match value {
160            SERVICE_STOPPED => Ok(ServiceState::Stopped),
161            SERVICE_START_PENDING => Ok(ServiceState::StartPending),
162            SERVICE_STOP_PENDING => Ok(ServiceState::StopPending),
163            SERVICE_RUNNING => Ok(ServiceState::Running),
164            SERVICE_CONTINUE_PENDING => Ok(ServiceState::ContinuePending),
165            SERVICE_PAUSE_PENDING => Ok(ServiceState::PausePending),
166            SERVICE_PAUSED => Ok(ServiceState::Paused),
167            _ => Err(QueryServiceError::from((
168                0,
169                "invalid service state".to_string(),
170            ))),
171        }
172    }
173}
174
175impl Drop for ServiceHandle {
176    fn drop(&mut self) {
177        if let Some(handle) = self.handle {
178            unsafe {
179                CloseServiceHandle(handle);
180            }
181        }
182    }
183}
184
185impl ServiceHandle {
186    /// Creates a new `ServiceHandle`.
187    pub fn new(handle: SC_HANDLE) -> Self {
188        Self {
189            handle: Some(handle),
190        }
191    }
192
193    /// Returns the current state of this `ServiceHandle`.
194    ///
195    /// # Errors
196    ///
197    /// This function will return an error if it can't retrieve the service status.
198    pub fn state(&self) -> Result<ServiceState, QueryServiceError> {
199        let status = self.get_status()?;
200
201        ServiceState::try_from(status.dwCurrentState)
202    }
203
204    /// Updates the configuration of the service.
205    ///
206    /// # Errors
207    ///
208    /// This function will return an error if it can't update the service configuration.
209    pub fn update_config(&self, options: ServiceConfig) -> Result<(), UpdateServiceError> {
210        let handle = self.handle.ok_or(UpdateServiceError::InvalidHandle(
211            0,
212            "[update_config] invalid service handle".to_string(),
213        ))?;
214
215        let display_name = U16CString::from_str(options.display_name.clone()).map_err(|_| {
216            UpdateServiceError::InvalidParameter(
217                0,
218                "[update_config] invalid display_name".to_string(),
219            )
220        })?;
221        let binary_path = U16CString::from_str(options.binary_path).map_err(|_| {
222            UpdateServiceError::InvalidParameter(
223                0,
224                "[update_config] invalid binary_path".to_string(),
225            )
226        })?;
227        unsafe {
228            if ChangeServiceConfigW(
229                handle,
230                options.service_type as u32,
231                options.start_type as u32,
232                options.error_control as u32,
233                binary_path.as_ptr(),
234                std::ptr::null(),
235                std::ptr::null_mut(),
236                std::ptr::null(),
237                std::ptr::null(),
238                std::ptr::null(),
239                display_name.as_ptr(),
240            ) == FALSE
241            {
242                return Err(UpdateServiceError::from((
243                    get_last_error(),
244                    "[update_config] failed".to_string(),
245                )));
246            }
247        }
248        Ok(())
249    }
250
251    /// Returns the get start type of this [`ServiceHandle`].
252    ///
253    /// # Errors
254    ///
255    /// This function will return an error if it can't get the start type.
256    pub fn get_start_type(&self) -> Result<ServiceStartType, QueryServiceError> {
257        let config = self.get_config()?;
258
259        ServiceStartType::try_from(config.dwStartType)
260    }
261
262    /// Sets the start type of the service.
263    ///
264    /// # Errors
265    ///
266    /// This function will return an error if it can't set the start type.
267    pub fn set_start_type(&self, start_type: ServiceStartType) -> Result<(), UpdateServiceError> {
268        let handle = self.handle.ok_or(UpdateServiceError::InvalidHandle(
269            0,
270            "[set_start_type] invalid service handle".to_string(),
271        ))?;
272        let config = self.get_config().map_err(|_| UpdateServiceError::AccessDenied(get_last_error(), "[set_start_type] failed to get service config".to_string()))?;
273
274        unsafe {
275            let mut tag_id = config.dwTagId;
276            if ChangeServiceConfigW(
277                handle,
278                config.dwServiceType,
279                start_type as u32,
280                config.dwErrorControl,
281                config.lpBinaryPathName,
282                config.lpLoadOrderGroup,
283                &mut tag_id,
284                config.lpDependencies,
285                config.lpServiceStartName,
286                std::ptr::null(),
287                config.lpDisplayName,
288            ) == FALSE
289            {
290                return Err(UpdateServiceError::from((
291                    get_last_error(),
292                    "[set_start_type] ChangeServiceConfig failed".to_string(),
293                )));
294            }
295        }
296        Ok(())
297    }
298    pub fn delete(&self) -> Result<(), DeleteServiceError> {
299        let handle = self.handle.ok_or(DeleteServiceError::InvalidHandle(
300            0,
301            "[delete] invalid service handle".to_string(),
302        ))?;
303        unsafe {
304            if DeleteService(handle) == FALSE {
305                return Err(DeleteServiceError::from((
306                    get_last_error(),
307                    "[delete] DeleteService failed".to_string(),
308                )));
309            }
310        }
311        Ok(())
312    }
313
314    /// Starts the service and blocks until it is running.
315    ///
316    /// # Errors
317    ///
318    /// This function will return an error if it can't start the service.
319    pub fn start_blocking(&self) -> Result<(), ControlServiceError> {
320        self.control_blocking(ServiceState::Running, || self.start())
321    }
322
323    /// Stops the service and blocks until it is stopped.
324    ///
325    /// # Errors
326    ///
327    /// This function will return an error if it can't stop the service.
328    pub fn stop_blocking(&self) -> Result<(), ControlServiceError> {
329        self.control_blocking(ServiceState::Stopped, || self.stop())
330    }
331
332    /// Pauses the service and blocks until it is paused.
333    ///
334    /// # Errors
335    ///
336    /// This function will return an error if it can't pause the service.
337    pub fn pause_blocking(&self) -> Result<(), ControlServiceError> {
338        self.control_blocking(ServiceState::Paused, || self.pause())
339    }
340
341    /// Starts the service.
342    ///
343    /// # Errors
344    ///
345    /// This function will return an error if it can't start the service.
346    pub fn start(&self) -> Result<(), ControlServiceError> {
347        let handle = self.handle.ok_or(ControlServiceError::InvalidHandle(
348            0,
349            "[start] invalid service handle".to_string(),
350        ))?;
351        unsafe {
352            if StartServiceW(handle, 0, std::ptr::null()) == FALSE {
353                return Err(ControlServiceError::from((
354                    get_last_error(),
355                    "[start] StartServiceW failed".to_string(),
356                )));
357            }
358        }
359
360        Ok(())
361    }
362
363    /// Stops the service.
364    ///
365    /// # Errors
366    ///
367    /// This function will return an error if it can't stop the service.
368    pub fn stop(&self) -> Result<(), ControlServiceError> {
369        self.control(SERVICE_CONTROL_STOP)
370    }
371
372    /// Pauses the service.
373    ///
374    /// # Errors
375    ///
376    /// This function will return an error if it can't pause the service.
377    pub fn pause(&self) -> Result<(), ControlServiceError> {
378        self.control(SERVICE_CONTROL_PAUSE)
379    }
380    #[doc(hidden)]
381    fn control(&self, control: u32) -> Result<(), ControlServiceError> {
382        let handle = self.handle.ok_or(ControlServiceError::InvalidHandle(
383            0,
384            "[control] invalid service handle".to_string(),
385        ))?;
386
387        unsafe {
388            let mut service_status = std::mem::zeroed::<SERVICE_STATUS>();
389
390            if ControlService(handle, control, &mut service_status) == FALSE {
391                return Err(ControlServiceError::from((
392                    get_last_error(),
393                    "[control] ControlService failed".to_string(),
394                )));
395            }
396        }
397
398        Ok(())
399    }
400    #[doc(hidden)]
401    fn control_blocking<F>(
402        &self,
403        service_state: ServiceState,
404        control_fn: F,
405    ) -> Result<(), ControlServiceError>
406    where
407        F: Fn() -> Result<(), ControlServiceError>,
408    {
409        control_fn()?;
410
411        loop {
412            if let Ok(status) = self.state() {
413                if status == service_state {
414                    break;
415                }
416            } else {
417                return Err(ControlServiceError::Unknown(
418                    get_last_error(),
419                    "[control_blocking] failed to get service state".to_string(),
420                ));
421            }
422
423            std::thread::sleep(std::time::Duration::from_millis(100))
424        }
425
426        Ok(())
427    }
428    #[doc(hidden)]
429    fn get_config(&self) -> Result<QUERY_SERVICE_CONFIGW, QueryServiceError> {
430        let handle = self.handle.ok_or(QueryServiceError::InvalidHandle(
431            0,
432            "[get_config] invalid service handle".to_string(),
433        ))?;
434        unsafe {
435            let mut bytes_needed: u32 = 0;
436
437            if QueryServiceConfigW(handle, std::ptr::null_mut(), 0, &mut bytes_needed) == FALSE
438                && get_last_error() != ERROR_INSUFFICIENT_BUFFER
439            {
440                return Err(QueryServiceError::from((
441                    get_last_error(),
442                    "[get_config] QueryServiceConfig failed".to_string(),
443                )));
444            }
445
446            let config_buffer = vec![0u8; bytes_needed as usize];
447            let config = config_buffer.as_ptr() as *mut QUERY_SERVICE_CONFIGW;
448
449            if QueryServiceConfigW(
450                handle,
451                config,
452                config_buffer.len() as u32,
453                &mut bytes_needed,
454            ) == FALSE
455            {
456                return Err(QueryServiceError::from((
457                    get_last_error(),
458                    "[get_config] QueryServiceConfig failed".to_string(),
459                )));
460            }
461            Ok(*config)
462        }
463    }
464    #[doc(hidden)]
465    fn get_status(&self) -> Result<SERVICE_STATUS, QueryServiceError> {
466        let handle = self.handle.ok_or(QueryServiceError::InvalidHandle(
467            0,
468            "[get_status] invalid service handle".to_string(),
469        ))?;
470        unsafe {
471            let mut status = std::mem::zeroed::<SERVICE_STATUS>();
472            if QueryServiceStatus(handle, &mut status) == FALSE {
473                return Err(QueryServiceError::from((
474                    get_last_error(),
475                    "[get_status] QueryServiceConfig failed".to_string(),
476                )));
477            }
478            Ok(status)
479        }
480    }
481}