use log::{ debug, info, warn, error }; use dbus::{ nonblock::Proxy, Error }; use std::sync::Arc; use clap::clap_app; const DBUS_NAME: &'static str = "org.ddnss.sfs.mc"; fn iface_name(iface: &str) -> String { DBUS_NAME.to_owned() + "." + iface } // #[non_exhaustive] pub enum NativeObject<'proxy> { Daemon(Proxy<'proxy, Arc>), Minecraft(Proxy<'proxy, Arc>), } impl core::fmt::Debug for NativeObject<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "NativeObject::").and( match self { Self::Daemon(_) => f.debug_tuple("Daemon"), Self::Minecraft(_) => f.debug_tuple("Minecraft") }.field(&"_").finish() ) } } impl<'proxy> NativeObject<'proxy> { pub fn new(c: Arc, path: &'proxy str) -> Self { match path { "/" => Self::Daemon(Proxy::<'proxy>::new(DBUS_NAME, path, std::time::Duration::from_secs(5), c)), "/minecraft" => Self::Minecraft(Proxy::<'proxy>::new(DBUS_NAME, path, std::time::Duration::from_secs(5), c)), _ => panic!("Unknown object path {:?}", path) } } fn no_path(method: MD, variant: VD) -> ! { panic!("No method {} on variant {:?}", method, variant) } pub async fn start(&self) -> Result<(), Error> { let proxy = match self { Self::Daemon(p) | Self::Minecraft(p) => p, _ => Self::no_path("start", self) }; proxy.method_call(iface_name("Service"), "Start", ()).await } pub async fn stop(&self) -> Result<(), Error> { let proxy = match self { Self::Daemon(p) | Self::Minecraft(p) => p, _ => Self::no_path("stop", self) }; let res = proxy.method_call(iface_name("Service"), "Stop", ()).await; res.or_else(|err| if proxy.path.to_string() == "/".to_owned() && err.name() == Some("org.freedesktop.DBus.Error.NoReply") && err.message() == Some("Message recipient disconnected from message bus without replying") { Ok(()) } else { Err(err) } ) } pub async fn is_running(&self) -> bool { let proxy = match self { Self::Daemon(p) | Self::Minecraft(p) => p, _ => Self::no_path("is_running", self) }; let res = proxy.method_call(iface_name("Service"), "IsRunning", ()).await; res.map(|data: (bool,)| data.0).or_else(|err| if proxy.path.to_string() == "/".to_string() && err.name() == Some("org.freedesktop.DBus.Error.ServiceUnknown") { Ok(false) } else { Err(err) } ).unwrap() } pub async fn restart(&self) -> Result<(), Error> { let proxy = match self { Self::Daemon(p) | Self::Minecraft(p) => p, _ => Self::no_path("restart", self) }; proxy.method_call(iface_name("Service"), "Restart", ()).await } } #[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(); // TODO: use clap let clapped = clap_app!((clap::crate_name!()) => (version: clap::crate_version!()) (author: &*clap::crate_authors!().split(":").collect::>().join(", ")) (about: clap::crate_description!()) (@setting DeriveDisplayOrder) (@setting SubcommandRequiredElseHelp) (@setting GlobalVersion) (@subcommand start => (about: "Start sfsmcd") ) (@subcommand stop => (about: "Stop sfsmcd") ) (@subcommand restart => (about: "Restart sfsmcd") ) (subcommand: clap::SubCommand::with_name("is-running") .about("Get whether sfsmcd is running") ) (@subcommand minecraft => (about: "The minecraft server service") (@setting DeriveDisplayOrder) (@setting SubcommandRequiredElseHelp) (@subcommand start => (about: "Start minecraft server") ) (@subcommand stop => (about: "Stop minecraft server") ) (@subcommand restart => (about: "Restart minecraft server") ) (subcommand: clap::SubCommand::with_name("is-running") .about("Get whether minecraft server is running") ) ) ).get_matches(); // 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); }); // Get a proxy connection to the daemon let daemon = NativeObject::new(c, "/"); // TODO: // Perform operations info!("The daemon is{} running", if !daemon.is_running().await {"n't"} else {""}); Ok(()) }