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}