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 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
28 pub struct ServiceType: u32 {
29 const FILE_SYSTEM_DRIVER = Services::SERVICE_FILE_SYSTEM_DRIVER;
31
32 const KERNEL_DRIVER = Services::SERVICE_KERNEL_DRIVER;
34
35 const OWN_PROCESS = Services::SERVICE_WIN32_OWN_PROCESS;
37
38 const SHARE_PROCESS = Services::SERVICE_WIN32_SHARE_PROCESS;
40
41 const USER_OWN_PROCESS = Services::SERVICE_USER_OWN_PROCESS;
43
44 const USER_SHARE_PROCESS = Services::SERVICE_USER_SHARE_PROCESS;
46
47 const INTERACTIVE_PROCESS = SystemServices::SERVICE_INTERACTIVE_PROCESS;
49 }
50}
51
52bitflags::bitflags! {
53 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
55 pub struct ServiceAccess: u32 {
56 const ALL_ACCESS = Services::SERVICE_ALL_ACCESS;
58
59 const QUERY_STATUS = Services::SERVICE_QUERY_STATUS;
61
62 const START = Services::SERVICE_START;
64
65 const STOP = Services::SERVICE_STOP;
67
68 const PAUSE_CONTINUE = Services::SERVICE_PAUSE_CONTINUE;
70
71 const INTERROGATE = Services::SERVICE_INTERROGATE;
73
74 const QUERY_CONFIG = Services::SERVICE_QUERY_CONFIG;
76
77 const CHANGE_CONFIG = Services::SERVICE_CHANGE_CONFIG;
79
80 const USER_DEFINED_CONTROL = Services::SERVICE_USER_DEFINED_CONTROL;
82
83 const DELETE = FileSystem::DELETE;
85
86 const READ_CONTROL = FileSystem::READ_CONTROL;
88
89 const WRITE_DAC = FileSystem::WRITE_DAC;
91
92 const WRITE_OWNER = FileSystem::WRITE_OWNER;
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
99#[repr(u32)]
100pub enum ServiceStartType {
101 AutoStart = Services::SERVICE_AUTO_START,
103 OnDemand = Services::SERVICE_DEMAND_START,
105 Disabled = Services::SERVICE_DISABLED,
107 SystemStart = Services::SERVICE_SYSTEM_START,
110 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#[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#[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 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#[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct ServiceAction {
229 pub action_type: ServiceActionType,
231
232 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#[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
293pub struct ServiceFailureActions {
294 pub reset_period: ServiceFailureResetPeriod,
297
298 pub reboot_msg: Option<OsString>,
304
305 pub command: Option<OsString>,
311
312 pub actions: Option<Vec<ServiceAction>>,
315}
316
317impl ServiceFailureActions {
318 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
364pub struct ServiceInfo {
365 pub name: OsString,
367
368 pub display_name: OsString,
370
371 pub service_type: ServiceType,
373
374 pub start_type: ServiceStartType,
376
377 pub error_control: ServiceErrorControl,
379
380 pub executable_path: PathBuf,
382
383 pub launch_arguments: Vec<OsString>,
386
387 pub dependencies: Vec<ServiceDependency>,
389
390 pub account_name: Option<OsString>,
394
395 pub account_password: Option<OsString>,
398}
399
400pub(crate) struct RawServiceInfo {
402 pub name: WideCString,
404
405 pub display_name: WideCString,
407
408 pub service_type: u32,
410
411 pub start_type: u32,
413
414 pub error_control: u32,
416
417 pub launch_command: WideCString,
419
420 pub dependencies: Option<WideString>,
422
423 pub account_name: Option<WideCString>,
427
428 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 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 if !service_info.launch_arguments.is_empty() {
452 return Err(Error::LaunchArgumentsNotSupported);
453 }
454
455 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 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
500pub struct ServiceConfig {
501 pub service_type: ServiceType,
503
504 pub start_type: ServiceStartType,
506
507 pub error_control: ServiceErrorControl,
509
510 pub executable_path: PathBuf,
512
513 pub load_order_group: Option<OsString>,
515
516 pub tag_id: u32,
519
520 pub dependencies: Vec<ServiceDependency>,
522
523 pub account_name: Option<OsString>,
529
530 pub display_name: OsString,
532}
533
534impl ServiceConfig {
535 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 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#[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#[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#[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#[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#[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#[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
738fn 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#[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#[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#[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#[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 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#[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 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#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1036pub struct SessionChangeParam {
1037 pub reason: SessionChangeReason,
1038 pub notification: SessionNotification,
1039}
1040
1041impl SessionChangeParam {
1042 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1063#[repr(transparent)]
1064pub struct UserEventCode(u32); impl UserEventCode {
1067 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#[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 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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1229pub enum ServiceExitCode {
1230 Win32(u32),
1231 ServiceSpecific(u32),
1232}
1233
1234impl ServiceExitCode {
1235 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 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
1281 pub struct ServiceControlAccept: u32 {
1282 const NETBIND_CHANGE = Services::SERVICE_ACCEPT_NETBINDCHANGE;
1286
1287 const PARAM_CHANGE = Services::SERVICE_ACCEPT_PARAMCHANGE;
1289
1290 const PAUSE_CONTINUE = Services::SERVICE_ACCEPT_PAUSE_CONTINUE;
1292
1293 const PRESHUTDOWN = Services::SERVICE_ACCEPT_PRESHUTDOWN;
1296
1297 const SHUTDOWN = Services::SERVICE_ACCEPT_SHUTDOWN;
1300
1301 const STOP = Services::SERVICE_ACCEPT_STOP;
1303
1304 const HARDWARE_PROFILE_CHANGE = Services::SERVICE_ACCEPT_HARDWAREPROFILECHANGE;
1308
1309 const POWER_EVENT = Services::SERVICE_ACCEPT_POWEREVENT;
1312
1313 const SESSION_CHANGE = Services::SERVICE_ACCEPT_SESSIONCHANGE;
1316
1317 const TIME_CHANGE = Services::SERVICE_ACCEPT_TIMECHANGE;
1320
1321 const TRIGGER_EVENT = Services::SERVICE_ACCEPT_TRIGGEREVENT;
1324 }
1325}
1326
1327#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1337pub struct ServiceStatus {
1338 pub service_type: ServiceType,
1340
1341 pub current_state: ServiceState,
1343
1344 pub controls_accepted: ServiceControlAccept,
1346
1347 pub exit_code: ServiceExitCode,
1350
1351 pub checkpoint: u32,
1357
1358 pub wait_hint: Duration,
1368
1369 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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1434#[repr(u32)]
1435pub enum ServiceSidType {
1436 None = 0,
1437 Restricted = 3,
1438 Unrestricted = 1,
1439}
1440
1441pub 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 pub fn raw_handle(&self) -> Services::SC_HANDLE {
1457 self.service_handle.raw_handle()
1458 }
1459
1460 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 pub fn stop(&self) -> crate::Result<ServiceStatus> {
1506 self.send_control_command(ServiceControl::Stop)
1507 }
1508
1509 pub fn pause(&self) -> crate::Result<ServiceStatus> {
1525 self.send_control_command(ServiceControl::Pause)
1526 }
1527
1528 pub fn resume(&self) -> crate::Result<ServiceStatus> {
1530 self.send_control_command(ServiceControl::Continue)
1531 }
1532
1533 pub fn notify(&self, code: UserEventCode) -> crate::Result<ServiceStatus> {
1535 self.send_control_command(ServiceControl::UserEvent(code))
1536 }
1537
1538 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 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 pub fn query_config(&self) -> crate::Result<ServiceConfig> {
1576 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 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(), ptr::null_mut(), 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 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 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 pub fn get_config_service_sid_info(&self) -> crate::Result<ServiceSidType> {
1679 let mut data = vec![0u8; u32::BITS as usize / 8];
1680
1681 unsafe { self.query_config2(Services::SERVICE_CONFIG_SERVICE_SID_INFO, &mut data) }
1686 .map_err(Error::Winapi)
1687 }
1688
1689 pub fn set_config_service_sid_info(
1695 &self,
1696 mut service_sid_type: ServiceSidType,
1697 ) -> crate::Result<()> {
1698 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 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 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 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 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 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 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 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 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
1918const 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
1985fn 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}