169 lines
5.5 KiB
Rust
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(())
|
|
} |