windows_service/lib.rs
1// Copyright 2017 Amagicom AB.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! A crate that provides facilities for management and implementation of windows services.
10//!
11//! # Implementing windows service
12//!
13//! This section describes the steps of implementing a program that runs as a windows service, for
14//! complete source code of such program take a look at examples folder.
15//!
16//! ## Basics
17//!
18//! Each windows service has to implement a service entry function `fn(argc: u32, argv: *mut *mut
19//! u16)` and register it with the system from the application's `main`.
20//!
21//! This crate provides a handy [`define_windows_service!`] macro to generate a low level
22//! boilerplate for the service entry function that parses input from the system and delegates
23//! handling to user defined higher level function `fn(arguments: Vec<OsString>)`.
24//!
25//! This guide references the low level entry function as `ffi_service_main` and higher
26//! level function as `my_service_main` but it's up to developer how to call them.
27//!
28//! ```rust,no_run
29//! #[macro_use]
30//! extern crate windows_service;
31//!
32//! use std::ffi::OsString;
33//! use windows_service::{Result, service_dispatcher};
34//!
35//! define_windows_service!(ffi_service_main, my_service_main);
36//!
37//! fn my_service_main(arguments: Vec<OsString>) {
38//! // The entry point where execution will start on a background thread after a call to
39//! // `service_dispatcher::start` from `main`.
40//! }
41//!
42//! fn main() -> Result<()> {
43//! // Register generated `ffi_service_main` with the system and start the service, blocking
44//! // this thread until the service is stopped.
45//! service_dispatcher::start("myservice", ffi_service_main)?;
46//! Ok(())
47//! }
48//! ```
49//!
50//! ## Handling service events
51//!
52//! The first thing that a windows service should do early in its lifecycle is to subscribe for
53//! service events such as stop or pause and many other.
54//!
55//! ```rust,no_run
56//! extern crate windows_service;
57//!
58//! use std::ffi::OsString;
59//! use windows_service::service::ServiceControl;
60//! use windows_service::service_control_handler::{self, ServiceControlHandlerResult};
61//! use windows_service::Result;
62//!
63//! fn my_service_main(arguments: Vec<OsString>) {
64//! if let Err(_e) = run_service(arguments) {
65//! // Handle errors in some way.
66//! }
67//! }
68//!
69//! fn run_service(arguments: Vec<OsString>) -> Result<()> {
70//! let event_handler = move |control_event| -> ServiceControlHandlerResult {
71//! match control_event {
72//! ServiceControl::Stop => {
73//! // Handle stop event and return control back to the system.
74//! ServiceControlHandlerResult::NoError
75//! }
76//! // All services must accept Interrogate even if it's a no-op.
77//! ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
78//! _ => ServiceControlHandlerResult::NotImplemented,
79//! }
80//! };
81//!
82//! // Register system service event handler
83//! let status_handle = service_control_handler::register("myservice", event_handler)?;
84//! Ok(())
85//! }
86//! ```
87//!
88//! Please see the corresponding MSDN article that describes how event handler works internally:\
89//! <https://msdn.microsoft.com/en-us/library/windows/desktop/ms685149(v=vs.85).aspx>
90//!
91//! ## Updating service status
92//!
93//! When application that implements a windows service is launched by the system, it's
94//! automatically put in the [`StartPending`] state.
95//!
96//! The application needs to complete the initialization, obtain [`ServiceStatusHandle`] (see
97//! [`service_control_handler::register`]) and transition to [`Running`] state.
98//!
99//! If service has a lengthy initialization, it should immediately tell the system how
100//! much time it needs to complete it, by sending the [`StartPending`] state, time
101//! estimate using [`ServiceStatus::wait_hint`] and increment [`ServiceStatus::checkpoint`] each
102//! time the service completes a step in initialization.
103//!
104//! The system will attempt to kill a service that is not able to transition to [`Running`]
105//! state before the proposed [`ServiceStatus::wait_hint`] expired.
106//!
107//! The same concept applies when transitioning between other pending states and their
108//! corresponding target states.
109//!
110//! Note that it's safe to clone [`ServiceStatusHandle`] and use it from any thread.
111//!
112//! ```rust,no_run
113//! extern crate windows_service;
114//!
115//! use std::ffi::OsString;
116//! use std::time::Duration;
117//! use windows_service::service::{
118//! ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
119//! ServiceType,
120//! };
121//! use windows_service::service_control_handler::{self, ServiceControlHandlerResult};
122//!
123//! fn my_service_main(arguments: Vec<OsString>) {
124//! if let Err(_e) = run_service(arguments) {
125//! // Handle error in some way.
126//! }
127//! }
128//!
129//! fn run_service(arguments: Vec<OsString>) -> windows_service::Result<()> {
130//! let event_handler = move |control_event| -> ServiceControlHandlerResult {
131//! match control_event {
132//! ServiceControl::Stop | ServiceControl::Interrogate => {
133//! ServiceControlHandlerResult::NoError
134//! }
135//! _ => ServiceControlHandlerResult::NotImplemented,
136//! }
137//! };
138//!
139//! // Register system service event handler
140//! let status_handle = service_control_handler::register("my_service_name", event_handler)?;
141//!
142//! let next_status = ServiceStatus {
143//! // Should match the one from system service registry
144//! service_type: ServiceType::OWN_PROCESS,
145//! // The new state
146//! current_state: ServiceState::Running,
147//! // Accept stop events when running
148//! controls_accepted: ServiceControlAccept::STOP,
149//! // Used to report an error when starting or stopping only, otherwise must be zero
150//! exit_code: ServiceExitCode::Win32(0),
151//! // Only used for pending states, otherwise must be zero
152//! checkpoint: 0,
153//! // Only used for pending states, otherwise must be zero
154//! wait_hint: Duration::default(),
155//! // Unused for setting status
156//! process_id: None,
157//! };
158//!
159//! // Tell the system that the service is running now
160//! status_handle.set_service_status(next_status)?;
161//!
162//! // Do some work
163//!
164//! Ok(())
165//! }
166//! ```
167//!
168//! Please refer to the "Service State Transitions" article on MSDN for more info:\
169//! <https://msdn.microsoft.com/en-us/library/windows/desktop/ee126211(v=vs.85).aspx>
170//!
171//! [`ServiceStatusHandle`]: service_control_handler::ServiceStatusHandle
172//! [`ServiceStatus::wait_hint`]: service::ServiceStatus::wait_hint
173//! [`ServiceStatus::checkpoint`]: service::ServiceStatus::checkpoint
174//! [`StartPending`]: service::ServiceState::StartPending
175//! [`Running`]: service::ServiceState::Running
176
177#![cfg(windows)]
178
179pub type Result<T> = std::result::Result<T, Error>;
180
181#[derive(Debug)]
182#[non_exhaustive]
183pub enum Error {
184 /// Kernel drivers do not support launch arguments
185 LaunchArgumentsNotSupported,
186 /// A parse error caused by an invalid raw value
187 ParseValue(&'static str, service::ParseRawError),
188 /// An argument contains a nul byte
189 ArgumentHasNulByte(&'static str),
190 /// An argument array contains a nul byte in element at the given index
191 ArgumentArrayElementHasNulByte(&'static str, usize),
192 /// IO error in winapi call
193 Winapi(std::io::Error),
194}
195
196impl std::error::Error for Error {
197 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
198 match self {
199 Self::ParseValue(_, e) => Some(e),
200 Self::Winapi(e) => Some(e),
201 _ => None,
202 }
203 }
204}
205
206impl std::fmt::Display for Error {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 match self {
209 Self::LaunchArgumentsNotSupported => {
210 write!(f, "kernel drivers do not support launch arguments")
211 }
212 Self::ParseValue(name, _) => write!(f, "invalid {} value", name),
213 Self::ArgumentHasNulByte(name) => write!(f, "{} contains a nul byte", name),
214 Self::ArgumentArrayElementHasNulByte(name, index) => write!(
215 f,
216 "{} contains a nul byte in element at {} index",
217 name, index
218 ),
219 Self::Winapi(_) => write!(f, "IO error in winapi call"),
220 }
221 }
222}
223
224mod sc_handle;
225pub mod service;
226pub mod service_control_handler;
227pub mod service_manager;
228#[macro_use]
229pub mod service_dispatcher;
230
231mod double_nul_terminated;
232mod shell_escape;