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