Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,10 +1,11 @@ [package] name = "qsu" -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "0BSD" +# https://crates.io/category_slugs categories = [ "asynchronous" ] keywords = [ "service", "systemd", "winsvc" ] repository = "https://repos.qrnch.tech/pub/qsu" description = "Service subsystem wrapper." rust-version = "1.56" @@ -15,10 +16,14 @@ "www", "build_docs.sh", "Rocket.toml", "rustfmt.toml" ] + +# https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section +[badges] +maintenance = { status = "experimental" } [features] default = ["rt"] clap = ["dep:clap", "dep:itertools"] full = ["clap", "installer", "rocket", "rt", "systemd", "tokio"] @@ -30,24 +35,24 @@ wait-for-debugger = ["dep:dbgtools-win"] [dependencies] apperr = { version = "0.2.0" } async-trait = { version = "0.1.77" } -chrono = { version = "0.4.33" } -clap = { version = "4.4.18", optional = true, features = [ +chrono = { version = "0.4.35" } +clap = { version = "4.5.2", optional = true, features = [ "derive", "env", "string", "wrap_help" ] } -env_logger = { version = "0.11.1" } +env_logger = { version = "0.11.3" } futures = { version = "0.3.30" } itertools = { version = "0.12.1", optional = true } killswitch = { version = "0.4.2" } log = { version = "0.4.20" } parking_lot = { version = "0.12.1" } rocket = { version = "0.5.0", optional = true } sidoc = { version = "0.1.0", optional = true } -tokio = { version = "1.35.1", features = ["sync"] } -time = { version = "0.3.31", features = ["macros"] } +tokio = { version = "1.36.0", features = ["sync"] } +time = { version = "0.3.34", features = ["macros"] } tracing = { version = "0.1.40" } [dependencies.tracing-subscriber] version = "0.3.18" default-features = false @@ -55,12 +60,12 @@ [target.'cfg(target_os = "linux")'.dependencies] sd-notify = { version = "0.4.1", optional = true } [target.'cfg(unix)'.dependencies] -libc = { version = "0.2.152" } -nix = { version = "0.27.1", features = ["pthread", "signal"] } +libc = { version = "0.2.153" } +nix = { version = "0.28.0", features = ["pthread", "signal"] } [target.'cfg(windows)'.dependencies] dbgtools-win = { version = "0.2.1", optional = true } eventlog = { version = "0.2.2" } registry = { version = "1.2.3" } @@ -70,12 +75,12 @@ "Win32_Foundation", "Win32_System_Console" ] } winreg = { version = "0.52.0" } [dev-dependencies] -clap = { version = "4.4.14", features = ["derive", "env", "wrap_help"] } -tokio = { version = "1.35.1", features = ["time"] } +clap = { version = "4.5.2", features = ["derive", "env", "wrap_help"] } +tokio = { version = "1.36.0", features = ["time"] } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] Index: examples/hellosvc-rocket.rs ================================================================== --- examples/hellosvc-rocket.rs +++ examples/hellosvc-rocket.rs @@ -6,12 +6,12 @@ mod procres; use qsu::{ argp::ArgParser, rt::{ - RocketServiceHandler, SrvAppRt, StartState, StopState, SvcEvt, - SvcEvtReader + InitCtx, RocketServiceHandler, RunEnv, SrvAppRt, SvcEvt, SvcEvtReader, + TermCtx } }; use rocket::{Build, Ignite, Rocket}; @@ -23,28 +23,29 @@ #[qsu::async_trait] impl RocketServiceHandler for MyService { async fn init( &mut self, - ss: StartState + ictx: InitCtx ) -> Result>, qsu::AppErr> { tracing::trace!("Running init()"); let mut rockets = vec![]; - ss.report(Some("Building a rocket!".into())); + ictx.report(Some("Building a rocket!".into())); let rocket = rocket::build().mount("/", routes![index]); - ss.report(Some("Pushing a rocket".into())); + ictx.report(Some("Pushing a rocket".into())); rockets.push(rocket); Ok(rockets) } async fn run( &mut self, rockets: Vec>, + _re: &RunEnv, mut set: SvcEvtReader ) -> Result<(), qsu::AppErr> { for rocket in rockets { tokio::task::spawn(async { rocket.launch().await.unwrap(); @@ -78,12 +79,12 @@ } Ok(()) } - async fn shutdown(&mut self, ss: StopState) -> Result<(), qsu::AppErr> { - ss.report(Some(format!("Entered {}", "shutdown").into())); + async fn shutdown(&mut self, tctx: TermCtx) -> Result<(), qsu::AppErr> { + tctx.report(Some(format!("Entered {}", "shutdown").into())); tracing::trace!("Running shutdown()"); Ok(()) } } Index: examples/hellosvc-tokio.rs ================================================================== --- examples/hellosvc-tokio.rs +++ examples/hellosvc-tokio.rs @@ -7,11 +7,12 @@ use std::time::{Duration, Instant}; use qsu::{ argp::ArgParser, rt::{ - SrvAppRt, StartState, StopState, SvcEvt, SvcEvtReader, TokioServiceHandler + InitCtx, RunEnv, SrvAppRt, SvcEvt, SvcEvtReader, TermCtx, + TokioServiceHandler } }; use err::Error; use procres::ProcRes; @@ -19,17 +20,21 @@ struct MyService {} #[qsu::async_trait] impl TokioServiceHandler for MyService { - async fn init(&mut self, ss: StartState) -> Result<(), qsu::AppErr> { - ss.report(Some("Entered init".into())); + async fn init(&mut self, ictx: InitCtx) -> Result<(), qsu::AppErr> { + ictx.report(Some("Entered init".into())); tracing::trace!("Running init()"); Ok(()) } - async fn run(&mut self, mut set: SvcEvtReader) -> Result<(), qsu::AppErr> { + async fn run( + &mut self, + _re: &RunEnv, + mut set: SvcEvtReader + ) -> Result<(), qsu::AppErr> { const SECS: u64 = 30; let mut last_dump = Instant::now() - Duration::from_secs(SECS); loop { if Instant::now() - last_dump > Duration::from_secs(SECS) { log::error!("error"); @@ -72,12 +77,12 @@ } Ok(()) } - async fn shutdown(&mut self, ss: StopState) -> Result<(), qsu::AppErr> { - ss.report(Some(("Entered shutdown".to_string()).into())); + async fn shutdown(&mut self, tctx: TermCtx) -> Result<(), qsu::AppErr> { + tctx.report(Some(("Entered shutdown".to_string()).into())); tracing::trace!("Running shutdown()"); Ok(()) } } Index: examples/hellosvc.rs ================================================================== --- examples/hellosvc.rs +++ examples/hellosvc.rs @@ -10,11 +10,11 @@ }; use qsu::{ argp::ArgParser, rt::{ - ServiceHandler, SrvAppRt, StartState, StopState, SvcEvt, SvcEvtReader + InitCtx, RunEnv, ServiceHandler, SrvAppRt, SvcEvt, SvcEvtReader, TermCtx } }; use err::Error; use procres::ProcRes; @@ -21,17 +21,21 @@ struct MyService {} impl ServiceHandler for MyService { - fn init(&mut self, ss: StartState) -> Result<(), qsu::AppErr> { - ss.report(Some("Entered init".into())); + fn init(&mut self, ictx: InitCtx) -> Result<(), qsu::AppErr> { + ictx.report(Some("Entered init".into())); tracing::trace!("Running init()"); Ok(()) } - fn run(&mut self, mut set: SvcEvtReader) -> Result<(), qsu::AppErr> { + fn run( + &mut self, + _re: &RunEnv, + mut set: SvcEvtReader + ) -> Result<(), qsu::AppErr> { const SECS: u64 = 30; let mut last_dump = Instant::now() - Duration::from_secs(SECS); loop { if Instant::now() - last_dump > Duration::from_secs(SECS) { log::error!("error"); @@ -75,12 +79,12 @@ } Ok(()) } - fn shutdown(&mut self, ss: StopState) -> Result<(), qsu::AppErr> { - ss.report(Some(("Entered shutdown".to_string()).into())); + fn shutdown(&mut self, tctx: TermCtx) -> Result<(), qsu::AppErr> { + tctx.report(Some(("Entered shutdown".to_string()).into())); tracing::trace!("Running shutdown()"); Ok(()) } } Index: src/rt.rs ================================================================== --- src/rt.rs +++ src/rt.rs @@ -127,10 +127,25 @@ use tokio::sync::broadcast; use crate::{err::Error, lumberjack::LumberJack, AppErr}; +/// The run time environment. +/// +/// Can be used by the application callbacks to determine whether it is running +/// as a service. +#[derive(Debug, Clone)] +pub enum RunEnv { + /// Running as a foreground process. + Foreground, + + /// Running as a service. + /// + /// If the service subsystem has named services, this will contain the + /// service name. + Service(Option) +} /// Used to pass an optional message to the service subsystem whenever a /// startup or shutdown checkpoint as been reached. pub enum StateMsg { Ref(&'static str), @@ -170,34 +185,53 @@ fn stopped(&self); } -/// Report startup checkpoints to the service subsystem. -/// -/// An instance of this is handed to the server application through the service -/// handler's `init()` trait method. -pub struct StartState { +/// Context passed to `init()` service application callback. +pub struct InitCtx { + re: RunEnv, sr: Arc, cnt: Arc } -impl StartState { + +impl InitCtx { + /// Return context used to identify whether service application is running as + /// a foreground process or a system service. + pub fn runenv(&self) -> RunEnv { + self.re.clone() + } + + /// Report startup state to the system service manager. + /// + /// For foreground processes and services that do not support startup state + /// notifications this method has no effect. pub fn report(&self, status: Option) { let checkpoint = self.cnt.fetch_add(1, Ordering::SeqCst); self.sr.starting(checkpoint, status); } } -/// Report shutdown checkpoints to the service subsystem. -/// -/// An instance of this is handed to the server application through the service -/// handler's `shutdown()` trait method. -pub struct StopState { + +/// Context passed to `term()` service application callback. +pub struct TermCtx { + re: RunEnv, sr: Arc, cnt: Arc } -impl StopState { + +impl TermCtx { + /// Return context used to identify whether service application is running as + /// a foreground process or a system service. + pub fn runenv(&self) -> RunEnv { + self.re.clone() + } + + /// Report shutdown state to the system service manager. + /// + /// For foreground processes and services that do not support shutdown state + /// notifications this method has no effect. pub fn report(&self, status: Option) { let checkpoint = self.cnt.fetch_add(1, Ordering::SeqCst); self.sr.stopping(checkpoint, status); } } @@ -206,15 +240,15 @@ /// "Synchronous" (non-`async`) server application. /// /// Implement this for an object that wraps a server application that does not /// use an async runtime. pub trait ServiceHandler { - fn init(&mut self, ss: StartState) -> Result<(), AppErr>; + fn init(&mut self, ictx: InitCtx) -> Result<(), AppErr>; - fn run(&mut self, ser: SvcEvtReader) -> Result<(), AppErr>; + fn run(&mut self, re: &RunEnv, ser: SvcEvtReader) -> Result<(), AppErr>; - fn shutdown(&mut self, ss: StopState) -> Result<(), AppErr>; + fn shutdown(&mut self, tctx: TermCtx) -> Result<(), AppErr>; } /// `async` server application built on the tokio runtime. /// @@ -222,15 +256,19 @@ /// tokio as an async runtime. #[cfg(feature = "tokio")] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] #[async_trait] pub trait TokioServiceHandler { - async fn init(&mut self, ss: StartState) -> Result<(), AppErr>; + async fn init(&mut self, ictx: InitCtx) -> Result<(), AppErr>; - async fn run(&mut self, ser: SvcEvtReader) -> Result<(), AppErr>; + async fn run( + &mut self, + re: &RunEnv, + ser: SvcEvtReader + ) -> Result<(), AppErr>; - async fn shutdown(&mut self, ss: StopState) -> Result<(), AppErr>; + async fn shutdown(&mut self, tctx: TermCtx) -> Result<(), AppErr>; } /// Rocket server application handler. /// @@ -262,11 +300,11 @@ /// /// The returned `Rocket`s will be ignited and their shutdown handlers will /// be triggered on shutdown. async fn init( &mut self, - ss: StartState + ictx: InitCtx ) -> Result>, AppErr>; /// Server application main entry point. /// /// If the `init()` trait method returned `Rocket` instances to the @@ -273,14 +311,15 @@ /// qsu runtime it will ignote these and pass them to `run()`. It is the /// responsibility of this method to launch the rockets and await them. async fn run( &mut self, rockets: Vec>, + re: &RunEnv, ser: SvcEvtReader ) -> Result<(), AppErr>; - async fn shutdown(&mut self, ss: StopState) -> Result<(), AppErr>; + async fn shutdown(&mut self, tctx: TermCtx) -> Result<(), AppErr>; } /// Event notifications that originate from the service subsystem that is /// controlling the server application. @@ -387,19 +426,23 @@ tracing::debug!("Running service '{}'", self.svcname); let reporter = Arc::new(systemd::ServiceReporter {}); + let re = RunEnv::Service(Some(self.svcname.clone())); + let res = match st { SrvAppRt::Sync(handler) => { - rttype::sync_main(handler, reporter, None, self.test_mode) + rttype::sync_main(re, handler, reporter, None, self.test_mode) } SrvAppRt::Tokio(rtbldr, handler) => { - rttype::tokio_main(rtbldr, handler, reporter, None) + rttype::tokio_main(rtbldr, re, handler, reporter, None) } #[cfg(feature = "rocket")] - SrvAppRt::Rocket(handler) => rttype::rocket_main(handler, reporter, None) + SrvAppRt::Rocket(handler) => { + rttype::rocket_main(re, handler, reporter, None) + } }; res } @@ -417,22 +460,26 @@ tracing::debug!("Running service '{}'", self.svcname); let reporter = Arc::new(nosvc::ServiceReporter {}); + let re = RunEnv::Foreground; + match st { SrvAppRt::Sync(handler) => { - rttype::sync_main(handler, reporter, None, self.test_mode) + rttype::sync_main(re, handler, reporter, None, self.test_mode) } #[cfg(feature = "tokio")] SrvAppRt::Tokio(rtbldr, handler) => { - rttype::tokio_main(rtbldr, handler, reporter, None) + rttype::tokio_main(rtbldr, re, handler, reporter, None) } #[cfg(feature = "rocket")] - SrvAppRt::Rocket(handler) => rttype::rocket_main(handler, reporter, None) + SrvAppRt::Rocket(handler) => { + rttype::rocket_main(re, handler, reporter, None) + } } } } impl RunCtx { Index: src/rt/rttype/rocket.rs ================================================================== --- src/rt/rttype/rocket.rs +++ src/rt/rttype/rocket.rs @@ -21,29 +21,31 @@ use killswitch::KillSwitch; use crate::{ err::{AppErrors, Error}, rt::{ - signals, RocketServiceHandler, StartState, StateReporter, StopState, - SvcEvt, SvcEvtReader + signals, InitCtx, RocketServiceHandler, RunEnv, StateReporter, SvcEvt, + SvcEvtReader, TermCtx } }; /// Internal "main()" routine for server applications that run one or more /// Rockets as their main application. pub(crate) fn rocket_main( + re: RunEnv, handler: Box, sr: Arc, rx_svcevt: Option> ) -> Result<(), Error> { - rocket::execute(rocket_async_main(handler, sr, rx_svcevt))?; + rocket::execute(rocket_async_main(re, handler, sr, rx_svcevt))?; Ok(()) } async fn rocket_async_main( + re: RunEnv, mut handler: Box, sr: Arc, rx_svcevt: Option> ) -> Result<(), Error> { let ks = KillSwitch::new(); @@ -106,15 +108,16 @@ let mut rx_svcevt2 = rx_svcevt.resubscribe(); let set = Box::new(SvcEvtReader { rx: rx_svcevt }); // Call application's init() method. - let ss = StartState { + let ictx = InitCtx { + re: re.clone(), sr: Arc::clone(&sr), cnt: Arc::new(AtomicU32::new(2)) // 1 is used by the runtime, so start at 2 }; - let (rockets, init_apperr) = match handler.init(ss).await { + let (rockets, init_apperr) = match handler.init(ictx).await { Ok(rockets) => (rockets, None), Err(e) => (Vec::new(), Some(e)) }; // Ignite rockets so we can get Shutdown contexts for each of the instances. @@ -166,11 +169,11 @@ } }); let run_apperr = if init_apperr.is_none() { sr.started(); - handler.run(ignited, *set).await.err() + handler.run(ignited, &re, *set).await.err() } else { None }; @@ -193,15 +196,16 @@ if (ks.finalize().await).is_err() { log::warn!("Attempted to finalize KillSwitch that wasn't triggered yet"); } // Call the application's shutdown() function. - let ss = StopState { + let tctx = TermCtx { + re, sr: Arc::clone(&sr), cnt: Arc::new(AtomicU32::new(2)) // 1 is used by the runtime, so start at 2 }; - let term_apperr = handler.shutdown(ss).await.err(); + let term_apperr = handler.shutdown(tctx).await.err(); // Inform the service subsystem that the the shutdown is complete sr.stopped(); // There can be multiple failures, and we don't want to lose information Index: src/rt/rttype/sync.rs ================================================================== --- src/rt/rttype/sync.rs +++ src/rt/rttype/sync.rs @@ -3,21 +3,22 @@ use tokio::sync::broadcast; use crate::{ err::{AppErrors, Error}, rt::{ - signals, ServiceHandler, StartState, StateReporter, StopState, SvcEvt, - SvcEvtReader + signals, InitCtx, RunEnv, ServiceHandler, StateReporter, SvcEvt, + SvcEvtReader, TermCtx } }; #[cfg(unix)] use crate::rt::signals::SigType; /// Internal "main()" routine for server applications that run plain old /// non-`async` code. pub(crate) fn sync_main( + re: RunEnv, mut handler: Box, sr: Arc, rx_svcevt: Option>, test_mode: bool ) -> Result<(), Error> { @@ -62,21 +63,22 @@ let set = Box::new(SvcEvtReader { rx: rx_svcevt }); // Call server application's init() method, passing along a startup state // reporter object. - let ss = StartState { + let ictx = InitCtx { + re: re.clone(), sr: Arc::clone(&sr), cnt: Arc::new(AtomicU32::new(2)) // 1 is used by the runtime, so start at 2 }; - let init_apperr = handler.init(ss).err(); + let init_apperr = handler.init(ictx).err(); // If init() was successful, set the service's state to "started" and then // call the server application's run() method. let run_apperr = if init_apperr.is_none() { sr.started(); - handler.run(*set).err() + handler.run(&re, *set).err() } else { None }; // Always send the first shutdown checkpoint here. Either init() failed or @@ -83,15 +85,16 @@ // run retuned. Either way, we're shutting down. sr.stopping(1, None); // Call the application's shutdown() function, passing along a shutdown state // reporter object. - let ss = StopState { + let tctx = TermCtx { + re, sr: Arc::clone(&sr), cnt: Arc::new(AtomicU32::new(2)) // 1 is used by the runtime, so start at 2 }; - let term_apperr = handler.shutdown(ss).err(); + let term_apperr = handler.shutdown(tctx).err(); // Inform the service subsystem that the the shutdown is complete sr.stopped(); // There can be multiple failures, and we don't want to lose information Index: src/rt/rttype/tokio.rs ================================================================== --- src/rt/rttype/tokio.rs +++ src/rt/rttype/tokio.rs @@ -3,11 +3,11 @@ use tokio::{runtime, sync::broadcast, task}; use crate::{ err::{AppErrors, Error}, rt::{ - signals, StartState, StateReporter, StopState, SvcEvt, SvcEvtReader, + signals, InitCtx, RunEnv, StateReporter, SvcEvt, SvcEvtReader, TermCtx, TokioServiceHandler } }; use killswitch::KillSwitch; @@ -15,20 +15,21 @@ /// Internal "main()" routine for server applications that run the tokio /// runtime for `async` code. pub(crate) fn tokio_main( rtbldr: Option, + re: RunEnv, handler: Box, sr: Arc, rx_svcevt: Option> ) -> Result<(), Error> { let rt = if let Some(mut bldr) = rtbldr { bldr.build()? } else { tokio::runtime::Runtime::new()? }; - rt.block_on(tokio_async_main(handler, sr, rx_svcevt))?; + rt.block_on(tokio_async_main(re, handler, sr, rx_svcevt))?; Ok(()) } /// The `async` main function for tokio servers. @@ -35,10 +36,11 @@ /// /// If `rx_svcevt` is `Some(_)` it means the channel was created elsewhere /// (implied: The transmitting endpoint lives somewhere else). If it is `None` /// the channel needs to be created. async fn tokio_async_main( + re: RunEnv, mut handler: Box, sr: Arc, rx_svcevt: Option> ) -> Result<(), Error> { let ks = KillSwitch::new(); @@ -99,19 +101,20 @@ }; let set = Box::new(SvcEvtReader { rx: rx_svcevt }); // Call application's init() method. - let ss = StartState { + let ictx = InitCtx { + re: re.clone(), sr: Arc::clone(&sr), cnt: Arc::new(AtomicU32::new(2)) // 1 is used by the runtime, so start at 2 }; - let init_apperr = handler.init(ss).await.err(); + let init_apperr = handler.init(ictx).await.err(); let run_apperr = if init_apperr.is_none() { sr.started(); - handler.run(*set).await.err() + handler.run(&re, *set).await.err() } else { None }; // Always send the first shutdown checkpoint here. Either init() failed or @@ -126,15 +129,16 @@ if (ks.finalize().await).is_err() { log::warn!("Attempted to finalize KillSwitch that wasn't triggered yet"); } // Call the application's shutdown() function. - let ss = StopState { + let tctx = TermCtx { + re, sr: Arc::clone(&sr), cnt: Arc::new(AtomicU32::new(2)) // 1 is used by the runtime, so start at 2 }; - let term_apperr = handler.shutdown(ss).await.err(); + let term_apperr = handler.shutdown(tctx).await.err(); // Inform the service subsystem that the the shutdown is complete sr.stopped(); // There can be multiple failures, and we don't want to lose information Index: src/rt/winsvc.rs ================================================================== --- src/rt/winsvc.rs +++ src/rt/winsvc.rs @@ -34,11 +34,11 @@ use crate::{ err::Error, lumberjack::LumberJack, - rt::{SrvAppRt, SvcEvt} + rt::{RunEnv, SrvAppRt, SvcEvt} }; use super::StateMsg; @@ -147,13 +147,14 @@ // Launch main application thread. // // The server application must be run on its own thread because the service // dispatcher call below will block the thread. + let svcnm = svcname.to_string(); let jh = thread::Builder::new() .name("svcapp".into()) - .spawn(move || srvapp_thread(st, rx_fromsvc))?; + .spawn(move || srvapp_thread(st, svcnm, rx_fromsvc))?; // Register generated `ffi_service_main` with the system and start the // service, blocking this thread until the service is stopped. service_dispatcher::start(svcname, ffi_service_main)?; @@ -165,10 +166,11 @@ } } fn srvapp_thread( st: SrvAppRt, + svcname: String, rx_fromsvc: oneshot::Receiver> ) -> Result<(), Error> { // Wait for the service subsystem to report that it has initialized. // It passes along a channel end-point that can be used to send events to // the service manager. @@ -181,22 +183,24 @@ panic!("Unable to receive handshake"); }; let reporter = Arc::new(ServiceReporter { tx: tx.clone() }); + let re = RunEnv::Service(Some(svcname)); + match st { SrvAppRt::Sync(handler) => { // Don't support test mode when running as a windows service - crate::rt::rttype::sync_main(handler, reporter, Some(rx), false) + crate::rt::rttype::sync_main(re, handler, reporter, Some(rx), false) } #[cfg(feature = "tokio")] SrvAppRt::Tokio(rtbldr, handler) => { - crate::rt::rttype::tokio_main(rtbldr, handler, reporter, Some(rx)) + crate::rt::rttype::tokio_main(rtbldr, re, handler, reporter, Some(rx)) } #[cfg(feature = "rocket")] SrvAppRt::Rocket(handler) => { - crate::rt::rttype::rocket_main(handler, reporter, Some(rx)) + crate::rt::rttype::rocket_main(re, handler, reporter, Some(rx)) } } } Index: tests/apps/mod.rs ================================================================== --- tests/apps/mod.rs +++ tests/apps/mod.rs @@ -1,10 +1,10 @@ use std::sync::Arc; use parking_lot::Mutex; -use qsu::rt::{ServiceHandler, StartState, StopState, SvcEvtReader}; +use qsu::rt::{InitCtx, RunEnv, ServiceHandler, SvcEvtReader, TermCtx}; #[cfg(feature = "tokio")] use qsu::rt::TokioServiceHandler; @@ -68,27 +68,31 @@ } } impl ServiceHandler for MySyncService { - fn init(&mut self, _ss: StartState) -> Result<(), qsu::AppErr> { + fn init(&mut self, _ictx: InitCtx) -> Result<(), qsu::AppErr> { self.visited.lock().init = true; if self.fail.init { Err(Error::hello("From Sync::init()"))?; } Ok(()) } - fn run(&mut self, _set: SvcEvtReader) -> Result<(), qsu::AppErr> { + fn run( + &mut self, + _re: &RunEnv, + _set: SvcEvtReader + ) -> Result<(), qsu::AppErr> { self.visited.lock().run = true; if self.fail.run { Err(Error::hello("From Sync::run()"))?; } Ok(()) } - fn shutdown(&mut self, _ss: StopState) -> Result<(), qsu::AppErr> { + fn shutdown(&mut self, _tctx: TermCtx) -> Result<(), qsu::AppErr> { self.visited.lock().shutdown = true; if self.fail.shutdown { Err(Error::hello("From Sync::shutdown()"))?; } Ok(()) @@ -121,27 +125,31 @@ } #[cfg(feature = "tokio")] #[qsu::async_trait] impl TokioServiceHandler for MyTokioService { - async fn init(&mut self, _ss: StartState) -> Result<(), qsu::AppErr> { + async fn init(&mut self, _ictx: InitCtx) -> Result<(), qsu::AppErr> { self.visited.lock().init = true; if self.fail.init { Err(Error::hello("From Tokio::init()"))?; } Ok(()) } - async fn run(&mut self, _set: SvcEvtReader) -> Result<(), qsu::AppErr> { + async fn run( + &mut self, + _re: &RunEnv, + _set: SvcEvtReader + ) -> Result<(), qsu::AppErr> { self.visited.lock().run = true; if self.fail.run { Err(Error::hello("From Tokio::run()"))?; } Ok(()) } - async fn shutdown(&mut self, _ss: StopState) -> Result<(), qsu::AppErr> { + async fn shutdown(&mut self, _tctx: TermCtx) -> Result<(), qsu::AppErr> { self.visited.lock().shutdown = true; if self.fail.shutdown { Err(Error::hello("From Tokio::shutdown()"))?; } Ok(()) @@ -176,11 +184,11 @@ #[cfg(feature = "rocket")] #[qsu::async_trait] impl RocketServiceHandler for MyRocketService { async fn init( &mut self, - _ss: StartState + _ictx: InitCtx ) -> Result>, qsu::AppErr> { self.visited.lock().init = true; if self.fail.init { Err(Error::hello("From Rocket::init()"))?; } @@ -188,24 +196,25 @@ } async fn run( &mut self, _rockets: Vec>, + _re: &RunEnv, _set: SvcEvtReader ) -> Result<(), qsu::AppErr> { self.visited.lock().run = true; if self.fail.run { Err(Error::hello("From Rocket::run()"))?; } Ok(()) } - async fn shutdown(&mut self, _ss: StopState) -> Result<(), qsu::AppErr> { + async fn shutdown(&mut self, _tctx: TermCtx) -> Result<(), qsu::AppErr> { self.visited.lock().shutdown = true; if self.fail.shutdown { Err(Error::hello("From Rocket::shutdown()"))?; } Ok(()) } } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: www/changelog.md ================================================================== --- www/changelog.md +++ www/changelog.md @@ -1,17 +1,34 @@ # Change log ## [Unreleased] -[Details](/vdiff?from=qsu-0.3.0&to=trunk) +[Details](/vdiff?from=qsu-0.4.0&to=trunk) ### Added ### Changed ### Removed +--- + +## [0.4.0] - 2024-03-22 + +[Details](/vdiff?from=qsu-0.3.0&to=qsu-0.4.0) + +### Added + +- Add a `rt::RunEnv` type that is passed to application handler callbacks, + allowing the application to be information about whether they are being run + as foreground processes or as services. + +### Changed + +- Replace `StartState` and `StopState` with `InitCtx` and `TermCtx`, each of + which also contain a `RunEnv`. + --- ## [0.3.0] - 2024-01-30 [Details](/vdiff?from=qsu-0.2.1&to=qsu-0.3.0)