ceviche/
lib.rs

1//! ceviche is a wrapper to write a service/daemon.
2//!
3//! At the moment only Windows services are supported. The Service macro is inspired
4//! from the [winservice](https://crates.io/crates/winservice) crate.
5//!
6//! A service implements a service main function and is generated by invoking
7//! the `Service!` macro. The events are sent to the service over the `rx` channel.
8//!
9//! ```rust,ignore
10//!  enum CustomServiceEvent {}
11//!
12//! fn my_service_main(
13//!     rx: mpsc::Receiver<ServiceEvent<CustomServiceEvent>>,
14//!     _tx: mpsc::Sender<ServiceEvent<CustomServiceEvent>>,
15//!     args: Vec<String>,
16//!     standalone_mode: bool) -> u32 {
17//!    loop {
18//!        if let Ok(control_code) = rx.recv() {
19//!            match control_code {
20//!                ServiceEvent::Stop => break,
21//!                _ => (),
22//!            }
23//!        }
24//!    }
25//!    0
26//! }
27//!
28//! Service!("Foobar", my_service_main);
29//! ```
30//!
31//! The Controller is a helper to create, remove, start or stop the service
32//! on the system. ceviche also supports a standalone mode were the service
33//! code runs as a normal executable which can be useful for development and
34//! debugging.
35//!
36//! ```rust,ignore
37//! static SERVICE_NAME: &'static str = "foobar";
38//! static DISPLAY_NAME: &'static str = "FooBar Service";
39//! static DESCRIPTION: &'static str = "This is the FooBar service";
40//!
41//! fn main() {
42//!     let yaml = load_yaml!("cli.yml");
43//!     let app = App::from_yaml(yaml);
44//!     let matches = app.version(crate_version!()).get_matches();
45//!     let cmd = matches.value_of("cmd").unwrap_or("").to_string();
46//!
47//!     let mut controller = Controller::new(SERVICE_NAME, DISPLAY_NAME, DESCRIPTION);
48//!
49//!     match cmd.as_str() {
50//!         "create" => controller.create(),
51//!         "delete" => controller.delete(),
52//!         "start" => controller.start(),
53//!         "stop" => controller.stop(),
54//!         "standalone" => {
55//!             let (tx, rx) = mpsc::channel();
56//!
57//!             ctrlc::set_handler(move || {
58//!                 let _ = tx.send(ServiceEvent::Stop);
59//!             }).expect("Failed to register Ctrl-C handler");
60//!
61//!             my_service_main(rx, vec![], true);
62//!         }
63//!         _ => {
64//!             let _result = controller.register(service_main_wrapper);
65//!         }
66//!     }
67//! }
68//!
69//! ```
70
71#[macro_use]
72extern crate cfg_if;
73
74/// Manages the service on the system.
75pub mod controller;
76pub mod session;
77
78#[cfg(windows)]
79pub use windows_sys;
80
81use self::controller::Session;
82use std::fmt;
83
84/// Service errors
85#[derive(Debug)]
86pub struct Error {
87    pub message: String,
88}
89
90impl From<&str> for Error {
91    fn from(message: &str) -> Self {
92        Error {
93            message: message.to_string(),
94        }
95    }
96}
97
98impl fmt::Display for Error {
99    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
100        write!(f, "{}", self.message,)
101    }
102}
103
104impl std::error::Error for Error {
105    fn description(&self) -> &str {
106        &self.message
107    }
108}
109
110impl Error {
111    pub fn new(message: &str) -> Error {
112        Error {
113            message: String::from(message),
114        }
115    }
116}
117
118/// Events that are sent to the service.
119pub enum ServiceEvent<T> {
120    Continue,
121    Pause,
122    Stop,
123    SessionConnect(Session),
124    SessionDisconnect(Session),
125    SessionRemoteConnect(Session),
126    SessionRemoteDisconnect(Session),
127    SessionLogon(Session),
128    SessionLogoff(Session),
129    SessionLock(Session),
130    SessionUnlock(Session),
131    Custom(T),
132}
133
134impl<T> fmt::Display for ServiceEvent<T> {
135    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
136        match self {
137            ServiceEvent::Continue => write!(f, "Continue"),
138            ServiceEvent::Pause => write!(f, "Pause"),
139            ServiceEvent::Stop => write!(f, "Stop"),
140            ServiceEvent::SessionConnect(id) => write!(f, "SessionConnect({})", id),
141            ServiceEvent::SessionDisconnect(id) => write!(f, "SessionDisconnect({})", id),
142            ServiceEvent::SessionRemoteConnect(id) => write!(f, "SessionRemoteConnect({})", id),
143            ServiceEvent::SessionRemoteDisconnect(id) => {
144                write!(f, "SessionRemoteDisconnect({})", id)
145            }
146            ServiceEvent::SessionLogon(id) => write!(f, "SessionLogon({})", id),
147            ServiceEvent::SessionLogoff(id) => write!(f, "SessionLogoff({})", id),
148            ServiceEvent::SessionLock(id) => write!(f, "SessionLock({})", id),
149            ServiceEvent::SessionUnlock(id) => write!(f, "SessionUnlock({})", id),
150            ServiceEvent::Custom(_) => write!(f, "Custom"),
151        }
152    }
153}