sfsmc/sfsmcctl/src/main.rs

169 lines
5.5 KiB
Rust

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<dbus::nonblock::SyncConnection>>),
Minecraft(Proxy<'proxy, Arc<dbus::nonblock::SyncConnection>>),
}
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<dbus::nonblock::SyncConnection>, 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<MD: core::fmt::Display, VD: core::fmt::Debug>(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<dyn std::error::Error>> {
// 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::<Vec<_>>().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(())
}