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;