138 lines
4.8 KiB
Rust
138 lines
4.8 KiB
Rust
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<tokio::runtime::Runtime> = None;
|
|
static mut IFACE_RT_HANDLE: Option<Arc<tokio::runtime::Handle>> = 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<tokio::runtime::Handle> {
|
|
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<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();
|
|
|
|
debug!("PID: {}, User: {}", std::process::id(), std::env::var("USER").unwrap_or("N/A".into()));
|
|
|
|
// Build interfaces
|
|
let f = Factory::new_sync::<ifaces::Data>();
|
|
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(())
|
|
} |