windows_service/service_manager.rs
1use std::ffi::{OsStr, OsString};
2use std::os::windows::ffi::OsStringExt;
3use std::{io, ptr};
4
5use widestring::WideCString;
6use windows_sys::Win32::System::Services;
7
8use crate::sc_handle::ScHandle;
9use crate::service::{to_wide, RawServiceInfo, Service, ServiceAccess, ServiceInfo};
10use crate::{Error, Result};
11
12bitflags::bitflags! {
13 /// Flags describing access permissions for [`ServiceManager`].
14 #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Hash)]
15 pub struct ServiceManagerAccess: u32 {
16 /// Can connect to service control manager.
17 const CONNECT = Services::SC_MANAGER_CONNECT;
18
19 /// Can create services.
20 const CREATE_SERVICE = Services::SC_MANAGER_CREATE_SERVICE;
21
22 /// Can enumerate services or receive notifications.
23 const ENUMERATE_SERVICE = Services::SC_MANAGER_ENUMERATE_SERVICE;
24
25 /// Includes all possible access rights.
26 const ALL_ACCESS = Services::SC_MANAGER_ALL_ACCESS;
27 }
28}
29
30/// Service manager.
31pub struct ServiceManager {
32 manager_handle: ScHandle,
33}
34
35impl ServiceManager {
36 /// Private initializer.
37 ///
38 /// # Arguments
39 ///
40 /// * `machine` - The name of machine. Pass `None` to connect to local machine.
41 /// * `database` - The name of database to connect to. Pass `None` to connect to active
42 /// database.
43 fn new(
44 machine: Option<impl AsRef<OsStr>>,
45 database: Option<impl AsRef<OsStr>>,
46 request_access: ServiceManagerAccess,
47 ) -> Result<Self> {
48 let machine_name =
49 to_wide(machine).map_err(|_| Error::ArgumentHasNulByte("machine name"))?;
50 let database_name =
51 to_wide(database).map_err(|_| Error::ArgumentHasNulByte("database name"))?;
52 let handle = unsafe {
53 Services::OpenSCManagerW(
54 machine_name.map_or(ptr::null(), |s| s.as_ptr()),
55 database_name.map_or(ptr::null(), |s| s.as_ptr()),
56 request_access.bits(),
57 )
58 };
59
60 if handle.is_null() {
61 Err(Error::Winapi(io::Error::last_os_error()))
62 } else {
63 Ok(ServiceManager {
64 manager_handle: unsafe { ScHandle::new(handle) },
65 })
66 }
67 }
68
69 /// Connect to local services database.
70 ///
71 /// # Arguments
72 ///
73 /// * `database` - The name of database to connect to. Pass `None` to connect to active
74 /// database.
75 /// * `request_access` - Desired access permissions.
76 pub fn local_computer(
77 database: Option<impl AsRef<OsStr>>,
78 request_access: ServiceManagerAccess,
79 ) -> Result<Self> {
80 ServiceManager::new(None::<&OsStr>, database, request_access)
81 }
82
83 /// Connect to remote services database.
84 ///
85 /// # Arguments
86 ///
87 /// * `machine` - The name of remote machine.
88 /// * `database` - The name of database to connect to. Pass `None` to connect to active
89 /// database.
90 /// * `request_access` - desired access permissions.
91 pub fn remote_computer(
92 machine: impl AsRef<OsStr>,
93 database: Option<impl AsRef<OsStr>>,
94 request_access: ServiceManagerAccess,
95 ) -> Result<Self> {
96 ServiceManager::new(Some(machine), database, request_access)
97 }
98
99 /// Create a service.
100 ///
101 /// # Arguments
102 ///
103 /// * `service_info` - The service information that will be saved to the system services
104 /// registry.
105 /// * `service_access` - Desired access permissions for the returned [`Service`] instance.
106 ///
107 /// # Example
108 ///
109 /// ```rust,no_run
110 /// use std::ffi::OsString;
111 /// use std::path::PathBuf;
112 /// use windows_service::service::{
113 /// ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceType,
114 /// };
115 /// use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
116 ///
117 /// fn main() -> windows_service::Result<()> {
118 /// let manager =
119 /// ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CREATE_SERVICE)?;
120 ///
121 /// let my_service_info = ServiceInfo {
122 /// name: OsString::from("my_service"),
123 /// display_name: OsString::from("My service"),
124 /// service_type: ServiceType::OWN_PROCESS,
125 /// start_type: ServiceStartType::OnDemand,
126 /// error_control: ServiceErrorControl::Normal,
127 /// executable_path: PathBuf::from(r"C:\path\to\my\service.exe"),
128 /// launch_arguments: vec![],
129 /// dependencies: vec![],
130 /// account_name: None, // run as System
131 /// account_password: None,
132 /// };
133 ///
134 /// let my_service = manager.create_service(&my_service_info, ServiceAccess::QUERY_STATUS)?;
135 /// Ok(())
136 /// }
137 /// ```
138 pub fn create_service(
139 &self,
140 service_info: &ServiceInfo,
141 service_access: ServiceAccess,
142 ) -> Result<Service> {
143 let raw_info = RawServiceInfo::new(service_info)?;
144 let service_handle = unsafe {
145 Services::CreateServiceW(
146 self.manager_handle.raw_handle(),
147 raw_info.name.as_ptr(),
148 raw_info.display_name.as_ptr(),
149 service_access.bits(),
150 raw_info.service_type,
151 raw_info.start_type,
152 raw_info.error_control,
153 raw_info.launch_command.as_ptr(),
154 ptr::null(), // load ordering group
155 ptr::null_mut(), // tag id within the load ordering group
156 raw_info
157 .dependencies
158 .as_ref()
159 .map_or(ptr::null(), |s| s.as_ptr()),
160 raw_info
161 .account_name
162 .as_ref()
163 .map_or(ptr::null(), |s| s.as_ptr()),
164 raw_info
165 .account_password
166 .as_ref()
167 .map_or(ptr::null(), |s| s.as_ptr()),
168 )
169 };
170
171 if service_handle.is_null() {
172 Err(Error::Winapi(io::Error::last_os_error()))
173 } else {
174 Ok(Service::new(unsafe { ScHandle::new(service_handle) }))
175 }
176 }
177
178 /// Open an existing service.
179 ///
180 /// # Arguments
181 ///
182 /// * `name` - The service name.
183 /// * `request_access` - Desired permissions for the returned [`Service`] instance.
184 ///
185 /// # Example
186 ///
187 /// ```rust,no_run
188 /// use windows_service::service::ServiceAccess;
189 /// use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
190 ///
191 /// # fn main() -> windows_service::Result<()> {
192 /// let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;
193 /// let my_service = manager.open_service("my_service", ServiceAccess::QUERY_STATUS)?;
194 /// # Ok(())
195 /// # }
196 /// ```
197 pub fn open_service(
198 &self,
199 name: impl AsRef<OsStr>,
200 request_access: ServiceAccess,
201 ) -> Result<Service> {
202 let service_name = WideCString::from_os_str(name)
203 .map_err(|_| Error::ArgumentHasNulByte("service name"))?;
204 let service_handle = unsafe {
205 Services::OpenServiceW(
206 self.manager_handle.raw_handle(),
207 service_name.as_ptr(),
208 request_access.bits(),
209 )
210 };
211
212 if service_handle.is_null() {
213 Err(Error::Winapi(io::Error::last_os_error()))
214 } else {
215 Ok(Service::new(unsafe { ScHandle::new(service_handle) }))
216 }
217 }
218
219 /// Return the service name given a service display name.
220 ///
221 /// # Arguments
222 ///
223 /// * `name` - A service display name.
224 ///
225 /// # Example
226 ///
227 /// ```rust,no_run
228 /// use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
229 ///
230 /// # fn main() -> windows_service::Result<()> {
231 /// let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;
232 /// let my_service_name = manager.service_name_from_display_name("My Service Display Name")?;
233 /// # Ok(())
234 /// # }
235 /// ```
236 pub fn service_name_from_display_name(
237 &self,
238 display_name: impl AsRef<OsStr>,
239 ) -> Result<OsString> {
240 let service_display_name = WideCString::from_os_str(display_name)
241 .map_err(|_| Error::ArgumentHasNulByte("display name"))?;
242
243 // As per docs, the maximum size of data buffer used by GetServiceKeyNameW is 4k bytes,
244 // which is 2k wchars
245 let mut buffer = [0u16; 2 * 1024];
246 let mut buffer_len = u32::try_from(buffer.len()).expect("size must fit in u32");
247
248 let result = unsafe {
249 Services::GetServiceKeyNameW(
250 self.manager_handle.raw_handle(),
251 service_display_name.as_ptr(),
252 buffer.as_mut_ptr(),
253 &mut buffer_len,
254 )
255 };
256
257 if result == 0 {
258 Err(Error::Winapi(io::Error::last_os_error()))
259 } else {
260 Ok(OsString::from_wide(
261 &buffer[..usize::try_from(buffer_len).unwrap()],
262 ))
263 }
264 }
265}