Complete moving from dbus-crossroads
to dbus::tree
This commit is contained in:
parent
a4bf221c92
commit
f0066c8e76
16
sfsmcd/src/ifaces/errors.rs
Normal file
16
sfsmcd/src/ifaces/errors.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ServiceError {
|
||||||
|
NoPermission,
|
||||||
|
AlreadyStarted,
|
||||||
|
AlreadyStopped
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for ServiceError {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::NoPermission => write!(f, "no permission"),
|
||||||
|
Self::AlreadyStarted => write!(f, "already started"),
|
||||||
|
Self::AlreadyStopped => write!(f, "already stopped")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
116
sfsmcd/src/ifaces/mod.rs
Normal file
116
sfsmcd/src/ifaces/mod.rs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
use dbus::tree::DataType;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub mod traits;
|
||||||
|
mod structs;
|
||||||
|
mod errors;
|
||||||
|
|
||||||
|
pub trait Interface {
|
||||||
|
fn path() -> dbus::Path<'static>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct Data {
|
||||||
|
daemon: Arc<RwLock<structs::DaemonService>>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ObjectPathData {
|
||||||
|
daemon: Option<Arc<RwLock<structs::DaemonService>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectPathData {
|
||||||
|
pub fn daemon(&self) -> Option<Arc<RwLock<structs::DaemonService>>> {
|
||||||
|
self.daemon.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<structs::DaemonService> for ObjectPathData {
|
||||||
|
fn from(d: structs::DaemonService) -> Self {
|
||||||
|
Self {
|
||||||
|
daemon: Some(Arc::new(RwLock::new(d)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataType for Data {
|
||||||
|
type Tree = ();
|
||||||
|
type ObjectPath = ObjectPathData;
|
||||||
|
type Property = ();
|
||||||
|
type Interface = ();
|
||||||
|
type Method = ();
|
||||||
|
type Signal = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_iface_generic {
|
||||||
|
($m:ident, $no_path:ident, $path:ident, |$data:ident: $data_ty:ty| {$($d:tt)+} $($f:tt)+) => {move |$m| tokio::task::block_in_place(move || {
|
||||||
|
// Prepare no-path error
|
||||||
|
let $no_path = dbus::tree::MethodErr::no_path($m.path.get_name());
|
||||||
|
let $path = $m.path.get_name();
|
||||||
|
let $data = $m.path.get_data();
|
||||||
|
// Get data depending on the object path
|
||||||
|
$($d)+
|
||||||
|
// If the object path couldn't be found, the method will return the no-path error
|
||||||
|
|
||||||
|
let f = |$data: $data_ty| $($f)+;
|
||||||
|
|
||||||
|
// Run a Future on the dedicated tokio runtime
|
||||||
|
iface_rt_handle().block_on(f($data))
|
||||||
|
})}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
#[inline]
|
||||||
|
macro_rules! handle_org_ddnss_sfs_mc_Service {
|
||||||
|
($m:ident, |$data:ident| $($f:tt)+) => {
|
||||||
|
$crate::handle_iface_generic!($m, no_path, path, |$data: std::sync::Arc<tokio::sync::RwLock<_>>| {
|
||||||
|
let $data = if path == &DaemonService::path() {
|
||||||
|
$data.daemon().ok_or(no_path)
|
||||||
|
} else {
|
||||||
|
Err(no_path)
|
||||||
|
}?;
|
||||||
|
} $($f)+)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub mod Service {
|
||||||
|
pub use super::{
|
||||||
|
traits::org_ddnss_sfs_mc_Service,
|
||||||
|
structs::DaemonService,
|
||||||
|
errors::ServiceError
|
||||||
|
};
|
||||||
|
pub use crate::handle_org_ddnss_sfs_mc_Service as handle;
|
||||||
|
|
||||||
|
impl super::Interface for DaemonService {
|
||||||
|
fn path() -> dbus::Path<'static> {
|
||||||
|
dbus::Path::new("/").unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DaemonService {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl org_ddnss_sfs_mc_Service for DaemonService {
|
||||||
|
fn start(&mut self) -> Result<tokio::task::JoinHandle<()>, ServiceError> {
|
||||||
|
Err(ServiceError::AlreadyStarted)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_running(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) -> Result<(), ServiceError> {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restart(&mut self) -> Result<tokio::task::JoinHandle<()>, ServiceError> {
|
||||||
|
Err(ServiceError::NoPermission)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
sfsmcd/src/ifaces/structs.rs
Normal file
2
sfsmcd/src/ifaces/structs.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DaemonService;
|
19
sfsmcd/src/ifaces/traits.rs
Normal file
19
sfsmcd/src/ifaces/traits.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use super::errors::ServiceError;
|
||||||
|
pub trait org_ddnss_sfs_mc_Service {
|
||||||
|
|
||||||
|
fn start(&mut self) -> Result<tokio::task::JoinHandle<()>, ServiceError>;
|
||||||
|
fn stop(&mut self) -> Result<(), ServiceError>;
|
||||||
|
fn is_running(&self) -> bool;
|
||||||
|
fn restart(&mut self) -> Result<tokio::task::JoinHandle<()>, ServiceError> {
|
||||||
|
if self.is_running() {
|
||||||
|
self.stop()?;
|
||||||
|
}
|
||||||
|
self.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for (dyn org_ddnss_sfs_mc_Service + 'static) {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "org.ddnss.sfs.mc.Service")
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,51 @@
|
|||||||
use log::{ debug, info, warn, error };
|
use log::{ debug, info, warn, error };
|
||||||
use dbus::{
|
use dbus::{
|
||||||
message::MatchRule,
|
message::MatchRule,
|
||||||
channel::MatchingReceiver
|
channel::MatchingReceiver,
|
||||||
|
tree::Factory,
|
||||||
|
tree::MethodErr
|
||||||
};
|
};
|
||||||
|
use ifaces::Interface;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
mod ifaces;
|
||||||
|
|
||||||
const DBUS_NAME: &'static str = "org.ddnss.sfs.mc";
|
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]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@ -17,6 +58,46 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
debug!("PID: {}, User: {}", std::process::id(), std::env::var("USER").unwrap_or("N/A".into()));
|
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
|
// Connect with D-Bus
|
||||||
let (resource, c) = dbus_tokio::connection::new_system_sync()?;
|
let (resource, c) = dbus_tokio::connection::new_system_sync()?;
|
||||||
debug!("D-Bus unique name: {}", c.unique_name());
|
debug!("D-Bus unique name: {}", c.unique_name());
|
||||||
@ -37,7 +118,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Receive method calls
|
// Receive method calls
|
||||||
c.start_receive(MatchRule::new_method_call(), Box::new(move |msg, conn| {
|
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
|
true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user