use log::{ debug, info, warn, error }; use dbus::{ message::MatchRule, channel::MatchingReceiver, tree::Factory, tree::MethodErr }; use ifaces::Interface; use std::sync::Arc; mod ifaces; const DBUS_NAME: &'static str = "org.ddnss.sfs.mc"; // Interface tokio runtime static mut IFACE_RT: Option = None; static mut IFACE_RT_HANDLE: Option> = None; static IFACE_RT_INIT: std::sync::Once = std::sync::Once::new(); fn iface_name(iface: &str) -> String { DBUS_NAME.to_owned() + "." + iface } fn iface_rt_handle() -> Arc { IFACE_RT_INIT.call_once(|| { unsafe { IFACE_RT = Some(tokio::runtime::Builder::new() .thread_name("iface-runtime-worker") .build() .unwrap() ); IFACE_RT_HANDLE = match &IFACE_RT { Some(iface_rt) => Some(Arc::new(iface_rt .handle() .clone() )), _ => unreachable!() }; } }); unsafe { match &IFACE_RT_HANDLE { Some(iface_rt_handle) => iface_rt_handle.clone(), _ => unreachable!() } } } #[tokio::main] async fn main() -> Result<(), Box> { // env_logger initialization // If running with debug profile and `RUST_LOG` environment variable isn't set, set it to "debug" if cfg!(debug_assertions) && std::env::var_os(env_logger::DEFAULT_FILTER_ENV).is_none() { std::env::set_var(env_logger::DEFAULT_FILTER_ENV, "debug"); } env_logger::init(); debug!("PID: {}, User: {}", std::process::id(), std::env::var("USER").unwrap_or("N/A".into())); // Build interfaces let f = Factory::new_sync::(); let mut iface_map = std::collections::HashMap::new(); iface_map.insert(iface_name("Service"), Arc::new({ use ifaces::Service::*; f.interface(iface_name("Service"), ()) // org_ddnss_sfs_mc_Service::start() .add_m(f.method_sync("Start", (), handle!(m, |data| async move { org_ddnss_sfs_mc_Service::start(&mut *data.write().await) .map(|_| vec![m.msg.method_return()]) .map_err(|err| MethodErr::failed(&err)) }))) // org_ddnss_sfs_mc_Service::stop() .add_m(f.method_sync("Stop", (), handle!(m, |data| async move { org_ddnss_sfs_mc_Service::stop(&mut *data.write().await) .map(|_| vec![m.msg.method_return()]) .map_err(|err| MethodErr::failed(&err)) }))) // org_ddnss_sfs_mc_Service::restart() .add_m(f.method_sync("Restart", (), handle!(m, |data| async move { org_ddnss_sfs_mc_Service::restart(&mut *data.write().await) .map(|_| vec![m.msg.method_return()]) .map_err(|err| MethodErr::failed(&err)) }))) // org_ddnss_sfs_mc_Service::is_running() .add_m(f.method_sync("IsRunning", (), handle!(m, |data| async move { Ok(vec![m.msg.method_return().append1( org_ddnss_sfs_mc_Service::is_running(&*data.read().await) )]) })).out_arg("b")) })); // Add interfaces to object paths in a new tree let tree = f.tree(()) .add(f.object_path(ifaces::Service::DaemonService::path(), ifaces::Service::DaemonService.into()) .introspectable() .add(iface_map[&iface_name("Service")].clone()) ); // Connect with D-Bus let (resource, c) = dbus_tokio::connection::new_system_sync()?; debug!("D-Bus unique name: {}", c.unique_name()); // Spawn a new task for the D-Bus resource tokio::spawn(async move { let err = resource.await; panic!("Lost connection to D-Bus: {}", err); }); // Aquire name on D-Bus match c.request_name(DBUS_NAME, false, true, false).await { Ok(_) => info!("Listening (D-Bus) on {}", DBUS_NAME), Err(err) => { error!("Couldn't request name on D-Bus: {}", err); std::process::exit(1); } }; // Receive method calls c.start_receive(MatchRule::new_method_call(), Box::new(move |msg, c| { use dbus::channel::Sender; if let Some(replies) = tree.handle(&msg) { for r in replies { let _ = c.send(r); } } true })); // Will (afaik) be stabilized in Rust 1.48 // core::future::pending::<()>().await; // Wait until SIGTERM (when systemd stops this service), then shutdown tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())?.recv().await; info!("Got SIGTERM, shutting down"); Ok(()) }