| 1 | use std::{ |
| 2 | any::{Any, TypeId}, |
| 3 | collections::HashMap, |
| 4 | fmt::{self, Write}, |
| 5 | future::Future, |
| 6 | pin::Pin, |
| 7 | sync::Arc, |
| 8 | }; |
| 9 | |
| 10 | use async_trait::async_trait; |
| 11 | use zbus::message::Flags; |
| 12 | use zbus_names::{InterfaceName, MemberName}; |
| 13 | use zvariant::{DynamicType, OwnedValue, Value}; |
| 14 | |
| 15 | use crate::{ |
| 16 | async_lock::RwLock, fdo, message::Message, object_server::SignalContext, Connection, |
| 17 | ObjectServer, Result, |
| 18 | }; |
| 19 | use tracing::trace; |
| 20 | |
| 21 | /// A helper type returned by [`Interface`] callbacks. |
| 22 | pub enum DispatchResult<'a> { |
| 23 | /// This interface does not support the given method |
| 24 | NotFound, |
| 25 | |
| 26 | /// Retry with [Interface::call_mut]. |
| 27 | /// |
| 28 | /// This is equivalent to NotFound if returned by call_mut. |
| 29 | RequiresMut, |
| 30 | |
| 31 | /// The method was found and will be completed by running this Future |
| 32 | Async(Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>), |
| 33 | } |
| 34 | |
| 35 | impl<'a> DispatchResult<'a> { |
| 36 | /// Helper for creating the Async variant |
| 37 | pub fn new_async<F, T, E>(conn: &'a Connection, msg: &'a Message, f: F) -> Self |
| 38 | where |
| 39 | F: Future<Output = ::std::result::Result<T, E>> + Send + 'a, |
| 40 | T: serde::Serialize + DynamicType + Send + Sync, |
| 41 | E: zbus::DBusError + Send, |
| 42 | { |
| 43 | DispatchResult::Async(Box::pin(async move { |
| 44 | let hdr: Header<'_> = msg.header(); |
| 45 | let ret: Result = f.await; |
| 46 | if !hdr.primary().flags().contains(Flags::NoReplyExpected) { |
| 47 | match ret { |
| 48 | Ok(r) => conn.reply(msg, &r).await, |
| 49 | Err(e) => conn.reply_dbus_error(&hdr, e).await, |
| 50 | } |
| 51 | .map(|_seq: ()| ()) |
| 52 | } else { |
| 53 | trace!("No reply expected for {:?} by the caller." , msg); |
| 54 | Ok(()) |
| 55 | } |
| 56 | })) |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | /// The trait is used to dispatch messages to an interface instance. |
| 61 | /// |
| 62 | /// This trait should be treated as unstable API and compatibility may break in minor |
| 63 | /// version bumps. Because of this and other reasons, it is not recommended to manually implement |
| 64 | /// this trait. The [`crate::dbus_interface`] macro implements it for you. |
| 65 | /// |
| 66 | /// If you have an advanced use case where `dbus_interface` is inadequate, consider using |
| 67 | /// [`crate::MessageStream`] or [`crate::blocking::MessageIterator`] instead. |
| 68 | #[async_trait ] |
| 69 | pub trait Interface: Any + Send + Sync { |
| 70 | /// Return the name of the interface. Ex: "org.foo.MyInterface" |
| 71 | fn name() -> InterfaceName<'static> |
| 72 | where |
| 73 | Self: Sized; |
| 74 | |
| 75 | /// Whether each method call will be handled from a different spawned task. |
| 76 | /// |
| 77 | /// Note: When methods are called from separate tasks, they may not be run in the order in which |
| 78 | /// they were called. |
| 79 | fn spawn_tasks_for_methods(&self) -> bool { |
| 80 | true |
| 81 | } |
| 82 | |
| 83 | /// Get a property value. Returns `None` if the property doesn't exist. |
| 84 | async fn get(&self, property_name: &str) -> Option<fdo::Result<OwnedValue>>; |
| 85 | |
| 86 | /// Return all the properties. |
| 87 | async fn get_all(&self) -> fdo::Result<HashMap<String, OwnedValue>>; |
| 88 | |
| 89 | /// Set a property value. |
| 90 | /// |
| 91 | /// Return [`DispatchResult::NotFound`] if the property doesn't exist, or |
| 92 | /// [`DispatchResult::RequiresMut`] if `set_mut` should be used instead. The default |
| 93 | /// implementation just returns `RequiresMut`. |
| 94 | fn set<'call>( |
| 95 | &'call self, |
| 96 | property_name: &'call str, |
| 97 | value: &'call Value<'_>, |
| 98 | ctxt: &'call SignalContext<'_>, |
| 99 | ) -> DispatchResult<'call> { |
| 100 | let _ = (property_name, value, ctxt); |
| 101 | DispatchResult::RequiresMut |
| 102 | } |
| 103 | |
| 104 | /// Set a property value. |
| 105 | /// |
| 106 | /// Returns `None` if the property doesn't exist. |
| 107 | /// |
| 108 | /// This will only be invoked if `set` returned `RequiresMut`. |
| 109 | async fn set_mut( |
| 110 | &mut self, |
| 111 | property_name: &str, |
| 112 | value: &Value<'_>, |
| 113 | ctxt: &SignalContext<'_>, |
| 114 | ) -> Option<fdo::Result<()>>; |
| 115 | |
| 116 | /// Call a method. |
| 117 | /// |
| 118 | /// Return [`DispatchResult::NotFound`] if the method doesn't exist, or |
| 119 | /// [`DispatchResult::RequiresMut`] if `call_mut` should be used instead. |
| 120 | /// |
| 121 | /// It is valid, though inefficient, for this to always return `RequiresMut`. |
| 122 | fn call<'call>( |
| 123 | &'call self, |
| 124 | server: &'call ObjectServer, |
| 125 | connection: &'call Connection, |
| 126 | msg: &'call Message, |
| 127 | name: MemberName<'call>, |
| 128 | ) -> DispatchResult<'call>; |
| 129 | |
| 130 | /// Call a `&mut self` method. |
| 131 | /// |
| 132 | /// This will only be invoked if `call` returned `RequiresMut`. |
| 133 | fn call_mut<'call>( |
| 134 | &'call mut self, |
| 135 | server: &'call ObjectServer, |
| 136 | connection: &'call Connection, |
| 137 | msg: &'call Message, |
| 138 | name: MemberName<'call>, |
| 139 | ) -> DispatchResult<'call>; |
| 140 | |
| 141 | /// Write introspection XML to the writer, with the given indentation level. |
| 142 | fn introspect_to_writer(&self, writer: &mut dyn Write, level: usize); |
| 143 | } |
| 144 | |
| 145 | /// A type for a reference counted Interface trait-object, with associated run-time details and a |
| 146 | /// manual Debug impl. |
| 147 | #[derive (Clone)] |
| 148 | pub(crate) struct ArcInterface { |
| 149 | pub instance: Arc<RwLock<dyn Interface>>, |
| 150 | pub spawn_tasks_for_methods: bool, |
| 151 | } |
| 152 | |
| 153 | impl ArcInterface { |
| 154 | pub fn new<I>(iface: I) -> Self |
| 155 | where |
| 156 | I: Interface, |
| 157 | { |
| 158 | let spawn_tasks_for_methods: bool = iface.spawn_tasks_for_methods(); |
| 159 | Self { |
| 160 | instance: Arc::new(data:RwLock::new(iface)), |
| 161 | spawn_tasks_for_methods, |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | impl fmt::Debug for ArcInterface { |
| 167 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 168 | fDebugStruct<'_, '_>.debug_struct(name:"Arc<RwLock<dyn Interface>>" ) |
| 169 | .finish_non_exhaustive() |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | // Note: while it is possible to implement this without `unsafe`, it currently requires a helper |
| 174 | // trait with a blanket impl that creates `dyn Any` refs. It's simpler (and more performant) to |
| 175 | // just check the type ID and do the downcast ourself. |
| 176 | // |
| 177 | // See https://github.com/rust-lang/rust/issues/65991 for a rustc feature that will make it |
| 178 | // possible to get a `dyn Any` ref directly from a `dyn Interface` ref; once that is stable, we can |
| 179 | // remove this unsafe code. |
| 180 | impl dyn Interface { |
| 181 | /// Return Any of self |
| 182 | pub(crate) fn downcast_ref<T: Any>(&self) -> Option<&T> { |
| 183 | if <dyn Interface as Any>::type_id(self) == TypeId::of::<T>() { |
| 184 | // SAFETY: If type ID matches, it means object is of type T |
| 185 | Some(unsafe { &*(self as *const dyn Interface as *const T) }) |
| 186 | } else { |
| 187 | None |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | /// Return Any of self |
| 192 | pub(crate) fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> { |
| 193 | if <dyn Interface as Any>::type_id(self) == TypeId::of::<T>() { |
| 194 | // SAFETY: If type ID matches, it means object is of type T |
| 195 | Some(unsafe { &mut *(self as *mut dyn Interface as *mut T) }) |
| 196 | } else { |
| 197 | None |
| 198 | } |
| 199 | } |
| 200 | } |
| 201 | |