windows_service/
service.rs

1use std::borrow::Cow;
2use std::ffi::{OsStr, OsString};
3use std::os::raw::c_void;
4use std::os::windows::ffi::{OsStrExt, OsStringExt};
5use std::path::PathBuf;
6use std::ptr;
7use std::time::Duration;
8use std::{io, mem};
9
10use widestring::{error::ContainsNul, WideCStr, WideCString, WideString};
11use windows_sys::{
12    core::GUID,
13    Win32::{
14        Foundation::{ERROR_SERVICE_SPECIFIC_ERROR, NO_ERROR},
15        Storage::FileSystem,
16        System::{Power, RemoteDesktop, Services, SystemServices, Threading::INFINITE},
17        UI::WindowsAndMessaging,
18    },
19};
20
21use crate::sc_handle::ScHandle;
22use crate::shell_escape;
23use crate::{double_nul_terminated, Error};
24
25bitflags::bitflags! {
26    /// Enum describing the types of Windows services.
27    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
28    pub struct ServiceType: u32 {
29        /// File system driver service.
30        const FILE_SYSTEM_DRIVER = Services::SERVICE_FILE_SYSTEM_DRIVER;
31
32        /// Driver service.
33        const KERNEL_DRIVER = Services::SERVICE_KERNEL_DRIVER;
34
35        /// Service that runs in its own process.
36        const OWN_PROCESS = Services::SERVICE_WIN32_OWN_PROCESS;
37
38        /// Service that shares a process with one or more other services.
39        const SHARE_PROCESS = Services::SERVICE_WIN32_SHARE_PROCESS;
40
41        /// The service runs in its own process under the logged-on user account.
42        const USER_OWN_PROCESS = Services::SERVICE_USER_OWN_PROCESS;
43
44        /// The service shares a process with one or more other services that run under the logged-on user account.
45        const USER_SHARE_PROCESS = Services::SERVICE_USER_SHARE_PROCESS;
46
47        /// The service can be interactive.
48        const INTERACTIVE_PROCESS = SystemServices::SERVICE_INTERACTIVE_PROCESS;
49    }
50}
51
52bitflags::bitflags! {
53    /// Flags describing the access permissions when working with services
54    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
55    pub struct ServiceAccess: u32 {
56        /// Full access to the service object
57        const ALL_ACCESS = Services::SERVICE_ALL_ACCESS;
58
59        /// Can query the service status
60        const QUERY_STATUS = Services::SERVICE_QUERY_STATUS;
61
62        /// Can start the service
63        const START = Services::SERVICE_START;
64
65        /// Can stop the service
66        const STOP = Services::SERVICE_STOP;
67
68        /// Can pause or continue the service execution
69        const PAUSE_CONTINUE = Services::SERVICE_PAUSE_CONTINUE;
70
71        /// Can ask the service to report its status
72        const INTERROGATE = Services::SERVICE_INTERROGATE;
73
74        /// Can query the services configuration
75        const QUERY_CONFIG = Services::SERVICE_QUERY_CONFIG;
76
77        /// Can change the services configuration
78        const CHANGE_CONFIG = Services::SERVICE_CHANGE_CONFIG;
79
80        /// Can use user-defined control codes
81        const USER_DEFINED_CONTROL = Services::SERVICE_USER_DEFINED_CONTROL;
82
83        /// Can delete the service
84        const DELETE = FileSystem::DELETE;
85
86        /// Required to call the `QueryServiceObjectSecurity` function to query the security descriptor of the service object
87        const READ_CONTROL = FileSystem::READ_CONTROL;
88
89        /// Required to call the `SetServiceObjectSecurity` function to modify the Dacl member of the service object's security descriptor
90        const WRITE_DAC = FileSystem::WRITE_DAC;
91
92        /// Required to call the `SetServiceObjectSecurity` function to modify the Owner and Group members of the service object's security descriptor
93        const WRITE_OWNER = FileSystem::WRITE_OWNER;
94    }
95}
96
97/// Enum describing the start options for windows services.
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
99#[repr(u32)]
100pub enum ServiceStartType {
101    /// Autostart on system startup
102    AutoStart = Services::SERVICE_AUTO_START,
103    /// Service is enabled, can be started manually
104    OnDemand = Services::SERVICE_DEMAND_START,
105    /// Disabled service
106    Disabled = Services::SERVICE_DISABLED,
107    /// Driver start on system startup.
108    /// This start type is only applicable to driver services.
109    SystemStart = Services::SERVICE_SYSTEM_START,
110    /// Driver start on OS boot.
111    /// This start type is only applicable to driver services.
112    BootStart = Services::SERVICE_BOOT_START,
113}
114
115impl ServiceStartType {
116    pub fn to_raw(&self) -> u32 {
117        *self as u32
118    }
119
120    pub fn from_raw(raw: u32) -> Result<ServiceStartType, ParseRawError> {
121        match raw {
122            x if x == ServiceStartType::AutoStart.to_raw() => Ok(ServiceStartType::AutoStart),
123            x if x == ServiceStartType::OnDemand.to_raw() => Ok(ServiceStartType::OnDemand),
124            x if x == ServiceStartType::Disabled.to_raw() => Ok(ServiceStartType::Disabled),
125            x if x == ServiceStartType::SystemStart.to_raw() => Ok(ServiceStartType::SystemStart),
126            x if x == ServiceStartType::BootStart.to_raw() => Ok(ServiceStartType::BootStart),
127            _ => Err(ParseRawError::InvalidInteger(raw)),
128        }
129    }
130}
131
132/// Error handling strategy for service failures.
133///
134/// See <https://msdn.microsoft.com/en-us/library/windows/desktop/ms682450(v=vs.85).aspx>
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
136#[repr(u32)]
137pub enum ServiceErrorControl {
138    Critical = Services::SERVICE_ERROR_CRITICAL,
139    Ignore = Services::SERVICE_ERROR_IGNORE,
140    Normal = Services::SERVICE_ERROR_NORMAL,
141    Severe = Services::SERVICE_ERROR_SEVERE,
142}
143
144impl ServiceErrorControl {
145    pub fn to_raw(&self) -> u32 {
146        *self as u32
147    }
148
149    pub fn from_raw(raw: u32) -> Result<ServiceErrorControl, ParseRawError> {
150        match raw {
151            x if x == ServiceErrorControl::Critical.to_raw() => Ok(ServiceErrorControl::Critical),
152            x if x == ServiceErrorControl::Ignore.to_raw() => Ok(ServiceErrorControl::Ignore),
153            x if x == ServiceErrorControl::Normal.to_raw() => Ok(ServiceErrorControl::Normal),
154            x if x == ServiceErrorControl::Severe.to_raw() => Ok(ServiceErrorControl::Severe),
155            _ => Err(ParseRawError::InvalidInteger(raw)),
156        }
157    }
158}
159
160/// Service dependency descriptor
161#[derive(Debug, Clone, PartialEq, Eq, Hash)]
162pub enum ServiceDependency {
163    Service(OsString),
164    Group(OsString),
165}
166
167impl ServiceDependency {
168    pub fn to_system_identifier(&self) -> OsString {
169        match *self {
170            ServiceDependency::Service(ref name) => name.to_owned(),
171            ServiceDependency::Group(ref name) => {
172                // since services and service groups share the same namespace the group identifiers
173                // should be prefixed with '+' (SC_GROUP_IDENTIFIER)
174                let mut group_identifier = OsString::new();
175                group_identifier.push("+");
176                group_identifier.push(name);
177                group_identifier
178            }
179        }
180    }
181
182    pub fn from_system_identifier(identifier: impl AsRef<OsStr>) -> Self {
183        let group_prefix: u16 = '+' as u16;
184        let mut iter = identifier.as_ref().encode_wide().peekable();
185
186        if iter.peek() == Some(&group_prefix) {
187            let chars: Vec<u16> = iter.skip(1).collect();
188            let group_name = OsString::from_wide(&chars);
189            ServiceDependency::Group(group_name)
190        } else {
191            let chars: Vec<u16> = iter.collect();
192            let service_name = OsString::from_wide(&chars);
193            ServiceDependency::Service(service_name)
194        }
195    }
196}
197
198/// Enum describing the types of actions that the service control manager can perform.
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
200#[repr(i32)]
201pub enum ServiceActionType {
202    None = Services::SC_ACTION_NONE,
203    Reboot = Services::SC_ACTION_REBOOT,
204    Restart = Services::SC_ACTION_RESTART,
205    RunCommand = Services::SC_ACTION_RUN_COMMAND,
206}
207
208impl ServiceActionType {
209    pub fn to_raw(&self) -> i32 {
210        *self as i32
211    }
212
213    pub fn from_raw(raw: i32) -> Result<ServiceActionType, ParseRawError> {
214        match raw {
215            x if x == ServiceActionType::None.to_raw() => Ok(ServiceActionType::None),
216            x if x == ServiceActionType::Reboot.to_raw() => Ok(ServiceActionType::Reboot),
217            x if x == ServiceActionType::Restart.to_raw() => Ok(ServiceActionType::Restart),
218            x if x == ServiceActionType::RunCommand.to_raw() => Ok(ServiceActionType::RunCommand),
219            _ => Err(ParseRawError::InvalidIntegerSigned(raw)),
220        }
221    }
222}
223
224/// Represents an action that the service control manager can perform.
225///
226/// See <https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-sc_action>
227#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct ServiceAction {
229    /// The action to be performed.
230    pub action_type: ServiceActionType,
231
232    /// The time to wait before performing the specified action
233    ///
234    /// # Panics
235    ///
236    /// Converting this to the FFI form will panic if the delay is too large to fit as milliseconds
237    /// in a `u32`.
238    pub delay: Duration,
239}
240
241impl ServiceAction {
242    pub fn from_raw(raw: Services::SC_ACTION) -> crate::Result<ServiceAction> {
243        Ok(ServiceAction {
244            action_type: ServiceActionType::from_raw(raw.Type)
245                .map_err(|e| Error::ParseValue("service action type", e))?,
246            delay: Duration::from_millis(raw.Delay as u64),
247        })
248    }
249
250    pub fn to_raw(&self) -> Services::SC_ACTION {
251        Services::SC_ACTION {
252            Type: self.action_type.to_raw(),
253            Delay: u32::try_from(self.delay.as_millis()).expect("Too long delay"),
254        }
255    }
256}
257
258/// A enum that represents the reset period for the failure counter.
259///
260/// # Panics
261///
262/// Converting this to the FFI form will panic if the period is too large to fit as seconds in a
263/// `u32`.
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
265pub enum ServiceFailureResetPeriod {
266    Never,
267    After(Duration),
268}
269
270impl ServiceFailureResetPeriod {
271    pub fn from_raw(raw: u32) -> ServiceFailureResetPeriod {
272        match raw {
273            INFINITE => ServiceFailureResetPeriod::Never,
274            _ => ServiceFailureResetPeriod::After(Duration::from_secs(raw as u64)),
275        }
276    }
277
278    pub fn to_raw(&self) -> u32 {
279        match self {
280            ServiceFailureResetPeriod::Never => INFINITE,
281            ServiceFailureResetPeriod::After(duration) => {
282                u32::try_from(duration.as_secs()).expect("Too long reset period")
283            }
284        }
285    }
286}
287
288/// A struct that describes the action that should be performed on the system service crash.
289///
290/// Please refer to MSDN for more info:\
291/// <https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-_service_failure_actionsw>
292#[derive(Debug, Clone, PartialEq, Eq, Hash)]
293pub struct ServiceFailureActions {
294    /// The time after which to reset the failure count to zero if there are no failures, in
295    /// seconds.
296    pub reset_period: ServiceFailureResetPeriod,
297
298    /// The message to be broadcast to server users before rebooting in response to the
299    /// `SC_ACTION_REBOOT` service controller action.
300    ///
301    /// If this value is `None`, the reboot message is unchanged.
302    /// If the value is an empty string, the reboot message is deleted and no message is broadcast.
303    pub reboot_msg: Option<OsString>,
304
305    /// The command line to execute in response to the `SC_ACTION_RUN_COMMAND` service controller
306    /// action. This process runs under the same account as the service.
307    ///
308    /// If this value is `None`, the command is unchanged. If the value is an empty string, the
309    /// command is deleted and no program is run when the service fails.
310    pub command: Option<OsString>,
311
312    /// The array of actions to perform.
313    /// If this value is `None`, the [`ServiceFailureActions::reset_period`] member is ignored.
314    pub actions: Option<Vec<ServiceAction>>,
315}
316
317impl ServiceFailureActions {
318    /// Tries to parse a `SERVICE_FAILURE_ACTIONSW` into Rust [`ServiceFailureActions`].
319    ///
320    /// # Errors
321    ///
322    /// Returns an error if any of the `SC_ACTION`s pointed to by `lpsaActions` does not
323    /// successfully convert into a [`ServiceAction`].
324    ///
325    /// # Safety
326    ///
327    /// The `SERVICE_FAILURE_ACTIONSW` fields `lpRebootMsg`, `lpCommand` must be either null
328    /// or proper null terminated wide C strings.
329    /// `lpsaActions` must be either null or an array with `cActions` number of of `SC_ACTION`s.
330    pub unsafe fn from_raw(
331        raw: Services::SERVICE_FAILURE_ACTIONSW,
332    ) -> crate::Result<ServiceFailureActions> {
333        let reboot_msg = ptr::NonNull::new(raw.lpRebootMsg)
334            .map(|wrapped_ptr| WideCStr::from_ptr_str(wrapped_ptr.as_ptr()).to_os_string());
335        let command = ptr::NonNull::new(raw.lpCommand)
336            .map(|wrapped_ptr| WideCStr::from_ptr_str(wrapped_ptr.as_ptr()).to_os_string());
337        let reset_period = ServiceFailureResetPeriod::from_raw(raw.dwResetPeriod);
338
339        let actions: Option<Vec<ServiceAction>> = if raw.lpsaActions.is_null() {
340            None
341        } else {
342            Some(
343                (0..raw.cActions)
344                    .map(|i| {
345                        let array_element_ptr: *mut Services::SC_ACTION =
346                            raw.lpsaActions.offset(i as isize);
347                        ServiceAction::from_raw(*array_element_ptr)
348                    })
349                    .collect::<crate::Result<Vec<ServiceAction>>>()?,
350            )
351        };
352
353        Ok(ServiceFailureActions {
354            reset_period,
355            reboot_msg,
356            command,
357            actions,
358        })
359    }
360}
361
362/// A struct that describes the service.
363#[derive(Debug, Clone, PartialEq, Eq, Hash)]
364pub struct ServiceInfo {
365    /// Service name
366    pub name: OsString,
367
368    /// User-friendly service name
369    pub display_name: OsString,
370
371    /// The service type
372    pub service_type: ServiceType,
373
374    /// The service startup options
375    pub start_type: ServiceStartType,
376
377    /// The severity of the error, and action taken, if this service fails to start.
378    pub error_control: ServiceErrorControl,
379
380    /// Path to the service binary
381    pub executable_path: PathBuf,
382
383    /// Launch arguments passed to `main` when system starts the service.
384    /// This is not the same as arguments passed to `service_main`.
385    pub launch_arguments: Vec<OsString>,
386
387    /// Service dependencies
388    pub dependencies: Vec<ServiceDependency>,
389
390    /// Account to use for running the service.
391    /// for example: NT Authority\System.
392    /// use `None` to run as LocalSystem.
393    pub account_name: Option<OsString>,
394
395    /// Account password.
396    /// For system accounts this should normally be `None`.
397    pub account_password: Option<OsString>,
398}
399
400/// Same as `ServiceInfo` but with fields that are compatible with the Windows API.
401pub(crate) struct RawServiceInfo {
402    /// Service name
403    pub name: WideCString,
404
405    /// User-friendly service name
406    pub display_name: WideCString,
407
408    /// The service type
409    pub service_type: u32,
410
411    /// The service startup options
412    pub start_type: u32,
413
414    /// The severity of the error, and action taken, if this service fails to start.
415    pub error_control: u32,
416
417    /// Path to the service binary with arguments appended
418    pub launch_command: WideCString,
419
420    /// Service dependencies
421    pub dependencies: Option<WideString>,
422
423    /// Account to use for running the service.
424    /// for example: NT Authority\System.
425    /// use `None` to run as LocalSystem.
426    pub account_name: Option<WideCString>,
427
428    /// Account password.
429    /// For system accounts this should normally be `None`.
430    pub account_password: Option<WideCString>,
431}
432
433impl RawServiceInfo {
434    pub fn new(service_info: &ServiceInfo) -> crate::Result<Self> {
435        let service_name = WideCString::from_os_str(&service_info.name)
436            .map_err(|_| Error::ArgumentHasNulByte("service name"))?;
437        let display_name = WideCString::from_os_str(&service_info.display_name)
438            .map_err(|_| Error::ArgumentHasNulByte("display name"))?;
439        let account_name = to_wide(service_info.account_name.as_ref())
440            .map_err(|_| Error::ArgumentHasNulByte("account name"))?;
441        let account_password = to_wide(service_info.account_password.as_ref())
442            .map_err(|_| Error::ArgumentHasNulByte("account password"))?;
443
444        // escape executable path and arguments and combine them into a single command
445        let mut launch_command_buffer = WideString::new();
446        if service_info
447            .service_type
448            .intersects(ServiceType::KERNEL_DRIVER | ServiceType::FILE_SYSTEM_DRIVER)
449        {
450            // drivers do not support launch arguments
451            if !service_info.launch_arguments.is_empty() {
452                return Err(Error::LaunchArgumentsNotSupported);
453            }
454
455            // also the path must not be quoted even if it contains spaces
456            let executable_path = WideCString::from_os_str(&service_info.executable_path)
457                .map_err(|_| Error::ArgumentHasNulByte("executable path"))?;
458            launch_command_buffer.push(executable_path.to_ustring());
459        } else {
460            let executable_path = escape_wide(&service_info.executable_path)
461                .map_err(|_| Error::ArgumentHasNulByte("executable path"))?;
462            launch_command_buffer.push(executable_path);
463
464            for (i, launch_argument) in service_info.launch_arguments.iter().enumerate() {
465                let wide = escape_wide(launch_argument)
466                    .map_err(|_| Error::ArgumentArrayElementHasNulByte("launch argument", i))?;
467
468                launch_command_buffer.push_str(" ");
469                launch_command_buffer.push(wide);
470            }
471        }
472
473        // Safety: We are sure launch_command_buffer does not contain nulls
474        let launch_command = unsafe { WideCString::from_ustr_unchecked(launch_command_buffer) };
475
476        let dependency_identifiers: Vec<OsString> = service_info
477            .dependencies
478            .iter()
479            .map(|dependency| dependency.to_system_identifier())
480            .collect();
481        let joined_dependencies = double_nul_terminated::from_slice(&dependency_identifiers)
482            .map_err(|_| Error::ArgumentHasNulByte("dependency"))?;
483
484        Ok(Self {
485            name: service_name,
486            display_name,
487            service_type: service_info.service_type.bits(),
488            start_type: service_info.start_type.to_raw(),
489            error_control: service_info.error_control.to_raw(),
490            launch_command,
491            dependencies: joined_dependencies,
492            account_name,
493            account_password,
494        })
495    }
496}
497
498/// A struct that describes the service.
499#[derive(Debug, Clone, PartialEq, Eq, Hash)]
500pub struct ServiceConfig {
501    /// The service type
502    pub service_type: ServiceType,
503
504    /// The service startup options
505    pub start_type: ServiceStartType,
506
507    /// The severity of the error, and action taken, if this service fails to start.
508    pub error_control: ServiceErrorControl,
509
510    /// Path to the service binary
511    pub executable_path: PathBuf,
512
513    /// Path to the service binary
514    pub load_order_group: Option<OsString>,
515
516    /// A unique tag value for this service in the group specified by the load_order_group
517    /// parameter.
518    pub tag_id: u32,
519
520    /// Service dependencies
521    pub dependencies: Vec<ServiceDependency>,
522
523    /// Account to use for running the service.
524    /// for example: NT Authority\System.
525    ///
526    /// This value can be `None` in certain cases, please refer to MSDN for more info:\
527    /// <https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_query_service_configw>
528    pub account_name: Option<OsString>,
529
530    /// User-friendly service name
531    pub display_name: OsString,
532}
533
534impl ServiceConfig {
535    /// Tries to parse a `QUERY_SERVICE_CONFIGW` into Rust [`ServiceConfig`].
536    ///
537    /// # Errors
538    ///
539    /// Returns an error if `dwStartType` does not successfully convert into a
540    /// [`ServiceStartType`], or `dwErrorControl` does not successfully convert
541    /// into a [`ServiceErrorControl`].
542    ///
543    /// # Safety
544    ///
545    /// `lpDependencies` must contain a wide string where each dependency is delimited with a NUL
546    /// and the entire string ends in two NULs.
547    ///
548    /// `lpLoadOrderGroup`, `lpServiceStartName`, `lpBinaryPathName` and `lpDisplayName` must be
549    /// either null or proper null terminated wide C strings.
550    pub unsafe fn from_raw(raw: Services::QUERY_SERVICE_CONFIGW) -> crate::Result<ServiceConfig> {
551        let dependencies = double_nul_terminated::parse_str_ptr(raw.lpDependencies)
552            .iter()
553            .map(ServiceDependency::from_system_identifier)
554            .collect();
555
556        let load_order_group = ptr::NonNull::new(raw.lpLoadOrderGroup).and_then(|wrapped_ptr| {
557            let group = WideCStr::from_ptr_str(wrapped_ptr.as_ptr()).to_os_string();
558            // Return None for consistency, because lpLoadOrderGroup can be either nul or empty
559            // string, which has the same meaning.
560            if group.is_empty() {
561                None
562            } else {
563                Some(group)
564            }
565        });
566
567        let account_name = ptr::NonNull::new(raw.lpServiceStartName)
568            .map(|wrapped_ptr| WideCStr::from_ptr_str(wrapped_ptr.as_ptr()).to_os_string());
569
570        Ok(ServiceConfig {
571            service_type: ServiceType::from_bits_truncate(raw.dwServiceType),
572            start_type: ServiceStartType::from_raw(raw.dwStartType)
573                .map_err(|e| Error::ParseValue("service start type", e))?,
574            error_control: ServiceErrorControl::from_raw(raw.dwErrorControl)
575                .map_err(|e| Error::ParseValue("service error control", e))?,
576            executable_path: PathBuf::from(
577                WideCStr::from_ptr_str(raw.lpBinaryPathName).to_os_string(),
578            ),
579            load_order_group,
580            tag_id: raw.dwTagId,
581            dependencies,
582            account_name,
583            display_name: WideCStr::from_ptr_str(raw.lpDisplayName).to_os_string(),
584        })
585    }
586}
587
588/// Enum describing the event type of HardwareProfileChange
589#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
590#[repr(u32)]
591pub enum HardwareProfileChangeParam {
592    ConfigChanged = WindowsAndMessaging::DBT_CONFIGCHANGED,
593    QueryChangeConfig = WindowsAndMessaging::DBT_QUERYCHANGECONFIG,
594    ConfigChangeCanceled = WindowsAndMessaging::DBT_CONFIGCHANGECANCELED,
595}
596
597impl HardwareProfileChangeParam {
598    pub fn to_raw(&self) -> u32 {
599        *self as u32
600    }
601
602    pub fn from_raw(raw: u32) -> Result<Self, ParseRawError> {
603        match raw {
604            x if x == HardwareProfileChangeParam::ConfigChanged.to_raw() => {
605                Ok(HardwareProfileChangeParam::ConfigChanged)
606            }
607            x if x == HardwareProfileChangeParam::QueryChangeConfig.to_raw() => {
608                Ok(HardwareProfileChangeParam::QueryChangeConfig)
609            }
610            x if x == HardwareProfileChangeParam::ConfigChangeCanceled.to_raw() => {
611                Ok(HardwareProfileChangeParam::ConfigChangeCanceled)
612            }
613            _ => Err(ParseRawError::InvalidInteger(raw)),
614        }
615    }
616}
617
618/// Enum indicates the current power source as
619/// the Data member of GUID_ACDC_POWER_SOURCE notification
620#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
621#[repr(i32)]
622pub enum PowerSource {
623    Ac = Power::PoAc,
624    Dc = Power::PoDc,
625    Hot = Power::PoHot,
626}
627
628impl PowerSource {
629    pub fn to_raw(&self) -> i32 {
630        *self as i32
631    }
632
633    pub fn from_raw(raw: i32) -> Result<PowerSource, ParseRawError> {
634        match raw {
635            x if x == PowerSource::Ac.to_raw() => Ok(PowerSource::Ac),
636            x if x == PowerSource::Dc.to_raw() => Ok(PowerSource::Dc),
637            x if x == PowerSource::Hot.to_raw() => Ok(PowerSource::Hot),
638            _ => Err(ParseRawError::InvalidIntegerSigned(raw)),
639        }
640    }
641}
642
643/// Enum indicates the current monitor's display state as
644/// the Data member of GUID_CONSOLE_DISPLAY_STATE notification
645#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
646#[repr(i32)]
647pub enum DisplayState {
648    Off = SystemServices::PowerMonitorOff,
649    On = SystemServices::PowerMonitorOn,
650    Dimmed = SystemServices::PowerMonitorDim,
651}
652
653impl DisplayState {
654    pub fn to_raw(&self) -> i32 {
655        *self as i32
656    }
657
658    pub fn from_raw(raw: i32) -> Result<DisplayState, ParseRawError> {
659        match raw {
660            x if x == DisplayState::Off.to_raw() => Ok(DisplayState::Off),
661            x if x == DisplayState::On.to_raw() => Ok(DisplayState::On),
662            x if x == DisplayState::Dimmed.to_raw() => Ok(DisplayState::Dimmed),
663            _ => Err(ParseRawError::InvalidIntegerSigned(raw)),
664        }
665    }
666}
667
668/// Enum indicates the combined status of user presence
669/// across all local and remote sessions on the system as
670/// the Data member of GUID_GLOBAL_USER_PRESENCE notification
671#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
672#[repr(i32)]
673pub enum UserStatus {
674    Present = Power::PowerUserPresent,
675    Inactive = Power::PowerUserInactive,
676}
677
678impl UserStatus {
679    pub fn to_raw(&self) -> i32 {
680        *self as i32
681    }
682
683    pub fn from_raw(raw: i32) -> Result<UserStatus, ParseRawError> {
684        match raw {
685            x if x == UserStatus::Present.to_raw() => Ok(UserStatus::Present),
686            x if x == UserStatus::Inactive.to_raw() => Ok(UserStatus::Inactive),
687            _ => Err(ParseRawError::InvalidIntegerSigned(raw)),
688        }
689    }
690}
691
692/// Enum indicates the current monitor state as
693/// the Data member of GUID_MONITOR_POWER_ON notification
694#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
695#[repr(u32)]
696pub enum MonitorState {
697    Off = 0,
698    On = 1,
699}
700
701impl MonitorState {
702    pub fn to_raw(&self) -> u32 {
703        *self as u32
704    }
705
706    pub fn from_raw(raw: u32) -> Result<MonitorState, ParseRawError> {
707        match raw {
708            x if x == MonitorState::Off.to_raw() => Ok(MonitorState::Off),
709            x if x == MonitorState::On.to_raw() => Ok(MonitorState::On),
710            _ => Err(ParseRawError::InvalidInteger(raw)),
711        }
712    }
713}
714
715/// Enum indicates the battery saver state as
716/// the Data member of GUID_POWER_SAVING_STATUS notification
717#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
718#[repr(u32)]
719pub enum BatterySaverState {
720    Off = 0,
721    On = 1,
722}
723
724impl BatterySaverState {
725    pub fn to_raw(&self) -> u32 {
726        *self as u32
727    }
728
729    pub fn from_raw(raw: u32) -> Result<BatterySaverState, ParseRawError> {
730        match raw {
731            x if x == BatterySaverState::Off.to_raw() => Ok(BatterySaverState::Off),
732            x if x == BatterySaverState::On.to_raw() => Ok(BatterySaverState::On),
733            _ => Err(ParseRawError::InvalidInteger(raw)),
734        }
735    }
736}
737
738// FIXME: Remove this function if microsoft/windows-rs#1798 gets merged and published.
739fn is_equal_guid(a: &GUID, b: &GUID) -> bool {
740    a.data1 == b.data1 && a.data2 == b.data2 && a.data3 == b.data3 && a.data4 == b.data4
741}
742
743/// Enum indicates the power scheme personality as
744/// the Data member of GUID_POWERSCHEME_PERSONALITY notification
745#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
746pub enum PowerSchemePersonality {
747    HighPerformance,
748    PowerSaver,
749    Automatic,
750}
751
752impl PowerSchemePersonality {
753    pub fn from_guid(guid: &GUID) -> Result<PowerSchemePersonality, ParseRawError> {
754        match guid {
755            x if is_equal_guid(x, &SystemServices::GUID_MIN_POWER_SAVINGS) => {
756                Ok(PowerSchemePersonality::HighPerformance)
757            }
758            x if is_equal_guid(x, &SystemServices::GUID_MAX_POWER_SAVINGS) => {
759                Ok(PowerSchemePersonality::PowerSaver)
760            }
761            x if is_equal_guid(x, &SystemServices::GUID_TYPICAL_POWER_SAVINGS) => {
762                Ok(PowerSchemePersonality::Automatic)
763            }
764            x => Err(ParseRawError::InvalidGuid(string_from_guid(x))),
765        }
766    }
767}
768
769/// Enum indicates the current away mode state as
770/// the Data member of GUID_SYSTEM_AWAYMODE notification
771#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
772#[repr(u32)]
773pub enum AwayModeState {
774    Exiting = 0,
775    Entering = 1,
776}
777
778impl AwayModeState {
779    pub fn to_raw(&self) -> u32 {
780        *self as u32
781    }
782
783    pub fn from_raw(raw: u32) -> Result<AwayModeState, ParseRawError> {
784        match raw {
785            x if x == AwayModeState::Exiting.to_raw() => Ok(AwayModeState::Exiting),
786            x if x == AwayModeState::Entering.to_raw() => Ok(AwayModeState::Entering),
787            _ => Err(ParseRawError::InvalidInteger(raw)),
788        }
789    }
790}
791
792/// Enum indicates the current lid switch state as
793/// the Data member of GUID_LIDSWITCH_STATE_CHANGE notification
794#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
795#[repr(u32)]
796pub enum LidSwitchStateChange {
797    Closed = 0,
798    Open = 1,
799}
800
801impl LidSwitchStateChange {
802    pub fn to_raw(&self) -> u32 {
803        *self as u32
804    }
805
806    pub fn from_raw(raw: u32) -> Result<LidSwitchStateChange, ParseRawError> {
807        match raw {
808            x if x == LidSwitchStateChange::Closed.to_raw() => Ok(LidSwitchStateChange::Closed),
809            x if x == LidSwitchStateChange::Open.to_raw() => Ok(LidSwitchStateChange::Open),
810            _ => Err(ParseRawError::InvalidInteger(raw)),
811        }
812    }
813}
814
815/// Struct converted from Power::POWERBROADCAST_SETTING
816///
817/// Please refer to MSDN for more info about the data members:
818/// <https://docs.microsoft.com/en-us/windows/win32/power/power-setting-guid>
819#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
820#[non_exhaustive]
821pub enum PowerBroadcastSetting {
822    AcdcPowerSource(PowerSource),
823    BatteryPercentageRemaining(u32),
824    ConsoleDisplayState(DisplayState),
825    GlobalUserPresence(UserStatus),
826    IdleBackgroundTask,
827    MonitorPowerOn(MonitorState),
828    PowerSavingStatus(BatterySaverState),
829    PowerSchemePersonality(PowerSchemePersonality),
830    SystemAwayMode(AwayModeState),
831    LidSwitchStateChange(LidSwitchStateChange),
832}
833
834impl PowerBroadcastSetting {
835    /// Extract PowerBroadcastSetting from `raw`
836    ///
837    /// # Safety
838    ///
839    /// The `raw` must be a valid Power::POWERBROADCAST_SETTING pointer.
840    /// Otherwise, it is undefined behavior.
841    pub unsafe fn from_raw(raw: *mut c_void) -> Result<PowerBroadcastSetting, ParseRawError> {
842        let setting = &*(raw as *const Power::POWERBROADCAST_SETTING);
843        let data = &setting.Data as *const u8;
844
845        match &setting.PowerSetting {
846            x if is_equal_guid(x, &SystemServices::GUID_ACDC_POWER_SOURCE) => {
847                let power_source = *(data as *const i32);
848                Ok(PowerBroadcastSetting::AcdcPowerSource(
849                    PowerSource::from_raw(power_source)?,
850                ))
851            }
852            x if is_equal_guid(x, &SystemServices::GUID_BATTERY_PERCENTAGE_REMAINING) => {
853                let percentage = *(data as *const u32);
854                Ok(PowerBroadcastSetting::BatteryPercentageRemaining(
855                    percentage,
856                ))
857            }
858            x if is_equal_guid(x, &SystemServices::GUID_CONSOLE_DISPLAY_STATE) => {
859                let display_state = *(data as *const i32);
860                Ok(PowerBroadcastSetting::ConsoleDisplayState(
861                    DisplayState::from_raw(display_state)?,
862                ))
863            }
864            x if is_equal_guid(x, &SystemServices::GUID_GLOBAL_USER_PRESENCE) => {
865                let user_status = *(data as *const i32);
866                Ok(PowerBroadcastSetting::GlobalUserPresence(
867                    UserStatus::from_raw(user_status)?,
868                ))
869            }
870            x if is_equal_guid(x, &SystemServices::GUID_IDLE_BACKGROUND_TASK) => {
871                Ok(PowerBroadcastSetting::IdleBackgroundTask)
872            }
873            x if is_equal_guid(x, &SystemServices::GUID_MONITOR_POWER_ON) => {
874                let monitor_state = *(data as *const u32);
875                Ok(PowerBroadcastSetting::MonitorPowerOn(
876                    MonitorState::from_raw(monitor_state)?,
877                ))
878            }
879            x if is_equal_guid(x, &SystemServices::GUID_POWER_SAVING_STATUS) => {
880                let battery_saver_state = *(data as *const u32);
881                Ok(PowerBroadcastSetting::PowerSavingStatus(
882                    BatterySaverState::from_raw(battery_saver_state)?,
883                ))
884            }
885            x if is_equal_guid(x, &SystemServices::GUID_POWERSCHEME_PERSONALITY) => {
886                let guid = *(data as *const GUID);
887                Ok(PowerBroadcastSetting::PowerSchemePersonality(
888                    PowerSchemePersonality::from_guid(&guid)?,
889                ))
890            }
891            x if is_equal_guid(x, &SystemServices::GUID_SYSTEM_AWAYMODE) => {
892                let away_mode_state = *(data as *const u32);
893                Ok(PowerBroadcastSetting::SystemAwayMode(
894                    AwayModeState::from_raw(away_mode_state)?,
895                ))
896            }
897            x if is_equal_guid(x, &SystemServices::GUID_LIDSWITCH_STATE_CHANGE) => {
898                let lid_switch_state = *(data as *const u32);
899                Ok(PowerBroadcastSetting::LidSwitchStateChange(
900                    LidSwitchStateChange::from_raw(lid_switch_state)?,
901                ))
902            }
903            x => Err(ParseRawError::InvalidGuid(string_from_guid(x))),
904        }
905    }
906}
907
908/// Enum describing the PowerEvent event
909#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
910#[non_exhaustive]
911pub enum PowerEventParam {
912    PowerStatusChange,
913    ResumeAutomatic,
914    ResumeSuspend,
915    Suspend,
916    PowerSettingChange(PowerBroadcastSetting),
917    BatteryLow,
918    OemEvent,
919    QuerySuspend,
920    QuerySuspendFailed,
921    ResumeCritical,
922}
923
924impl PowerEventParam {
925    /// Extract PowerEventParam from `event_type` and `event_data`
926    ///
927    /// # Safety
928    ///
929    /// Invalid `event_data` pointer may cause undefined behavior in some circumstances.
930    /// Please refer to MSDN for more info about the requirements:
931    /// <https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nc-winsvc-lphandler_function_ex>
932    pub unsafe fn from_event(
933        event_type: u32,
934        event_data: *mut c_void,
935    ) -> Result<Self, ParseRawError> {
936        match event_type {
937            WindowsAndMessaging::PBT_APMPOWERSTATUSCHANGE => Ok(PowerEventParam::PowerStatusChange),
938            WindowsAndMessaging::PBT_APMRESUMEAUTOMATIC => Ok(PowerEventParam::ResumeAutomatic),
939            WindowsAndMessaging::PBT_APMRESUMESUSPEND => Ok(PowerEventParam::ResumeSuspend),
940            WindowsAndMessaging::PBT_APMSUSPEND => Ok(PowerEventParam::Suspend),
941            WindowsAndMessaging::PBT_POWERSETTINGCHANGE => Ok(PowerEventParam::PowerSettingChange(
942                PowerBroadcastSetting::from_raw(event_data)?,
943            )),
944            WindowsAndMessaging::PBT_APMBATTERYLOW => Ok(PowerEventParam::BatteryLow),
945            WindowsAndMessaging::PBT_APMOEMEVENT => Ok(PowerEventParam::OemEvent),
946            WindowsAndMessaging::PBT_APMQUERYSUSPEND => Ok(PowerEventParam::QuerySuspend),
947            WindowsAndMessaging::PBT_APMQUERYSUSPENDFAILED => {
948                Ok(PowerEventParam::QuerySuspendFailed)
949            }
950            WindowsAndMessaging::PBT_APMRESUMECRITICAL => Ok(PowerEventParam::ResumeCritical),
951            _ => Err(ParseRawError::InvalidInteger(event_type)),
952        }
953    }
954}
955
956/// Enum describing the reason of a SessionChange event
957#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
958#[repr(u32)]
959pub enum SessionChangeReason {
960    ConsoleConnect = WindowsAndMessaging::WTS_CONSOLE_CONNECT,
961    ConsoleDisconnect = WindowsAndMessaging::WTS_CONSOLE_DISCONNECT,
962    RemoteConnect = WindowsAndMessaging::WTS_REMOTE_CONNECT,
963    RemoteDisconnect = WindowsAndMessaging::WTS_REMOTE_DISCONNECT,
964    SessionLogon = WindowsAndMessaging::WTS_SESSION_LOGON,
965    SessionLogoff = WindowsAndMessaging::WTS_SESSION_LOGOFF,
966    SessionLock = WindowsAndMessaging::WTS_SESSION_LOCK,
967    SessionUnlock = WindowsAndMessaging::WTS_SESSION_UNLOCK,
968    SessionRemoteControl = WindowsAndMessaging::WTS_SESSION_REMOTE_CONTROL,
969    SessionCreate = WindowsAndMessaging::WTS_SESSION_CREATE,
970    SessionTerminate = WindowsAndMessaging::WTS_SESSION_TERMINATE,
971}
972
973impl SessionChangeReason {
974    pub fn from_raw(raw: u32) -> Result<SessionChangeReason, ParseRawError> {
975        match raw {
976            x if x == SessionChangeReason::ConsoleConnect.to_raw() => {
977                Ok(SessionChangeReason::ConsoleConnect)
978            }
979            x if x == SessionChangeReason::ConsoleDisconnect.to_raw() => {
980                Ok(SessionChangeReason::ConsoleDisconnect)
981            }
982            x if x == SessionChangeReason::RemoteConnect.to_raw() => {
983                Ok(SessionChangeReason::RemoteConnect)
984            }
985            x if x == SessionChangeReason::RemoteDisconnect.to_raw() => {
986                Ok(SessionChangeReason::RemoteDisconnect)
987            }
988            x if x == SessionChangeReason::SessionLogon.to_raw() => {
989                Ok(SessionChangeReason::SessionLogon)
990            }
991            x if x == SessionChangeReason::SessionLogoff.to_raw() => {
992                Ok(SessionChangeReason::SessionLogoff)
993            }
994            x if x == SessionChangeReason::SessionLock.to_raw() => {
995                Ok(SessionChangeReason::SessionLock)
996            }
997            x if x == SessionChangeReason::SessionUnlock.to_raw() => {
998                Ok(SessionChangeReason::SessionUnlock)
999            }
1000            x if x == SessionChangeReason::SessionRemoteControl.to_raw() => {
1001                Ok(SessionChangeReason::SessionRemoteControl)
1002            }
1003            x if x == SessionChangeReason::SessionCreate.to_raw() => {
1004                Ok(SessionChangeReason::SessionCreate)
1005            }
1006            x if x == SessionChangeReason::SessionTerminate.to_raw() => {
1007                Ok(SessionChangeReason::SessionTerminate)
1008            }
1009            _ => Err(ParseRawError::InvalidInteger(raw)),
1010        }
1011    }
1012
1013    pub fn to_raw(&self) -> u32 {
1014        *self as u32
1015    }
1016}
1017
1018/// Struct converted from RemoteDesktop::WTSSESSION_NOTIFICATION
1019#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1020pub struct SessionNotification {
1021    pub size: u32,
1022    pub session_id: u32,
1023}
1024
1025impl SessionNotification {
1026    pub fn from_raw(raw: RemoteDesktop::WTSSESSION_NOTIFICATION) -> Self {
1027        SessionNotification {
1028            size: raw.cbSize,
1029            session_id: raw.dwSessionId,
1030        }
1031    }
1032}
1033
1034/// Struct describing the SessionChange event
1035#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1036pub struct SessionChangeParam {
1037    pub reason: SessionChangeReason,
1038    pub notification: SessionNotification,
1039}
1040
1041impl SessionChangeParam {
1042    /// Extract SessionChangeParam from `event_data`
1043    ///
1044    /// # Safety
1045    ///
1046    /// The `event_data` must be a valid RemoteDesktop::WTSSESSION_NOTIFICATION pointer.
1047    /// Otherwise, it is undefined behavior.
1048    pub unsafe fn from_event(
1049        event_type: u32,
1050        event_data: *mut c_void,
1051    ) -> Result<Self, ParseRawError> {
1052        let notification = *(event_data as *const RemoteDesktop::WTSSESSION_NOTIFICATION);
1053
1054        Ok(SessionChangeParam {
1055            reason: SessionChangeReason::from_raw(event_type)?,
1056            notification: SessionNotification::from_raw(notification),
1057        })
1058    }
1059}
1060
1061/// Struct describing a user-defined control code (**128** to **255**)
1062#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1063#[repr(transparent)]
1064pub struct UserEventCode(u32); // invariant: always in 128..=255
1065
1066impl UserEventCode {
1067    /// Mainly for declaring user events as constants:
1068    ///
1069    /// ```
1070    /// # use windows_service::service::UserEventCode;
1071    /// const MY_EVENT: UserEventCode = unsafe { UserEventCode::from_unchecked(130) };
1072    /// ```
1073    ///
1074    /// # Safety
1075    /// `raw` should be a valid user control code in the range of **128** to **255**.
1076    pub const unsafe fn from_unchecked(raw: u32) -> Self {
1077        Self(raw)
1078    }
1079
1080    pub fn from_raw(raw: u32) -> Result<UserEventCode, ParseRawError> {
1081        match raw {
1082            128..=255 => Ok(Self(raw)),
1083            _ => Err(ParseRawError::InvalidInteger(raw)),
1084        }
1085    }
1086
1087    pub fn to_raw(&self) -> u32 {
1088        self.0
1089    }
1090}
1091
1092/// Enum describing the service control operations.
1093#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1094#[non_exhaustive]
1095pub enum ServiceControl {
1096    Continue,
1097    Interrogate,
1098    NetBindAdd,
1099    NetBindDisable,
1100    NetBindEnable,
1101    NetBindRemove,
1102    ParamChange,
1103    Pause,
1104    Preshutdown,
1105    Shutdown,
1106    Stop,
1107    HardwareProfileChange(HardwareProfileChangeParam),
1108    PowerEvent(PowerEventParam),
1109    SessionChange(SessionChangeParam),
1110    TimeChange,
1111    TriggerEvent,
1112    UserEvent(UserEventCode),
1113}
1114
1115impl ServiceControl {
1116    /// Convert to ServiceControl from parameters received by `service_control_handler`
1117    ///
1118    /// # Safety
1119    ///
1120    /// Invalid `event_data` pointer may cause undefined behavior in some circumstances.
1121    /// Please refer to MSDN for more info about the requirements:
1122    /// <https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nc-winsvc-lphandler_function_ex>
1123    pub unsafe fn from_raw(
1124        raw: u32,
1125        event_type: u32,
1126        event_data: *mut c_void,
1127    ) -> Result<Self, ParseRawError> {
1128        match raw {
1129            Services::SERVICE_CONTROL_CONTINUE => Ok(ServiceControl::Continue),
1130            Services::SERVICE_CONTROL_INTERROGATE => Ok(ServiceControl::Interrogate),
1131            Services::SERVICE_CONTROL_NETBINDADD => Ok(ServiceControl::NetBindAdd),
1132            Services::SERVICE_CONTROL_NETBINDDISABLE => Ok(ServiceControl::NetBindDisable),
1133            Services::SERVICE_CONTROL_NETBINDENABLE => Ok(ServiceControl::NetBindEnable),
1134            Services::SERVICE_CONTROL_NETBINDREMOVE => Ok(ServiceControl::NetBindRemove),
1135            Services::SERVICE_CONTROL_PARAMCHANGE => Ok(ServiceControl::ParamChange),
1136            Services::SERVICE_CONTROL_PAUSE => Ok(ServiceControl::Pause),
1137            Services::SERVICE_CONTROL_PRESHUTDOWN => Ok(ServiceControl::Preshutdown),
1138            Services::SERVICE_CONTROL_SHUTDOWN => Ok(ServiceControl::Shutdown),
1139            Services::SERVICE_CONTROL_STOP => Ok(ServiceControl::Stop),
1140            Services::SERVICE_CONTROL_HARDWAREPROFILECHANGE => {
1141                HardwareProfileChangeParam::from_raw(event_type)
1142                    .map(ServiceControl::HardwareProfileChange)
1143            }
1144            Services::SERVICE_CONTROL_POWEREVENT => {
1145                PowerEventParam::from_event(event_type, event_data).map(ServiceControl::PowerEvent)
1146            }
1147            Services::SERVICE_CONTROL_SESSIONCHANGE => {
1148                SessionChangeParam::from_event(event_type, event_data)
1149                    .map(ServiceControl::SessionChange)
1150            }
1151            Services::SERVICE_CONTROL_TIMECHANGE => Ok(ServiceControl::TimeChange),
1152            Services::SERVICE_CONTROL_TRIGGEREVENT => Ok(ServiceControl::TriggerEvent),
1153            _ => UserEventCode::from_raw(raw).map(ServiceControl::UserEvent),
1154        }
1155    }
1156
1157    pub fn raw_service_control_type(&self) -> u32 {
1158        match self {
1159            ServiceControl::Continue => Services::SERVICE_CONTROL_CONTINUE,
1160            ServiceControl::Interrogate => Services::SERVICE_CONTROL_INTERROGATE,
1161            ServiceControl::NetBindAdd => Services::SERVICE_CONTROL_NETBINDADD,
1162            ServiceControl::NetBindDisable => Services::SERVICE_CONTROL_NETBINDDISABLE,
1163            ServiceControl::NetBindEnable => Services::SERVICE_CONTROL_NETBINDENABLE,
1164            ServiceControl::NetBindRemove => Services::SERVICE_CONTROL_NETBINDREMOVE,
1165            ServiceControl::ParamChange => Services::SERVICE_CONTROL_PARAMCHANGE,
1166            ServiceControl::Pause => Services::SERVICE_CONTROL_PAUSE,
1167            ServiceControl::Preshutdown => Services::SERVICE_CONTROL_PRESHUTDOWN,
1168            ServiceControl::Shutdown => Services::SERVICE_CONTROL_SHUTDOWN,
1169            ServiceControl::Stop => Services::SERVICE_CONTROL_STOP,
1170            ServiceControl::HardwareProfileChange(_) => {
1171                Services::SERVICE_CONTROL_HARDWAREPROFILECHANGE
1172            }
1173            ServiceControl::PowerEvent(_) => Services::SERVICE_CONTROL_POWEREVENT,
1174            ServiceControl::SessionChange(_) => Services::SERVICE_CONTROL_SESSIONCHANGE,
1175            ServiceControl::TimeChange => Services::SERVICE_CONTROL_TIMECHANGE,
1176            ServiceControl::TriggerEvent => Services::SERVICE_CONTROL_TRIGGEREVENT,
1177            ServiceControl::UserEvent(event) => event.to_raw(),
1178        }
1179    }
1180}
1181
1182/// Service state returned as a part of [`ServiceStatus`].
1183#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1184#[repr(u32)]
1185pub enum ServiceState {
1186    Stopped = Services::SERVICE_STOPPED,
1187    StartPending = Services::SERVICE_START_PENDING,
1188    StopPending = Services::SERVICE_STOP_PENDING,
1189    Running = Services::SERVICE_RUNNING,
1190    ContinuePending = Services::SERVICE_CONTINUE_PENDING,
1191    PausePending = Services::SERVICE_PAUSE_PENDING,
1192    Paused = Services::SERVICE_PAUSED,
1193}
1194
1195impl ServiceState {
1196    fn from_raw(raw: u32) -> Result<Self, ParseRawError> {
1197        match raw {
1198            x if x == ServiceState::Stopped.to_raw() => Ok(ServiceState::Stopped),
1199            x if x == ServiceState::StartPending.to_raw() => Ok(ServiceState::StartPending),
1200            x if x == ServiceState::StopPending.to_raw() => Ok(ServiceState::StopPending),
1201            x if x == ServiceState::Running.to_raw() => Ok(ServiceState::Running),
1202            x if x == ServiceState::ContinuePending.to_raw() => Ok(ServiceState::ContinuePending),
1203            x if x == ServiceState::PausePending.to_raw() => Ok(ServiceState::PausePending),
1204            x if x == ServiceState::Paused.to_raw() => Ok(ServiceState::Paused),
1205            _ => Err(ParseRawError::InvalidInteger(raw)),
1206        }
1207    }
1208
1209    fn to_raw(self) -> u32 {
1210        self as u32
1211    }
1212}
1213
1214/// Service exit code abstraction.
1215///
1216/// This struct provides a logic around the relationship between [`dwWin32ExitCode`] and
1217/// [`dwServiceSpecificExitCode`].
1218///
1219/// The service can either return a win32 error code or a custom error code. In case of custom
1220/// error, [`dwWin32ExitCode`] has to be set to [`ERROR_SERVICE_SPECIFIC_ERROR`] and the
1221/// [`dwServiceSpecificExitCode`] assigned with custom error code.
1222///
1223/// Refer to the corresponding MSDN article for more info:\
1224/// <https://msdn.microsoft.com/en-us/library/windows/desktop/ms685996(v=vs.85).aspx>
1225///
1226/// [`dwWin32ExitCode`]: Services::SERVICE_STATUS::dwWin32ExitCode
1227/// [`dwServiceSpecificExitCode`]: Services::SERVICE_STATUS::dwServiceSpecificExitCode
1228#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1229pub enum ServiceExitCode {
1230    Win32(u32),
1231    ServiceSpecific(u32),
1232}
1233
1234impl ServiceExitCode {
1235    /// A `ServiceExitCode` indicating success, no errors.
1236    pub const NO_ERROR: Self = ServiceExitCode::Win32(NO_ERROR);
1237
1238    fn copy_to(&self, raw_service_status: &mut Services::SERVICE_STATUS) {
1239        match *self {
1240            ServiceExitCode::Win32(win32_error_code) => {
1241                raw_service_status.dwWin32ExitCode = win32_error_code;
1242                raw_service_status.dwServiceSpecificExitCode = 0;
1243            }
1244            ServiceExitCode::ServiceSpecific(service_error_code) => {
1245                raw_service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
1246                raw_service_status.dwServiceSpecificExitCode = service_error_code;
1247            }
1248        }
1249    }
1250}
1251
1252impl Default for ServiceExitCode {
1253    fn default() -> Self {
1254        Self::NO_ERROR
1255    }
1256}
1257
1258impl<'a> From<&'a Services::SERVICE_STATUS> for ServiceExitCode {
1259    fn from(service_status: &'a Services::SERVICE_STATUS) -> Self {
1260        if service_status.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR {
1261            ServiceExitCode::ServiceSpecific(service_status.dwServiceSpecificExitCode)
1262        } else {
1263            ServiceExitCode::Win32(service_status.dwWin32ExitCode)
1264        }
1265    }
1266}
1267
1268impl<'a> From<&'a Services::SERVICE_STATUS_PROCESS> for ServiceExitCode {
1269    fn from(service_status: &'a Services::SERVICE_STATUS_PROCESS) -> Self {
1270        if service_status.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR {
1271            ServiceExitCode::ServiceSpecific(service_status.dwServiceSpecificExitCode)
1272        } else {
1273            ServiceExitCode::Win32(service_status.dwWin32ExitCode)
1274        }
1275    }
1276}
1277
1278bitflags::bitflags! {
1279    /// Flags describing accepted types of service control events.
1280    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
1281    pub struct ServiceControlAccept: u32 {
1282        /// The service is a network component that can accept changes in its binding without being
1283        /// stopped and restarted. This allows service to receive `ServiceControl::Netbind*`
1284        /// family of events.
1285        const NETBIND_CHANGE = Services::SERVICE_ACCEPT_NETBINDCHANGE;
1286
1287        /// The service can reread its startup parameters without being stopped and restarted.
1288        const PARAM_CHANGE = Services::SERVICE_ACCEPT_PARAMCHANGE;
1289
1290        /// The service can be paused and continued.
1291        const PAUSE_CONTINUE = Services::SERVICE_ACCEPT_PAUSE_CONTINUE;
1292
1293        /// The service can perform preshutdown tasks.
1294        /// Mutually exclusive with shutdown.
1295        const PRESHUTDOWN = Services::SERVICE_ACCEPT_PRESHUTDOWN;
1296
1297        /// The service is notified when system shutdown occurs.
1298        /// Mutually exclusive with preshutdown.
1299        const SHUTDOWN = Services::SERVICE_ACCEPT_SHUTDOWN;
1300
1301        /// The service can be stopped.
1302        const STOP = Services::SERVICE_ACCEPT_STOP;
1303
1304        /// The service is notified when the computer's hardware profile has changed.
1305        /// This enables the system to send SERVICE_CONTROL_HARDWAREPROFILECHANGE
1306        /// notifications to the service.
1307        const HARDWARE_PROFILE_CHANGE = Services::SERVICE_ACCEPT_HARDWAREPROFILECHANGE;
1308
1309        /// The service is notified when the computer's power status has changed.
1310        /// This enables the system to send SERVICE_CONTROL_POWEREVENT notifications to the service.
1311        const POWER_EVENT = Services::SERVICE_ACCEPT_POWEREVENT;
1312
1313        /// The service is notified when the computer's session status has changed.
1314        /// This enables the system to send SERVICE_CONTROL_SESSIONCHANGE notifications to the service.
1315        const SESSION_CHANGE = Services::SERVICE_ACCEPT_SESSIONCHANGE;
1316
1317        /// The service is notified when the system time has changed.
1318        /// This enables the system to send SERVICE_CONTROL_TIMECHANGE notifications to the service.
1319        const TIME_CHANGE = Services::SERVICE_ACCEPT_TIMECHANGE;
1320
1321        /// The service is notified when an event for which the service has registered occurs.
1322        /// This enables the system to send SERVICE_CONTROL_TRIGGEREVENT notifications to the service.
1323        const TRIGGER_EVENT = Services::SERVICE_ACCEPT_TRIGGEREVENT;
1324    }
1325}
1326
1327/// Service status.
1328///
1329/// This struct wraps the lower level [`SERVICE_STATUS`] providing a few convenience types to fill
1330/// in the service status information. However it doesn't fully guard the developer from producing
1331/// an invalid `ServiceStatus`, therefore please refer to the corresponding MSDN article and in
1332/// particular how to fill in the `exit_code`, `checkpoint`, `wait_hint` fields:\
1333/// <https://msdn.microsoft.com/en-us/library/windows/desktop/ms685996(v=vs.85).aspx>
1334///
1335/// [`SERVICE_STATUS`]: Services::SERVICE_STATUS
1336#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1337pub struct ServiceStatus {
1338    /// Type of service.
1339    pub service_type: ServiceType,
1340
1341    /// Current state of the service.
1342    pub current_state: ServiceState,
1343
1344    /// Control commands that service accepts.
1345    pub controls_accepted: ServiceControlAccept,
1346
1347    /// The error code the service uses to report an error that occurs when it is starting or
1348    /// stopping.
1349    pub exit_code: ServiceExitCode,
1350
1351    /// Service initialization progress value that should be increased during a lengthy start,
1352    /// stop, pause or continue operations. For example the service should increment the value as
1353    /// it completes each step of initialization.
1354    /// This value must be zero if the service does not have any pending start, stop, pause or
1355    /// continue operations.
1356    pub checkpoint: u32,
1357
1358    /// Estimated time for pending operation.
1359    /// This basically works as a timeout until the system assumes that the service hung.
1360    /// This could be either circumvented by updating the [`ServiceStatus::current_state`] or
1361    /// incrementing a [`ServiceStatus::checkpoint`] value.
1362    ///
1363    /// # Panics
1364    ///
1365    /// Converting this to the FFI form will panic if the duration is too large to fit as
1366    /// milliseconds in a `u32`.
1367    pub wait_hint: Duration,
1368
1369    /// Process ID of the service
1370    /// This is only retrieved when querying the service status.
1371    pub process_id: Option<u32>,
1372}
1373
1374impl ServiceStatus {
1375    pub(crate) fn to_raw(&self) -> Services::SERVICE_STATUS {
1376        let mut raw_status = unsafe { mem::zeroed::<Services::SERVICE_STATUS>() };
1377        raw_status.dwServiceType = self.service_type.bits();
1378        raw_status.dwCurrentState = self.current_state.to_raw();
1379        raw_status.dwControlsAccepted = self.controls_accepted.bits();
1380
1381        self.exit_code.copy_to(&mut raw_status);
1382
1383        raw_status.dwCheckPoint = self.checkpoint;
1384
1385        raw_status.dwWaitHint =
1386            u32::try_from(self.wait_hint.as_millis()).expect("Too long wait_hint");
1387
1388        raw_status
1389    }
1390
1391    /// Tries to parse a `SERVICE_STATUS` into a Rust [`ServiceStatus`].
1392    ///
1393    /// # Errors
1394    ///
1395    /// Returns an error if the `dwCurrentState` field does not represent a valid [`ServiceState`].
1396    fn from_raw(raw: Services::SERVICE_STATUS) -> Result<Self, ParseRawError> {
1397        Ok(ServiceStatus {
1398            service_type: ServiceType::from_bits_truncate(raw.dwServiceType),
1399            current_state: ServiceState::from_raw(raw.dwCurrentState)?,
1400            controls_accepted: ServiceControlAccept::from_bits_truncate(raw.dwControlsAccepted),
1401            exit_code: ServiceExitCode::from(&raw),
1402            checkpoint: raw.dwCheckPoint,
1403            wait_hint: Duration::from_millis(raw.dwWaitHint as u64),
1404            process_id: None,
1405        })
1406    }
1407
1408    /// Tries to parse a `SERVICE_STATUS_PROCESS` into a Rust [`ServiceStatus`].
1409    ///
1410    /// # Errors
1411    ///
1412    /// Returns an error if the `dwCurrentState` field does not represent a valid [`ServiceState`].
1413    fn from_raw_ex(raw: Services::SERVICE_STATUS_PROCESS) -> Result<Self, ParseRawError> {
1414        let current_state = ServiceState::from_raw(raw.dwCurrentState)?;
1415        let process_id = match current_state {
1416            ServiceState::Running => Some(raw.dwProcessId),
1417            _ => None,
1418        };
1419        Ok(ServiceStatus {
1420            service_type: ServiceType::from_bits_truncate(raw.dwServiceType),
1421            current_state,
1422            controls_accepted: ServiceControlAccept::from_bits_truncate(raw.dwControlsAccepted),
1423            exit_code: ServiceExitCode::from(&raw),
1424            checkpoint: raw.dwCheckPoint,
1425            wait_hint: Duration::from_millis(raw.dwWaitHint as u64),
1426            process_id,
1427        })
1428    }
1429}
1430
1431/// This controls how the service SID is added to the service process token.
1432/// <https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_sid_info>
1433#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1434#[repr(u32)]
1435pub enum ServiceSidType {
1436    None = 0,
1437    Restricted = 3,
1438    Unrestricted = 1,
1439}
1440
1441/// A struct that represents a system service.
1442///
1443/// The instances of the [`Service`] can be obtained via [`ServiceManager`].
1444///
1445/// [`ServiceManager`]: super::service_manager::ServiceManager
1446pub struct Service {
1447    service_handle: ScHandle,
1448}
1449
1450impl Service {
1451    pub(crate) fn new(service_handle: ScHandle) -> Self {
1452        Service { service_handle }
1453    }
1454
1455    /// Provides access to the underlying system service handle
1456    pub fn raw_handle(&self) -> Services::SC_HANDLE {
1457        self.service_handle.raw_handle()
1458    }
1459
1460    /// Start the service.
1461    ///
1462    /// # Example
1463    ///
1464    /// ```rust,no_run
1465    /// use std::ffi::OsStr;
1466    /// use windows_service::service::ServiceAccess;
1467    /// use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
1468    ///
1469    /// # fn main() -> windows_service::Result<()> {
1470    /// let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;
1471    /// let my_service = manager.open_service("my_service", ServiceAccess::START)?;
1472    /// my_service.start(&[OsStr::new("Started from Rust!")])?;
1473    /// # Ok(())
1474    /// # }
1475    /// ```
1476    pub fn start<S: AsRef<OsStr>>(&self, service_arguments: &[S]) -> crate::Result<()> {
1477        let wide_service_arguments = service_arguments
1478            .iter()
1479            .map(|s| {
1480                WideCString::from_os_str(s).map_err(|_| Error::ArgumentHasNulByte("start argument"))
1481            })
1482            .collect::<crate::Result<Vec<WideCString>>>()?;
1483
1484        let raw_service_arguments: Vec<*const u16> = wide_service_arguments
1485            .iter()
1486            .map(|s| s.as_ptr() as _)
1487            .collect();
1488
1489        let success = unsafe {
1490            Services::StartServiceW(
1491                self.service_handle.raw_handle(),
1492                raw_service_arguments.len() as u32,
1493                raw_service_arguments.as_ptr(),
1494            )
1495        };
1496
1497        if success == 0 {
1498            Err(Error::Winapi(io::Error::last_os_error()))
1499        } else {
1500            Ok(())
1501        }
1502    }
1503
1504    /// Stop the service.
1505    pub fn stop(&self) -> crate::Result<ServiceStatus> {
1506        self.send_control_command(ServiceControl::Stop)
1507    }
1508
1509    /// Pause the service.
1510    ///
1511    /// # Example
1512    ///
1513    /// ```rust,no_run
1514    /// use windows_service::service::ServiceAccess;
1515    /// use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
1516    ///
1517    /// # fn main() -> windows_service::Result<()> {
1518    /// let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;
1519    /// let my_service = manager.open_service("my_service", ServiceAccess::PAUSE_CONTINUE)?;
1520    /// my_service.pause()?;
1521    /// # Ok(())
1522    /// # }
1523    /// ```
1524    pub fn pause(&self) -> crate::Result<ServiceStatus> {
1525        self.send_control_command(ServiceControl::Pause)
1526    }
1527
1528    /// Resume the paused service.
1529    pub fn resume(&self) -> crate::Result<ServiceStatus> {
1530        self.send_control_command(ServiceControl::Continue)
1531    }
1532
1533    /// Send user-defined control code.
1534    pub fn notify(&self, code: UserEventCode) -> crate::Result<ServiceStatus> {
1535        self.send_control_command(ServiceControl::UserEvent(code))
1536    }
1537
1538    /// Get the service status from the system.
1539    pub fn query_status(&self) -> crate::Result<ServiceStatus> {
1540        let mut raw_status = unsafe { mem::zeroed::<Services::SERVICE_STATUS_PROCESS>() };
1541        let mut bytes_needed: u32 = 0;
1542        let success = unsafe {
1543            Services::QueryServiceStatusEx(
1544                self.service_handle.raw_handle(),
1545                Services::SC_STATUS_PROCESS_INFO,
1546                &mut raw_status as *mut _ as _,
1547                std::mem::size_of::<Services::SERVICE_STATUS_PROCESS>() as u32,
1548                &mut bytes_needed,
1549            )
1550        };
1551        if success == 0 {
1552            Err(Error::Winapi(io::Error::last_os_error()))
1553        } else {
1554            ServiceStatus::from_raw_ex(raw_status)
1555                .map_err(|e| Error::ParseValue("service status", e))
1556        }
1557    }
1558
1559    /// Mark the service for deletion from the service control manager database.
1560    ///
1561    /// The database entry is not removed until all open handles to the service have been closed
1562    /// and the service is stopped. If the service is not or cannot be stopped, the database entry
1563    /// is removed when the system is restarted. This function will return an error if the service
1564    /// has already been marked for deletion.
1565    pub fn delete(&self) -> crate::Result<()> {
1566        let success = unsafe { Services::DeleteService(self.service_handle.raw_handle()) };
1567        if success == 0 {
1568            Err(Error::Winapi(io::Error::last_os_error()))
1569        } else {
1570            Ok(())
1571        }
1572    }
1573
1574    /// Get the service config from the system.
1575    pub fn query_config(&self) -> crate::Result<ServiceConfig> {
1576        // As per docs, the maximum size of data buffer used by QueryServiceConfigW is 8K
1577        let mut data = vec![0u8; MAX_QUERY_BUFFER_SIZE];
1578        let mut bytes_written: u32 = 0;
1579
1580        let success = unsafe {
1581            Services::QueryServiceConfigW(
1582                self.service_handle.raw_handle(),
1583                data.as_mut_ptr() as _,
1584                data.len() as u32,
1585                &mut bytes_written,
1586            )
1587        };
1588
1589        if success == 0 {
1590            Err(Error::Winapi(io::Error::last_os_error()))
1591        } else {
1592            unsafe {
1593                let raw_config = data.as_ptr() as *const Services::QUERY_SERVICE_CONFIGW;
1594                ServiceConfig::from_raw(*raw_config)
1595            }
1596        }
1597    }
1598
1599    /// Update the service config.
1600    /// Caveat: You cannot reset the account name/password by passing NULL.
1601    ///
1602    /// This implementation does not currently expose the full flexibility of the
1603    /// `ChangeServiceConfigW` API. When calling the API it's possible to pass NULL in place of
1604    /// any of the string arguments to indicate that they should not be updated.
1605    ///
1606    /// If we wanted to support this we wouldn't be able to reuse the `ServiceInfo` struct.
1607    pub fn change_config(&self, service_info: &ServiceInfo) -> crate::Result<()> {
1608        let raw_info = RawServiceInfo::new(service_info)?;
1609        let success = unsafe {
1610            Services::ChangeServiceConfigW(
1611                self.service_handle.raw_handle(),
1612                raw_info.service_type,
1613                raw_info.start_type,
1614                raw_info.error_control,
1615                raw_info.launch_command.as_ptr(),
1616                ptr::null(),     // load ordering group
1617                ptr::null_mut(), // tag id within the load ordering group
1618                raw_info
1619                    .dependencies
1620                    .as_ref()
1621                    .map_or(ptr::null(), |s| s.as_ptr()),
1622                raw_info
1623                    .account_name
1624                    .as_ref()
1625                    .map_or(ptr::null(), |s| s.as_ptr()),
1626                raw_info
1627                    .account_password
1628                    .as_ref()
1629                    .map_or(ptr::null(), |s| s.as_ptr()),
1630                raw_info.display_name.as_ptr(),
1631            )
1632        };
1633
1634        if success == 0 {
1635            Err(Error::Winapi(io::Error::last_os_error()))
1636        } else {
1637            Ok(())
1638        }
1639    }
1640
1641    /// Configure failure actions to run when the service terminates before reporting the
1642    /// [`ServiceState::Stopped`] back to the system or if it exits with non-zero
1643    /// [`ServiceExitCode`].
1644    ///
1645    /// Please refer to MSDN for more info:\
1646    /// <https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-_service_failure_actions_flag>
1647    pub fn set_failure_actions_on_non_crash_failures(&self, enabled: bool) -> crate::Result<()> {
1648        let mut raw_failure_actions_flag =
1649            unsafe { mem::zeroed::<Services::SERVICE_FAILURE_ACTIONS_FLAG>() };
1650
1651        raw_failure_actions_flag.fFailureActionsOnNonCrashFailures = if enabled { 1 } else { 0 };
1652
1653        unsafe {
1654            self.change_config2(
1655                Services::SERVICE_CONFIG_FAILURE_ACTIONS_FLAG,
1656                &mut raw_failure_actions_flag,
1657            )
1658            .map_err(Error::Winapi)
1659        }
1660    }
1661
1662    /// Query the system for the boolean indication that the service is configured to run failure
1663    /// actions on non-crash failures.
1664    pub fn get_failure_actions_on_non_crash_failures(&self) -> crate::Result<bool> {
1665        let mut data = vec![0u8; MAX_QUERY_BUFFER_SIZE];
1666
1667        let raw_failure_actions_flag: Services::SERVICE_FAILURE_ACTIONS_FLAG = unsafe {
1668            self.query_config2(Services::SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &mut data)
1669                .map_err(Error::Winapi)?
1670        };
1671        Ok(raw_failure_actions_flag.fFailureActionsOnNonCrashFailures != 0)
1672    }
1673
1674    /// Query the system for the service's SID type information.
1675    ///
1676    /// The service must be open with the [`ServiceAccess::QUERY_CONFIG`]
1677    /// access permission prior to calling this method.
1678    pub fn get_config_service_sid_info(&self) -> crate::Result<ServiceSidType> {
1679        let mut data = vec![0u8; u32::BITS as usize / 8];
1680
1681        // SAFETY: The structure we get back is `SERVICE_SID_INFO`. It has a
1682        // single member that specifies the new SID type as a `u32`, and as
1683        // such, we can get away with not explicitly creating a structure and
1684        // instead re-using `ServiceSidType` that is `repr(u32)`.
1685        unsafe { self.query_config2(Services::SERVICE_CONFIG_SERVICE_SID_INFO, &mut data) }
1686            .map_err(Error::Winapi)
1687    }
1688
1689    /// Require the system to set the service's SID type information to the
1690    /// provided value.
1691    ///
1692    /// The service must be open with the [`ServiceAccess::CHANGE_CONFIG`]
1693    /// access permission prior to calling this method.
1694    pub fn set_config_service_sid_info(
1695        &self,
1696        mut service_sid_type: ServiceSidType,
1697    ) -> crate::Result<()> {
1698        // SAFETY: The structure we need to pass in is `SERVICE_SID_INFO`.
1699        // It has a single member that specifies the new SID type, and as such,
1700        // we can get away with not explicitly creating a structure in Rust.
1701        unsafe {
1702            self.change_config2(
1703                Services::SERVICE_CONFIG_SERVICE_SID_INFO,
1704                &mut service_sid_type,
1705            )
1706            .map_err(Error::Winapi)
1707        }
1708    }
1709
1710    /// Query the configured failure actions for the service.
1711    pub fn get_failure_actions(&self) -> crate::Result<ServiceFailureActions> {
1712        unsafe {
1713            let mut data = vec![0u8; MAX_QUERY_BUFFER_SIZE];
1714
1715            let raw_failure_actions: Services::SERVICE_FAILURE_ACTIONSW = self
1716                .query_config2(Services::SERVICE_CONFIG_FAILURE_ACTIONS, &mut data)
1717                .map_err(Error::Winapi)?;
1718
1719            ServiceFailureActions::from_raw(raw_failure_actions)
1720        }
1721    }
1722
1723    /// Update failure actions.
1724    ///
1725    /// Pass `None` for optional fields to keep the corresponding fields unchanged, or pass an empty
1726    /// value to reset them.
1727    ///
1728    ///
1729    /// # Example
1730    ///
1731    /// ```rust,no_run
1732    /// use std::ffi::OsString;
1733    /// use std::time::Duration;
1734    /// use windows_service::service::{
1735    ///     ServiceAccess, ServiceAction, ServiceActionType, ServiceFailureActions,
1736    ///     ServiceFailureResetPeriod,
1737    /// };
1738    /// use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
1739    ///
1740    /// # fn main() -> windows_service::Result<()> {
1741    /// let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;
1742    /// let my_service = manager.open_service(
1743    ///     "my_service",
1744    ///     ServiceAccess::START | ServiceAccess::CHANGE_CONFIG,
1745    /// )?;
1746    ///
1747    /// let actions = vec![
1748    ///     ServiceAction {
1749    ///         action_type: ServiceActionType::Restart,
1750    ///         delay: Duration::from_secs(5),
1751    ///     },
1752    ///     ServiceAction {
1753    ///         action_type: ServiceActionType::RunCommand,
1754    ///         delay: Duration::from_secs(10),
1755    ///     },
1756    ///     ServiceAction {
1757    ///         action_type: ServiceActionType::None,
1758    ///         delay: Duration::default(),
1759    ///     },
1760    /// ];
1761    ///
1762    /// let failure_actions = ServiceFailureActions {
1763    ///     reset_period: ServiceFailureResetPeriod::After(Duration::from_secs(86400)),
1764    ///     reboot_msg: None,
1765    ///     command: Some(OsString::from("ping 127.0.0.1")),
1766    ///     actions: Some(actions),
1767    /// };
1768    ///
1769    /// my_service.update_failure_actions(failure_actions)?;
1770    /// #    Ok(())
1771    /// # }
1772    /// ```
1773    pub fn update_failure_actions(&self, update: ServiceFailureActions) -> crate::Result<()> {
1774        let mut raw_failure_actions =
1775            unsafe { mem::zeroed::<Services::SERVICE_FAILURE_ACTIONSW>() };
1776
1777        let mut reboot_msg = to_wide_slice(update.reboot_msg)
1778            .map_err(|_| Error::ArgumentHasNulByte("service action failures reboot message"))?;
1779        let mut command = to_wide_slice(update.command)
1780            .map_err(|_| Error::ArgumentHasNulByte("service action failures command"))?;
1781        let mut sc_actions: Option<Vec<Services::SC_ACTION>> = update
1782            .actions
1783            .map(|actions| actions.iter().map(ServiceAction::to_raw).collect());
1784
1785        raw_failure_actions.dwResetPeriod = update.reset_period.to_raw();
1786        raw_failure_actions.lpRebootMsg = reboot_msg
1787            .as_mut()
1788            .map_or(ptr::null_mut(), |s| s.as_mut_ptr());
1789        raw_failure_actions.lpCommand =
1790            command.as_mut().map_or(ptr::null_mut(), |s| s.as_mut_ptr());
1791        raw_failure_actions.cActions = sc_actions.as_ref().map_or(0, |v| v.len()) as u32;
1792        raw_failure_actions.lpsaActions = sc_actions
1793            .as_mut()
1794            .map_or(ptr::null_mut(), |actions| actions.as_mut_ptr());
1795
1796        unsafe {
1797            self.change_config2(
1798                Services::SERVICE_CONFIG_FAILURE_ACTIONS,
1799                &mut raw_failure_actions,
1800            )
1801            .map_err(Error::Winapi)
1802        }
1803    }
1804
1805    /// Set service description.
1806    ///
1807    /// Required permission: [`ServiceAccess::CHANGE_CONFIG`].
1808    pub fn set_description(&self, description: impl AsRef<OsStr>) -> crate::Result<()> {
1809        let wide_str = WideCString::from_os_str(description)
1810            .map_err(|_| Error::ArgumentHasNulByte("service description"))?;
1811        let mut service_description = Services::SERVICE_DESCRIPTIONW {
1812            lpDescription: wide_str.as_ptr() as *mut _,
1813        };
1814
1815        unsafe {
1816            self.change_config2(
1817                Services::SERVICE_CONFIG_DESCRIPTION,
1818                &mut service_description,
1819            )
1820            .map_err(Error::Winapi)
1821        }
1822    }
1823
1824    /// Set if an auto-start service should be delayed.
1825    ///
1826    /// If true, the service is started after other auto-start services are started plus a short delay.
1827    /// Otherwise, the service is started during system boot. The default is false. This setting is
1828    /// ignored unless the service is an auto-start service.
1829    ///
1830    /// Required permission: [`ServiceAccess::CHANGE_CONFIG`].
1831    pub fn set_delayed_auto_start(&self, delayed: bool) -> crate::Result<()> {
1832        let mut delayed = Services::SERVICE_DELAYED_AUTO_START_INFO {
1833            fDelayedAutostart: delayed as i32,
1834        };
1835        unsafe {
1836            self.change_config2(
1837                Services::SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
1838                &mut delayed,
1839            )
1840            .map_err(Error::Winapi)
1841        }
1842    }
1843
1844    /// Set the preshutdown timeout value of the service.
1845    ///
1846    /// When the system prepares to shutdown, the service control manager will send [`ServiceControl::Preshutdown`]
1847    /// to any service that accepts [`ServiceControlAccept::PRESHUTDOWN`] and block shutdown until either the
1848    /// service is stopped or the preshutdown timeout has elapsed. The default value is 180 seconds on releases
1849    /// prior to Windows 10 build 15063, and 10 seconds afterwards. This value is irrelevant unless the service
1850    /// handles [`ServiceControl::Preshutdown`].
1851    ///
1852    /// Panics if the specified timeout is too large to fit as milliseconds in a `u32`.
1853    ///
1854    /// Required permission: [`ServiceAccess::CHANGE_CONFIG`].
1855    pub fn set_preshutdown_timeout(&self, timeout: Duration) -> crate::Result<()> {
1856        let mut timeout = Services::SERVICE_PRESHUTDOWN_INFO {
1857            dwPreshutdownTimeout: u32::try_from(timeout.as_millis()).expect("Too long timeout"),
1858        };
1859        unsafe {
1860            self.change_config2(Services::SERVICE_CONFIG_PRESHUTDOWN_INFO, &mut timeout)
1861                .map_err(Error::Winapi)
1862        }
1863    }
1864
1865    /// Private helper to send the control commands to the system.
1866    fn send_control_command(&self, command: ServiceControl) -> crate::Result<ServiceStatus> {
1867        let mut raw_status = unsafe { mem::zeroed::<Services::SERVICE_STATUS>() };
1868        let success = unsafe {
1869            Services::ControlService(
1870                self.service_handle.raw_handle(),
1871                command.raw_service_control_type(),
1872                &mut raw_status,
1873            )
1874        };
1875
1876        if success == 0 {
1877            Err(Error::Winapi(io::Error::last_os_error()))
1878        } else {
1879            ServiceStatus::from_raw(raw_status).map_err(|e| Error::ParseValue("service status", e))
1880        }
1881    }
1882
1883    /// Private helper to query the optional configuration parameters of windows services.
1884    unsafe fn query_config2<T: Copy>(&self, kind: u32, data: &mut [u8]) -> io::Result<T> {
1885        let mut bytes_written: u32 = 0;
1886
1887        let success = Services::QueryServiceConfig2W(
1888            self.service_handle.raw_handle(),
1889            kind,
1890            data.as_mut_ptr() as _,
1891            data.len() as u32,
1892            &mut bytes_written,
1893        );
1894
1895        if success == 0 {
1896            Err(io::Error::last_os_error())
1897        } else {
1898            Ok(*(data.as_ptr() as *const _))
1899        }
1900    }
1901
1902    /// Private helper to update the optional configuration parameters of windows services.
1903    unsafe fn change_config2<T>(&self, kind: u32, data: &mut T) -> io::Result<()> {
1904        let success = Services::ChangeServiceConfig2W(
1905            self.service_handle.raw_handle(),
1906            kind,
1907            data as *mut _ as *mut _,
1908        );
1909
1910        if success == 0 {
1911            Err(io::Error::last_os_error())
1912        } else {
1913            Ok(())
1914        }
1915    }
1916}
1917
1918/// The maximum size of data buffer used by QueryServiceConfigW and QueryServiceConfig2W is 8K
1919const MAX_QUERY_BUFFER_SIZE: usize = 8 * 1024;
1920
1921fn to_wide_slice(
1922    s: Option<impl AsRef<OsStr>>,
1923) -> ::std::result::Result<Option<Vec<u16>>, ContainsNul<u16>> {
1924    if let Some(s) = s {
1925        Ok(Some(
1926            WideCString::from_os_str(s).map(|s| s.into_vec_with_nul())?,
1927        ))
1928    } else {
1929        Ok(None)
1930    }
1931}
1932
1933#[derive(Debug)]
1934pub enum ParseRawError {
1935    InvalidInteger(u32),
1936    InvalidIntegerSigned(i32),
1937    InvalidGuid(String),
1938}
1939
1940impl std::error::Error for ParseRawError {}
1941
1942impl std::fmt::Display for ParseRawError {
1943    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1944        match self {
1945            Self::InvalidInteger(u) => {
1946                write!(f, "invalid unsigned integer for the target type: {}", u)
1947            }
1948            Self::InvalidIntegerSigned(i) => {
1949                write!(f, "invalid signed integer for the target type: {}", i)
1950            }
1951            Self::InvalidGuid(guid) => {
1952                write!(f, "invalid GUID value for the target type: {}", guid)
1953            }
1954        }
1955    }
1956}
1957
1958fn string_from_guid(guid: &GUID) -> String {
1959    format!(
1960        "{:8X}-{:4X}-{:4X}-{:2X}{:2X}-{:2X}{:2X}{:2X}{:2X}{:2X}{:2X}",
1961        guid.data1,
1962        guid.data2,
1963        guid.data3,
1964        guid.data4[0],
1965        guid.data4[1],
1966        guid.data4[2],
1967        guid.data4[3],
1968        guid.data4[4],
1969        guid.data4[5],
1970        guid.data4[6],
1971        guid.data4[7]
1972    )
1973}
1974
1975pub(crate) fn to_wide(
1976    s: Option<impl AsRef<OsStr>>,
1977) -> ::std::result::Result<Option<WideCString>, ContainsNul<u16>> {
1978    if let Some(s) = s {
1979        Ok(Some(WideCString::from_os_str(s)?))
1980    } else {
1981        Ok(None)
1982    }
1983}
1984
1985/// Escapes a given string, but also checks it does not contain any null bytes
1986fn escape_wide(s: impl AsRef<OsStr>) -> ::std::result::Result<WideString, ContainsNul<u16>> {
1987    let escaped = shell_escape::escape(Cow::Borrowed(s.as_ref()));
1988    let wide = WideCString::from_os_str(escaped)?;
1989    Ok(wide.to_ustring())
1990}
1991
1992#[cfg(test)]
1993mod tests {
1994    use super::*;
1995
1996    #[test]
1997    fn test_service_group_identifier() {
1998        let dependency = ServiceDependency::from_system_identifier("+network");
1999        assert_eq!(
2000            dependency,
2001            ServiceDependency::Group(OsString::from("network"))
2002        );
2003    }
2004
2005    #[test]
2006    fn test_service_name_identifier() {
2007        let dependency = ServiceDependency::from_system_identifier("netlogon");
2008        assert_eq!(
2009            dependency,
2010            ServiceDependency::Service(OsString::from("netlogon"))
2011        );
2012    }
2013}