#[cfg(feature = "object-manager")]
use std::collections::HashMap;
use std::{any::Any, marker::PhantomData, sync::Arc};
use crate::{
BoxDBusError, Connection, Error, ObjectPath, Result,
connection::WeakConnection,
fdo,
log::{Instrument, debug, trace, trace_span},
message::{Header, Message},
names::InterfaceName,
runtime::locks::RwLock,
};
#[cfg(feature = "object-manager")]
use crate::{Variant, fdo::ObjectManager};
mod interface;
pub(crate) use interface::ArcInterface;
#[doc(hidden)]
pub use interface::introspect_doc_comment;
pub use interface::{
DispatchResult, Interface, InterfaceDeref, InterfaceDerefMut, InterfaceRef, IntoDBusError,
};
#[deprecated(since = "6.0.0", note = "renamed to `DispatchResult`")]
pub type DispatchResult2<'a> = DispatchResult<'a>;
mod signal_emitter;
pub use signal_emitter::SignalEmitter;
mod dispatch_notifier;
pub use dispatch_notifier::{ResponseDispatchListener, ResponseDispatchNotifier};
mod node;
pub(crate) use node::Node;
#[derive(Debug, Clone)]
pub struct ObjectServer {
conn: WeakConnection,
root: Arc<RwLock<Node>>,
}
impl ObjectServer {
pub(crate) fn new(conn: &Connection) -> Self {
Self {
conn: conn.into(),
root: Arc::new(RwLock::new(Node::new(
"/".try_into().expect("zvariant bug"),
))),
}
}
pub(crate) fn root(&self) -> &RwLock<Node> {
&self.root
}
pub async fn at<'p, P, I>(&self, path: P, iface: I) -> Result<Option<I>>
where
I: Interface,
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
{
let mut iface = Some(iface);
let added = self
.add_interface_with(path, I::name(), || {
ArcInterface::new(iface.take().expect("closure is called at most once"))
})
.await?;
Ok(if added { None } else { iface })
}
pub(crate) async fn add_arc_interface<'p, P>(
&self,
path: P,
name: InterfaceName<'static>,
arc_iface: ArcInterface,
) -> Result<bool>
where
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
{
self.add_interface_with(path, name, || arc_iface).await
}
async fn add_interface_with<'p, P, F>(
&self,
path: P,
name: InterfaceName<'static>,
make: F,
) -> Result<bool>
where
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
F: FnOnce() -> ArcInterface,
{
let path = path.try_into().map_err(Into::into)?;
let mut root = self.root().write().await;
let (node, manager_path) = root.get_child_mut(&path, true);
let node = node.unwrap();
if node.has_interface(&name) {
root.remove_node(&path);
return Ok(false);
}
let added = node.add_arc_interface(name.clone(), make());
debug_assert!(added);
self.interfaces_added(node, name, path, manager_path)
.await?;
Ok(true)
}
pub async fn remove<'p, I, P>(&self, path: P) -> Result<bool>
where
I: Interface,
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
{
self.remove_named(path, I::name()).await
}
pub async fn remove_named<'p, P>(
&self,
path: P,
interface_name: InterfaceName<'static>,
) -> Result<bool>
where
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
{
let path = path.try_into().map_err(Into::into)?;
let mut root = self.root.write().await;
let (node, manager_path) = root.get_child_mut(&path, false);
let manager_path = manager_path.map(ObjectPath::into_owned);
let node = node.ok_or(Error::InterfaceNotFound)?;
if !node.remove_interface(&interface_name) {
return Err(Error::InterfaceNotFound);
}
let destroyed = root.remove_node(&path);
self.interfaces_removed(path, interface_name, manager_path)
.await?;
Ok(destroyed)
}
#[cfg(feature = "object-manager")]
async fn interfaces_added(
&self,
node: &Node,
name: InterfaceName<'static>,
path: ObjectPath<'_>,
manager_path: Option<ObjectPath<'_>>,
) -> Result<()> {
if name == ObjectManager::name() {
let emitter = SignalEmitter::new(&self.connection(), path)?;
let objects = node.get_managed_objects(self, &self.connection()).await?;
for (path, owned_interfaces) in objects {
let interfaces = owned_interfaces
.iter()
.map(|(i, props)| {
let props = props
.iter()
.map(|(k, v)| Ok((k.as_str(), Variant::try_from(v)?)))
.collect::<Result<_>>();
Ok((i.into(), props?))
})
.collect::<Result<_>>()?;
ObjectManager::interfaces_added(&emitter, path.into(), interfaces).await?;
}
} else if let Some(manager_path) = manager_path {
let emitter = SignalEmitter::new(&self.connection(), manager_path)?;
let mut interfaces = HashMap::new();
let owned_props = node
.get_properties(self, &self.connection(), name.clone())
.await?;
let props = owned_props
.iter()
.map(|(k, v)| Ok((k.as_str(), Variant::try_from(v)?)))
.collect::<Result<_>>()?;
interfaces.insert(name, props);
ObjectManager::interfaces_added(&emitter, path, interfaces).await?;
}
Ok(())
}
#[cfg(not(feature = "object-manager"))]
async fn interfaces_added(
&self,
_node: &Node,
_name: InterfaceName<'static>,
_path: ObjectPath<'_>,
_manager_path: Option<ObjectPath<'_>>,
) -> Result<()> {
Ok(())
}
#[cfg(feature = "object-manager")]
async fn interfaces_removed(
&self,
path: ObjectPath<'_>,
interface_name: InterfaceName<'static>,
manager_path: Option<ObjectPath<'static>>,
) -> Result<()> {
if let Some(manager_path) = manager_path {
let ctxt = SignalEmitter::new(&self.connection(), manager_path)?;
ObjectManager::interfaces_removed(&ctxt, path, (&[interface_name]).into()).await?;
}
Ok(())
}
#[cfg(not(feature = "object-manager"))]
async fn interfaces_removed(
&self,
_path: ObjectPath<'_>,
_interface_name: InterfaceName<'static>,
_manager_path: Option<ObjectPath<'static>>,
) -> Result<()> {
Ok(())
}
pub async fn interface<'p, P, I>(&self, path: P) -> Result<InterfaceRef<I>>
where
I: Interface,
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
{
let path = path.try_into().map_err(Into::into)?;
let root = self.root().read().await;
let node = root.get_child(&path).ok_or(Error::InterfaceNotFound)?;
let lock = node
.interface_lock(I::name())
.ok_or(Error::InterfaceNotFound)?
.instance;
<dyn Any>::downcast_ref::<I>(&*lock.read().await).ok_or(Error::InterfaceNotFound)?;
let conn = self.connection();
let emitter = SignalEmitter::new(&conn, path).unwrap().into_owned();
Ok(InterfaceRef {
emitter,
lock,
phantom: PhantomData,
})
}
async fn dispatch_call_to_iface(
&self,
iface: Arc<RwLock<dyn Interface>>,
connection: &Connection,
msg: &Message,
hdr: &Header<'_>,
) -> std::result::Result<(), BoxDBusError> {
let member = hdr
.member()
.ok_or_else(|| fdo::Error::Failed("Missing member".into()))?;
let iface_name = hdr
.interface()
.ok_or_else(|| fdo::Error::Failed("Missing interface".into()))?;
trace!("acquiring read lock on interface `{}`", iface_name);
let read_lock = iface.read().await;
trace!("acquired read lock on interface `{}`", iface_name);
match read_lock.call(self, connection, msg, member.as_ref()) {
DispatchResult::NotFound => {
return Err(fdo::Error::UnknownMethod(format!("Unknown method '{member}'")).into());
}
DispatchResult::Async(f) => {
return f.await;
}
DispatchResult::RequiresMut => {}
}
drop(read_lock);
trace!("acquiring write lock on interface `{}`", iface_name);
let mut write_lock = iface.write().await;
trace!("acquired write lock on interface `{}`", iface_name);
match write_lock.call_mut(self, connection, msg, member.as_ref()) {
DispatchResult::NotFound => {}
DispatchResult::RequiresMut => {}
DispatchResult::Async(f) => {
return f.await;
}
}
drop(write_lock);
Err(fdo::Error::UnknownMethod(format!("Unknown method '{member}'")).into())
}
async fn dispatch_method_call_try(
&self,
connection: &Connection,
msg: &Message,
hdr: &Header<'_>,
) -> std::result::Result<(), BoxDBusError> {
let path = hdr
.path()
.ok_or_else(|| fdo::Error::Failed("Missing object path".into()))?;
let iface_name = hdr
.interface()
.ok_or_else(|| fdo::Error::Failed("Missing interface".into()))?;
let _ = hdr
.member()
.ok_or_else(|| fdo::Error::Failed("Missing member".into()))?;
let (iface, with_spawn) = {
let root = self.root.read().await;
let path = if *iface_name == fdo::Peer::name() {
ObjectPath::from_static_str_unchecked("/")
} else {
path.clone()
};
let node = root
.get_child(&path)
.ok_or_else(|| fdo::Error::UnknownObject(format!("Unknown object '{path}'")))?;
let iface = node.interface_lock(iface_name.as_ref()).ok_or_else(|| {
fdo::Error::UnknownInterface(format!("Unknown interface '{iface_name}'"))
})?;
(iface.instance, iface.spawn_tasks_for_methods)
};
if with_spawn {
let task_name = format!("`{msg}` method dispatcher");
let runtime = connection.runtime().clone();
let connection = connection.clone();
let msg = msg.clone();
let dispatch = async move {
let server = connection.object_server();
let hdr = msg.header();
if let Err(e) = server
.dispatch_call_to_iface(iface, &connection, &msg, &hdr)
.await
{
debug!("Returning error: {}", e);
if let Err(e) = connection.reply_dbus_error(&hdr, e).await {
debug!(
"Error dispatching message. Message: {:?}, error: {:?}",
msg, e
);
}
}
}
.instrument(trace_span!("{}", task_name));
runtime.spawn(task_name, dispatch).detach();
Ok(())
} else {
self.dispatch_call_to_iface(iface, connection, msg, hdr)
.await
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
pub(crate) async fn dispatch_call(&self, msg: &Message, hdr: &Header<'_>) -> Result<()> {
let conn = self.connection();
if let Err(e) = self.dispatch_method_call_try(&conn, msg, hdr).await {
debug!("Returning error: {}", e);
conn.reply_dbus_error(hdr, e).await?;
}
trace!("Handled: {}", msg);
Ok(())
}
pub(crate) fn connection(&self) -> Connection {
self.conn
.upgrade()
.expect("ObjectServer can't exist w/o an associated Connection")
}
}