| 1 | //! D-Bus standard interfaces. |
| 2 | //! |
| 3 | //! The D-Bus specification defines the message bus messages and some standard interfaces that may |
| 4 | //! be useful across various D-Bus applications. This module provides their proxy. |
| 5 | |
| 6 | use enumflags2::{bitflags , BitFlags}; |
| 7 | use serde::{Deserialize, Serialize}; |
| 8 | use serde_repr::{Deserialize_repr, Serialize_repr}; |
| 9 | use static_assertions::assert_impl_all; |
| 10 | use std::collections::HashMap; |
| 11 | use zbus_names::{ |
| 12 | BusName, InterfaceName, OwnedBusName, OwnedInterfaceName, OwnedUniqueName, UniqueName, |
| 13 | WellKnownName, |
| 14 | }; |
| 15 | use zvariant::{ |
| 16 | DeserializeDict, ObjectPath, Optional, OwnedObjectPath, OwnedValue, SerializeDict, Type, Value, |
| 17 | }; |
| 18 | |
| 19 | use crate::{ |
| 20 | interface , message::Header, object_server::SignalContext, proxy, DBusError, ObjectServer, |
| 21 | OwnedGuid, |
| 22 | }; |
| 23 | |
| 24 | #[rustfmt::skip] |
| 25 | macro_rules! gen_introspectable_proxy { |
| 26 | ($gen_async:literal, $gen_blocking:literal) => { |
| 27 | /// Proxy for the `org.freedesktop.DBus.Introspectable` interface. |
| 28 | #[proxy( |
| 29 | interface = "org.freedesktop.DBus.Introspectable" , |
| 30 | default_path = "/" , |
| 31 | gen_async = $gen_async, |
| 32 | gen_blocking = $gen_blocking, |
| 33 | )] |
| 34 | trait Introspectable { |
| 35 | /// Returns an XML description of the object, including its interfaces (with signals and |
| 36 | /// methods), objects below it in the object path tree, and its properties. |
| 37 | fn introspect(&self) -> Result<String>; |
| 38 | } |
| 39 | }; |
| 40 | } |
| 41 | |
| 42 | gen_introspectable_proxy!(true, false); |
| 43 | assert_impl_all!(IntrospectableProxy<'_>: Send, Sync, Unpin); |
| 44 | |
| 45 | /// Server-side implementation for the `org.freedesktop.DBus.Introspectable` interface. |
| 46 | /// This interface is implemented automatically for any object registered to the |
| 47 | /// [ObjectServer](crate::ObjectServer). |
| 48 | pub(crate) struct Introspectable; |
| 49 | |
| 50 | #[interface (name = "org.freedesktop.DBus.Introspectable" )] |
| 51 | impl Introspectable { |
| 52 | async fn introspect( |
| 53 | &self, |
| 54 | #[zbus(object_server)] server: &ObjectServer, |
| 55 | #[zbus(header)] header: Header<'_>, |
| 56 | ) -> Result<String> { |
| 57 | let path: &ObjectPath<'_> = header.path().ok_or(err:crate::Error::MissingField)?; |
| 58 | let root: RwLockReadGuard<'_, Node> = server.root().read().await; |
| 59 | let node: &Node = root |
| 60 | .get_child(path) |
| 61 | .ok_or_else(|| Error::UnknownObject(format!("Unknown object ' {path}'" )))?; |
| 62 | |
| 63 | Ok(node.introspect().await) |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | #[rustfmt::skip] |
| 68 | macro_rules! gen_properties_proxy { |
| 69 | ($gen_async:literal, $gen_blocking:literal) => { |
| 70 | /// Proxy for the `org.freedesktop.DBus.Properties` interface. |
| 71 | #[proxy( |
| 72 | interface = "org.freedesktop.DBus.Properties" , |
| 73 | gen_async = $gen_async, |
| 74 | gen_blocking = $gen_blocking, |
| 75 | )] |
| 76 | trait Properties { |
| 77 | /// Get a property value. |
| 78 | async fn get( |
| 79 | &self, |
| 80 | interface_name: InterfaceName<'_>, |
| 81 | property_name: &str, |
| 82 | ) -> Result<OwnedValue>; |
| 83 | |
| 84 | /// Set a property value. |
| 85 | async fn set( |
| 86 | &self, |
| 87 | interface_name: InterfaceName<'_>, |
| 88 | property_name: &str, |
| 89 | value: &Value<'_>, |
| 90 | ) -> Result<()>; |
| 91 | |
| 92 | /// Get all properties. |
| 93 | async fn get_all( |
| 94 | &self, |
| 95 | interface_name: Optional<InterfaceName<'_>>, |
| 96 | ) -> Result<HashMap<String, OwnedValue>>; |
| 97 | |
| 98 | #[zbus(signal)] |
| 99 | async fn properties_changed( |
| 100 | &self, |
| 101 | interface_name: InterfaceName<'_>, |
| 102 | changed_properties: HashMap<&str, Value<'_>>, |
| 103 | invalidated_properties: Vec<&str>, |
| 104 | ) -> Result<()>; |
| 105 | } |
| 106 | }; |
| 107 | } |
| 108 | |
| 109 | gen_properties_proxy!(true, false); |
| 110 | assert_impl_all!(PropertiesProxy<'_>: Send, Sync, Unpin); |
| 111 | |
| 112 | /// Server-side implementation for the `org.freedesktop.DBus.Properties` interface. |
| 113 | /// This interface is implemented automatically for any object registered to the |
| 114 | /// [ObjectServer]. |
| 115 | pub struct Properties; |
| 116 | |
| 117 | assert_impl_all!(Properties: Send, Sync, Unpin); |
| 118 | |
| 119 | #[interface (name = "org.freedesktop.DBus.Properties" )] |
| 120 | impl Properties { |
| 121 | async fn get( |
| 122 | &self, |
| 123 | interface_name: InterfaceName<'_>, |
| 124 | property_name: &str, |
| 125 | #[zbus(object_server)] server: &ObjectServer, |
| 126 | #[zbus(header)] header: Header<'_>, |
| 127 | ) -> Result<OwnedValue> { |
| 128 | let path = header.path().ok_or(crate::Error::MissingField)?; |
| 129 | let root = server.root().read().await; |
| 130 | let iface = root |
| 131 | .get_child(path) |
| 132 | .and_then(|node| node.interface_lock(interface_name.as_ref())) |
| 133 | .ok_or_else(|| { |
| 134 | Error::UnknownInterface(format!("Unknown interface ' {interface_name}'" )) |
| 135 | })?; |
| 136 | |
| 137 | let res = iface.instance.read().await.get(property_name).await; |
| 138 | res.unwrap_or_else(|| { |
| 139 | Err(Error::UnknownProperty(format!( |
| 140 | "Unknown property ' {property_name}'" |
| 141 | ))) |
| 142 | }) |
| 143 | } |
| 144 | |
| 145 | async fn set( |
| 146 | &self, |
| 147 | interface_name: InterfaceName<'_>, |
| 148 | property_name: &str, |
| 149 | value: Value<'_>, |
| 150 | #[zbus(object_server)] server: &ObjectServer, |
| 151 | #[zbus(header)] header: Header<'_>, |
| 152 | #[zbus(signal_context)] ctxt: SignalContext<'_>, |
| 153 | ) -> Result<()> { |
| 154 | let path = header.path().ok_or(crate::Error::MissingField)?; |
| 155 | let root = server.root().read().await; |
| 156 | let iface = root |
| 157 | .get_child(path) |
| 158 | .and_then(|node| node.interface_lock(interface_name.as_ref())) |
| 159 | .ok_or_else(|| { |
| 160 | Error::UnknownInterface(format!("Unknown interface ' {interface_name}'" )) |
| 161 | })?; |
| 162 | |
| 163 | match iface |
| 164 | .instance |
| 165 | .read() |
| 166 | .await |
| 167 | .set(property_name, &value, &ctxt) |
| 168 | { |
| 169 | zbus::object_server::DispatchResult::RequiresMut => {} |
| 170 | zbus::object_server::DispatchResult::NotFound => { |
| 171 | return Err(Error::UnknownProperty(format!( |
| 172 | "Unknown property ' {property_name}'" |
| 173 | ))); |
| 174 | } |
| 175 | zbus::object_server::DispatchResult::Async(f) => { |
| 176 | return f.await.map_err(Into::into); |
| 177 | } |
| 178 | } |
| 179 | let res = iface |
| 180 | .instance |
| 181 | .write() |
| 182 | .await |
| 183 | .set_mut(property_name, &value, &ctxt) |
| 184 | .await; |
| 185 | res.unwrap_or_else(|| { |
| 186 | Err(Error::UnknownProperty(format!( |
| 187 | "Unknown property ' {property_name}'" |
| 188 | ))) |
| 189 | }) |
| 190 | } |
| 191 | |
| 192 | async fn get_all( |
| 193 | &self, |
| 194 | interface_name: InterfaceName<'_>, |
| 195 | #[zbus(object_server)] server: &ObjectServer, |
| 196 | #[zbus(header)] header: Header<'_>, |
| 197 | ) -> Result<HashMap<String, OwnedValue>> { |
| 198 | let path = header.path().ok_or(crate::Error::MissingField)?; |
| 199 | let root = server.root().read().await; |
| 200 | let iface = root |
| 201 | .get_child(path) |
| 202 | .and_then(|node| node.interface_lock(interface_name.as_ref())) |
| 203 | .ok_or_else(|| { |
| 204 | Error::UnknownInterface(format!("Unknown interface ' {interface_name}'" )) |
| 205 | })?; |
| 206 | |
| 207 | let res = iface.instance.read().await.get_all().await?; |
| 208 | Ok(res) |
| 209 | } |
| 210 | |
| 211 | /// Emits the `org.freedesktop.DBus.Properties.PropertiesChanged` signal. |
| 212 | #[zbus(signal)] |
| 213 | #[rustfmt::skip] |
| 214 | pub async fn properties_changed( |
| 215 | ctxt: &SignalContext<'_>, |
| 216 | interface_name: InterfaceName<'_>, |
| 217 | changed_properties: &HashMap<&str, &Value<'_>>, |
| 218 | invalidated_properties: &[&str], |
| 219 | ) -> zbus::Result<()>; |
| 220 | } |
| 221 | |
| 222 | /// The type returned by the [`ObjectManagerProxy::get_managed_objects`] method. |
| 223 | pub type ManagedObjects = |
| 224 | HashMap<OwnedObjectPath, HashMap<OwnedInterfaceName, HashMap<String, OwnedValue>>>; |
| 225 | |
| 226 | #[rustfmt::skip] |
| 227 | macro_rules! gen_object_manager_proxy { |
| 228 | ($gen_async:literal, $gen_blocking:literal) => { |
| 229 | /// Proxy for the `org.freedesktop.DBus.ObjectManager` interface. |
| 230 | /// |
| 231 | /// **NB:** Changes to properties on existing interfaces are not reported using this interface. |
| 232 | /// Please use [`PropertiesProxy::receive_properties_changed`] to monitor changes to properties on |
| 233 | /// objects. |
| 234 | #[proxy( |
| 235 | interface = "org.freedesktop.DBus.ObjectManager" , |
| 236 | gen_async = $gen_async, |
| 237 | gen_blocking = $gen_blocking, |
| 238 | )] |
| 239 | trait ObjectManager { |
| 240 | /// The return value of this method is a dict whose keys are object paths. All returned object |
| 241 | /// paths are children of the object path implementing this interface, i.e. their object paths |
| 242 | /// start with the ObjectManager's object path plus '/'. |
| 243 | /// |
| 244 | /// Each value is a dict whose keys are interfaces names. Each value in this inner dict is the |
| 245 | /// same dict that would be returned by the org.freedesktop.DBus.Properties.GetAll() method for |
| 246 | /// that combination of object path and interface. If an interface has no properties, the empty |
| 247 | /// dict is returned. |
| 248 | fn get_managed_objects(&self) -> Result<ManagedObjects>; |
| 249 | |
| 250 | /// This signal is emitted when either a new object is added or when an existing object gains |
| 251 | /// one or more interfaces. The `interfaces_and_properties` argument contains a map with the |
| 252 | /// interfaces and properties (if any) that have been added to the given object path. |
| 253 | #[zbus(signal)] |
| 254 | fn interfaces_added( |
| 255 | &self, |
| 256 | object_path: ObjectPath<'_>, |
| 257 | interfaces_and_properties: HashMap<&str, HashMap<&str, Value<'_>>>, |
| 258 | ) -> Result<()>; |
| 259 | |
| 260 | /// This signal is emitted whenever an object is removed or it loses one or more interfaces. |
| 261 | /// The `interfaces` parameters contains a list of the interfaces that were removed. |
| 262 | #[zbus(signal)] |
| 263 | fn interfaces_removed( |
| 264 | &self, |
| 265 | object_path: ObjectPath<'_>, |
| 266 | interfaces: Vec<&str>, |
| 267 | ) -> Result<()>; |
| 268 | } |
| 269 | }; |
| 270 | } |
| 271 | |
| 272 | gen_object_manager_proxy!(true, false); |
| 273 | assert_impl_all!(ObjectManagerProxy<'_>: Send, Sync, Unpin); |
| 274 | |
| 275 | /// Service-side [Object Manager][om] interface implementation. |
| 276 | /// |
| 277 | /// The recommended path to add this interface at is the path form of the well-known name of a D-Bus |
| 278 | /// service, or below. For example, if a D-Bus service is available at the well-known name |
| 279 | /// `net.example.ExampleService1`, this interface should typically be registered at |
| 280 | /// `/net/example/ExampleService1`, or below (to allow for multiple object managers in a service). |
| 281 | /// |
| 282 | /// It is supported, but not recommended, to add this interface at the root path, `/`. |
| 283 | /// |
| 284 | /// When added to an `ObjectServer`, `InterfacesAdded` signal is emitted for all the objects under |
| 285 | /// the `path` its added at. You can use this fact to minimize the signal emissions by populating |
| 286 | /// the entire (sub)tree under `path` before registering an object manager. |
| 287 | /// |
| 288 | /// [om]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager |
| 289 | #[derive (Debug, Clone)] |
| 290 | pub struct ObjectManager; |
| 291 | |
| 292 | #[interface (name = "org.freedesktop.DBus.ObjectManager" )] |
| 293 | impl dynObjectManager { |
| 294 | async fn get_managed_objects( |
| 295 | &self, |
| 296 | #[zbus(object_server)] server: &ObjectServer, |
| 297 | #[zbus(header)] header: Header<'_>, |
| 298 | ) -> Result<ManagedObjects> { |
| 299 | let path = header.path().ok_or(crate::Error::MissingField)?; |
| 300 | let root = server.root().read().await; |
| 301 | let node = root |
| 302 | .get_child(path) |
| 303 | .ok_or_else(|| Error::UnknownObject(format!("Unknown object ' {path}'" )))?; |
| 304 | |
| 305 | node.get_managed_objects().await |
| 306 | } |
| 307 | |
| 308 | /// This signal is emitted when either a new object is added or when an existing object gains |
| 309 | /// one or more interfaces. The `interfaces_and_properties` argument contains a map with the |
| 310 | /// interfaces and properties (if any) that have been added to the given object path. |
| 311 | #[zbus(signal)] |
| 312 | pub async fn interfaces_added( |
| 313 | ctxt: &SignalContext<'_>, |
| 314 | object_path: &ObjectPath<'_>, |
| 315 | interfaces_and_properties: &HashMap<InterfaceName<'_>, HashMap<&str, Value<'_>>>, |
| 316 | ) -> zbus::Result<()>; |
| 317 | |
| 318 | /// This signal is emitted whenever an object is removed or it loses one or more interfaces. |
| 319 | /// The `interfaces` parameters contains a list of the interfaces that were removed. |
| 320 | #[zbus(signal)] |
| 321 | pub async fn interfaces_removed( |
| 322 | ctxt: &SignalContext<'_>, |
| 323 | object_path: &ObjectPath<'_>, |
| 324 | interfaces: &[InterfaceName<'_>], |
| 325 | ) -> zbus::Result<()>; |
| 326 | } |
| 327 | |
| 328 | #[rustfmt::skip] |
| 329 | macro_rules! gen_peer_proxy { |
| 330 | ($gen_async:literal, $gen_blocking:literal) => { |
| 331 | /// Proxy for the `org.freedesktop.DBus.Peer` interface. |
| 332 | #[proxy( |
| 333 | interface = "org.freedesktop.DBus.Peer" , |
| 334 | gen_async = $gen_async, |
| 335 | gen_blocking = $gen_blocking, |
| 336 | )] |
| 337 | trait Peer { |
| 338 | /// On receipt, an application should do nothing other than reply as usual. It does not matter |
| 339 | /// which object path a ping is sent to. |
| 340 | fn ping(&self) -> Result<()>; |
| 341 | |
| 342 | /// An application should reply the containing a hex-encoded UUID representing the identity of |
| 343 | /// the machine the process is running on. This UUID must be the same for all processes on a |
| 344 | /// single system at least until that system next reboots. It should be the same across reboots |
| 345 | /// if possible, but this is not always possible to implement and is not guaranteed. It does not |
| 346 | /// matter which object path a GetMachineId is sent to. |
| 347 | fn get_machine_id(&self) -> Result<String>; |
| 348 | } |
| 349 | }; |
| 350 | } |
| 351 | |
| 352 | gen_peer_proxy!(true, false); |
| 353 | assert_impl_all!(PeerProxy<'_>: Send, Sync, Unpin); |
| 354 | |
| 355 | pub(crate) struct Peer; |
| 356 | |
| 357 | /// Server-side implementation for the `org.freedesktop.DBus.Peer` interface. |
| 358 | /// This interface is implemented automatically for any object registered to the |
| 359 | /// [ObjectServer](crate::ObjectServer). |
| 360 | #[interface (name = "org.freedesktop.DBus.Peer" )] |
| 361 | impl Peer { |
| 362 | fn ping(&self) {} |
| 363 | |
| 364 | fn get_machine_id(&self) -> Result<String> { |
| 365 | let mut id: String = match std::fs::read_to_string(path:"/var/lib/dbus/machine-id" ) { |
| 366 | Ok(id: String) => id, |
| 367 | Err(e: Error) => { |
| 368 | if let Ok(id: String) = std::fs::read_to_string(path:"/etc/machine-id" ) { |
| 369 | id |
| 370 | } else { |
| 371 | return Err(Error::IOError(format!( |
| 372 | "Failed to read from /var/lib/dbus/machine-id or /etc/machine-id: {e}" |
| 373 | ))); |
| 374 | } |
| 375 | } |
| 376 | }; |
| 377 | |
| 378 | let len: usize = id.trim_end().len(); |
| 379 | id.truncate(len); |
| 380 | Ok(id) |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | #[rustfmt::skip] |
| 385 | macro_rules! gen_monitoring_proxy { |
| 386 | ($gen_async:literal, $gen_blocking:literal) => { |
| 387 | /// Proxy for the `org.freedesktop.DBus.Monitoring` interface. |
| 388 | #[proxy( |
| 389 | interface = "org.freedesktop.DBus.Monitoring" , |
| 390 | default_service = "org.freedesktop.DBus" , |
| 391 | default_path = "/org/freedesktop/DBus" , |
| 392 | gen_async = $gen_async, |
| 393 | gen_blocking = $gen_blocking, |
| 394 | )] |
| 395 | trait Monitoring { |
| 396 | /// Converts the connection into a monitor connection which can be used as a |
| 397 | /// debugging/monitoring tool. |
| 398 | /// |
| 399 | /// After this call successfully returns, sending any messages on the bus will result |
| 400 | /// in an error. This is why this method takes ownership of `self`, since there is not |
| 401 | /// much use for the proxy anymore. It is highly recommended to convert the underlying |
| 402 | /// [`Connection`] to a [`MessageStream`] and iterate over messages from the stream, |
| 403 | /// after this call. |
| 404 | /// |
| 405 | /// See [the spec] for details on all the implications and caveats. |
| 406 | /// |
| 407 | /// # Arguments |
| 408 | /// |
| 409 | /// * `match_rules` - A list of match rules describing the messages you want to receive. |
| 410 | /// An empty list means you are want to receive all messages going through the bus. |
| 411 | /// * `flags` - This argument is currently unused by the bus. Just pass a `0`. |
| 412 | /// |
| 413 | /// [the spec]: https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-become-monitor |
| 414 | /// [`Connection`]: https://docs.rs/zbus/latest/zbus/connection/struct.Connection.html |
| 415 | /// [`MessageStream`]: https://docs.rs/zbus/latest/zbus/struct.MessageStream.html |
| 416 | fn become_monitor( |
| 417 | self, |
| 418 | match_rules: &[crate::MatchRule<'_>], |
| 419 | flags: u32, |
| 420 | ) -> Result<()>; |
| 421 | } |
| 422 | }; |
| 423 | } |
| 424 | |
| 425 | gen_monitoring_proxy!(true, false); |
| 426 | assert_impl_all!(MonitoringProxy<'_>: Send, Sync, Unpin); |
| 427 | |
| 428 | #[rustfmt::skip] |
| 429 | macro_rules! gen_stats_proxy { |
| 430 | ($gen_async:literal, $gen_blocking:literal) => { |
| 431 | /// Proxy for the `org.freedesktop.DBus.Debug.Stats` interface. |
| 432 | #[proxy( |
| 433 | interface = "org.freedesktop.DBus.Debug.Stats" , |
| 434 | default_service = "org.freedesktop.DBus" , |
| 435 | default_path = "/org/freedesktop/DBus" , |
| 436 | gen_async = $gen_async, |
| 437 | gen_blocking = $gen_blocking, |
| 438 | )] |
| 439 | trait Stats { |
| 440 | /// GetStats (undocumented) |
| 441 | fn get_stats(&self) -> Result<Vec<HashMap<String, OwnedValue>>>; |
| 442 | |
| 443 | /// GetConnectionStats (undocumented) |
| 444 | fn get_connection_stats(&self, name: BusName<'_>) -> Result<Vec<HashMap<String, OwnedValue>>>; |
| 445 | |
| 446 | /// GetAllMatchRules (undocumented) |
| 447 | fn get_all_match_rules(&self) -> |
| 448 | Result< |
| 449 | Vec< |
| 450 | HashMap< |
| 451 | crate::names::OwnedUniqueName, |
| 452 | Vec<crate::OwnedMatchRule>, |
| 453 | > |
| 454 | > |
| 455 | >; |
| 456 | } |
| 457 | }; |
| 458 | } |
| 459 | |
| 460 | gen_stats_proxy!(true, false); |
| 461 | assert_impl_all!(StatsProxy<'_>: Send, Sync, Unpin); |
| 462 | |
| 463 | /// The flags used by the bus [`request_name`] method. |
| 464 | /// |
| 465 | /// [`request_name`]: struct.DBusProxy.html#method.request_name |
| 466 | #[bitflags ] |
| 467 | #[repr (u32)] |
| 468 | #[derive (Type, Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] |
| 469 | pub enum RequestNameFlags { |
| 470 | /// If an application A specifies this flag and succeeds in becoming the owner of the name, and |
| 471 | /// another application B later calls [`request_name`] with the [`ReplaceExisting`] flag, then |
| 472 | /// application A will lose ownership and receive a `org.freedesktop.DBus.NameLost` signal, and |
| 473 | /// application B will become the new owner. If [`AllowReplacement`] is not specified by |
| 474 | /// application A, or [`ReplaceExisting`] is not specified by application B, then application B |
| 475 | /// will not replace application A as the owner. |
| 476 | /// |
| 477 | /// [`ReplaceExisting`]: enum.RequestNameFlags.html#variant.ReplaceExisting |
| 478 | /// [`AllowReplacement`]: enum.RequestNameFlags.html#variant.AllowReplacement |
| 479 | /// [`request_name`]: struct.DBusProxy.html#method.request_name |
| 480 | AllowReplacement = 0x01, |
| 481 | /// Try to replace the current owner if there is one. If this flag is not set the application |
| 482 | /// will only become the owner of the name if there is no current owner. If this flag is set, |
| 483 | /// the application will replace the current owner if the current owner specified |
| 484 | /// [`AllowReplacement`]. |
| 485 | /// |
| 486 | /// [`AllowReplacement`]: enum.RequestNameFlags.html#variant.AllowReplacement |
| 487 | ReplaceExisting = 0x02, |
| 488 | /// Without this flag, if an application requests a name that is already owned, the |
| 489 | /// application will be placed in a queue to own the name when the current owner gives it |
| 490 | /// up. If this flag is given, the application will not be placed in the queue, the |
| 491 | /// request for the name will simply fail. This flag also affects behavior when an |
| 492 | /// application is replaced as name owner; by default the application moves back into the |
| 493 | /// waiting queue, unless this flag was provided when the application became the name |
| 494 | /// owner. |
| 495 | DoNotQueue = 0x04, |
| 496 | } |
| 497 | |
| 498 | assert_impl_all!(RequestNameFlags: Send, Sync, Unpin); |
| 499 | |
| 500 | /// The return code of the [`request_name`] method. |
| 501 | /// |
| 502 | /// [`request_name`]: struct.DBusProxy.html#method.request_name |
| 503 | #[repr (u32)] |
| 504 | #[derive (Deserialize_repr, Serialize_repr, Type, Debug, PartialEq, Eq)] |
| 505 | pub enum RequestNameReply { |
| 506 | /// The caller is now the primary owner of the name, replacing any previous owner. Either the |
| 507 | /// name had no owner before, or the caller specified [`ReplaceExisting`] and the current owner |
| 508 | /// specified [`AllowReplacement`]. |
| 509 | /// |
| 510 | /// [`ReplaceExisting`]: enum.RequestNameFlags.html#variant.ReplaceExisting |
| 511 | /// [`AllowReplacement`]: enum.RequestNameFlags.html#variant.AllowReplacement |
| 512 | PrimaryOwner = 0x01, |
| 513 | /// The name already had an owner, [`DoNotQueue`] was not specified, and either the current |
| 514 | /// owner did not specify [`AllowReplacement`] or the requesting application did not specify |
| 515 | /// [`ReplaceExisting`]. |
| 516 | /// |
| 517 | /// [`DoNotQueue`]: enum.RequestNameFlags.html#variant.DoNotQueue |
| 518 | /// [`ReplaceExisting`]: enum.RequestNameFlags.html#variant.ReplaceExisting |
| 519 | /// [`AllowReplacement`]: enum.RequestNameFlags.html#variant.AllowReplacement |
| 520 | InQueue = 0x02, |
| 521 | /// The name already has an owner, [`DoNotQueue`] was specified, and either |
| 522 | /// [`AllowReplacement`] was not specified by the current owner, or [`ReplaceExisting`] was |
| 523 | /// not specified by the requesting application. |
| 524 | /// |
| 525 | /// [`DoNotQueue`]: enum.RequestNameFlags.html#variant.DoNotQueue |
| 526 | /// [`ReplaceExisting`]: enum.RequestNameFlags.html#variant.ReplaceExisting |
| 527 | /// [`AllowReplacement`]: enum.RequestNameFlags.html#variant.AllowReplacement |
| 528 | Exists = 0x03, |
| 529 | /// The application trying to request ownership of a name is already the owner of it. |
| 530 | AlreadyOwner = 0x04, |
| 531 | } |
| 532 | |
| 533 | assert_impl_all!(RequestNameReply: Send, Sync, Unpin); |
| 534 | |
| 535 | /// The return code of the [`release_name`] method. |
| 536 | /// |
| 537 | /// [`release_name`]: struct.DBusProxy.html#method.release_name |
| 538 | #[repr (u32)] |
| 539 | #[derive (Deserialize_repr, Serialize_repr, Type, Debug, PartialEq, Eq)] |
| 540 | pub enum ReleaseNameReply { |
| 541 | /// The caller has released their claim on the given name. Either the caller was the primary |
| 542 | /// owner of the name, and the name is now unused or taken by somebody waiting in the queue for |
| 543 | /// the name, or the caller was waiting in the queue for the name and has now been removed from |
| 544 | /// the queue. |
| 545 | Released = 0x01, |
| 546 | /// The given name does not exist on this bus. |
| 547 | NonExistent = 0x02, |
| 548 | /// The caller was not the primary owner of this name, and was also not waiting in the queue to |
| 549 | /// own this name. |
| 550 | NotOwner = 0x03, |
| 551 | } |
| 552 | |
| 553 | assert_impl_all!(ReleaseNameReply: Send, Sync, Unpin); |
| 554 | |
| 555 | /// Credentials of a process connected to a bus server. |
| 556 | /// |
| 557 | /// If unable to determine certain credentials (for instance, because the process is not on the same |
| 558 | /// machine as the bus daemon, or because this version of the bus daemon does not support a |
| 559 | /// particular security framework), or if the values of those credentials cannot be represented as |
| 560 | /// documented here, then those credentials are omitted. |
| 561 | /// |
| 562 | /// **Note**: unknown keys, in particular those with "." that are not from the specification, will |
| 563 | /// be ignored. Use your own implementation or contribute your keys here, or in the specification. |
| 564 | #[derive (Debug, Default, DeserializeDict, PartialEq, Eq, SerializeDict, Type)] |
| 565 | #[zvariant(signature = "a{sv}" )] |
| 566 | pub struct ConnectionCredentials { |
| 567 | #[zvariant(rename = "UnixUserID" )] |
| 568 | pub(crate) unix_user_id: Option<u32>, |
| 569 | |
| 570 | #[zvariant(rename = "UnixGroupIDs" )] |
| 571 | pub(crate) unix_group_ids: Option<Vec<u32>>, |
| 572 | |
| 573 | #[zvariant(rename = "ProcessID" )] |
| 574 | pub(crate) process_id: Option<u32>, |
| 575 | |
| 576 | #[zvariant(rename = "WindowsSID" )] |
| 577 | pub(crate) windows_sid: Option<String>, |
| 578 | |
| 579 | #[zvariant(rename = "LinuxSecurityLabel" )] |
| 580 | pub(crate) linux_security_label: Option<Vec<u8>>, |
| 581 | } |
| 582 | |
| 583 | impl ConnectionCredentials { |
| 584 | /// The numeric Unix user ID, as defined by POSIX. |
| 585 | pub fn unix_user_id(&self) -> Option<u32> { |
| 586 | self.unix_user_id |
| 587 | } |
| 588 | |
| 589 | /// The numeric Unix group IDs (including both the primary group and the supplementary groups), |
| 590 | /// as defined by POSIX, in numerically sorted order. This array is either complete or absent: |
| 591 | /// if the message bus is able to determine some but not all of the caller's groups, or if one |
| 592 | /// of the groups is not representable in a UINT32, it must not add this credential to the |
| 593 | /// dictionary. |
| 594 | pub fn unix_group_ids(&self) -> Option<&Vec<u32>> { |
| 595 | self.unix_group_ids.as_ref() |
| 596 | } |
| 597 | |
| 598 | /// Same as [`ConnectionCredentials::unix_group_ids`], but consumes `self` and returns the group |
| 599 | /// IDs Vec. |
| 600 | pub fn into_unix_group_ids(self) -> Option<Vec<u32>> { |
| 601 | self.unix_group_ids |
| 602 | } |
| 603 | |
| 604 | /// The numeric process ID, on platforms that have this concept. On Unix, this is the process ID |
| 605 | /// defined by POSIX. |
| 606 | pub fn process_id(&self) -> Option<u32> { |
| 607 | self.process_id |
| 608 | } |
| 609 | |
| 610 | /// The Windows security identifier in its string form, e.g. |
| 611 | /// `S-1-5-21-3623811015-3361044348-30300820-1013` for a domain or local computer user or |
| 612 | /// "S-1-5-18` for the LOCAL_SYSTEM user. |
| 613 | pub fn windows_sid(&self) -> Option<&String> { |
| 614 | self.windows_sid.as_ref() |
| 615 | } |
| 616 | |
| 617 | /// Same as [`ConnectionCredentials::windows_sid`], but consumes `self` and returns the SID |
| 618 | /// string. |
| 619 | pub fn into_windows_sid(self) -> Option<String> { |
| 620 | self.windows_sid |
| 621 | } |
| 622 | |
| 623 | /// On Linux systems, the security label that would result from the SO_PEERSEC getsockopt call. |
| 624 | /// The array contains the non-zero bytes of the security label in an unspecified |
| 625 | /// ASCII-compatible encoding, followed by a single zero byte. |
| 626 | /// |
| 627 | /// For example, the SELinux context `system_u:system_r:init_t:s0` (a string of length 27) would |
| 628 | /// be encoded as 28 bytes ending with `':', 's', '0', '\x00'` |
| 629 | /// |
| 630 | /// On SELinux systems this is the SELinux context, as output by `ps -Z` or `ls -Z`. Typical |
| 631 | /// values might include `system_u:system_r:init_t:s0`, |
| 632 | /// `unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023`, or |
| 633 | /// `unconfined_u:unconfined_r:chrome_sandbox_t:s0-s0:c0.c1023`. |
| 634 | /// |
| 635 | /// On Smack systems, this is the Smack label. Typical values might include `_`, `*`, `User`, |
| 636 | /// `System` or `System::Shared`. |
| 637 | /// |
| 638 | /// On AppArmor systems, this is the AppArmor context, a composite string encoding the AppArmor |
| 639 | /// label (one or more profiles) and the enforcement mode. Typical values might include |
| 640 | /// `unconfined`, `/usr/bin/firefox (enforce)` or `user1 (complain)`. |
| 641 | pub fn linux_security_label(&self) -> Option<&Vec<u8>> { |
| 642 | self.linux_security_label.as_ref() |
| 643 | } |
| 644 | |
| 645 | /// Same as [`ConnectionCredentials::linux_security_label`], but consumes `self` and returns |
| 646 | /// the security label bytes. |
| 647 | pub fn into_linux_security_label(self) -> Option<Vec<u8>> { |
| 648 | self.linux_security_label |
| 649 | } |
| 650 | |
| 651 | /// Set the numeric Unix user ID, as defined by POSIX. |
| 652 | pub fn set_unix_user_id(mut self, unix_user_id: u32) -> Self { |
| 653 | self.unix_user_id = Some(unix_user_id); |
| 654 | |
| 655 | self |
| 656 | } |
| 657 | |
| 658 | /// Add a numeric Unix group ID. |
| 659 | /// |
| 660 | /// See [`ConnectionCredentials::unix_group_ids`] for more information. |
| 661 | pub fn add_unix_group_id(mut self, unix_group_id: u32) -> Self { |
| 662 | self.unix_group_ids |
| 663 | .get_or_insert_with(Vec::new) |
| 664 | .push(unix_group_id); |
| 665 | |
| 666 | self |
| 667 | } |
| 668 | |
| 669 | /// Set the numeric process ID, on platforms that have this concept. |
| 670 | /// |
| 671 | /// See [`ConnectionCredentials::process_id`] for more information. |
| 672 | pub fn set_process_id(mut self, process_id: u32) -> Self { |
| 673 | self.process_id = Some(process_id); |
| 674 | |
| 675 | self |
| 676 | } |
| 677 | |
| 678 | /// Set the Windows security identifier in its string form. |
| 679 | pub fn set_windows_sid(mut self, windows_sid: String) -> Self { |
| 680 | self.windows_sid = Some(windows_sid); |
| 681 | |
| 682 | self |
| 683 | } |
| 684 | |
| 685 | /// Set the Linux security label. |
| 686 | /// |
| 687 | /// See [`ConnectionCredentials::linux_security_label`] for more information. |
| 688 | pub fn set_linux_security_label(mut self, linux_security_label: Vec<u8>) -> Self { |
| 689 | self.linux_security_label = Some(linux_security_label); |
| 690 | |
| 691 | self |
| 692 | } |
| 693 | } |
| 694 | |
| 695 | #[rustfmt::skip] |
| 696 | macro_rules! gen_dbus_proxy { |
| 697 | ($gen_async:literal, $gen_blocking:literal) => { |
| 698 | /// Proxy for the `org.freedesktop.DBus` interface. |
| 699 | #[proxy( |
| 700 | default_service = "org.freedesktop.DBus" , |
| 701 | default_path = "/org/freedesktop/DBus" , |
| 702 | interface = "org.freedesktop.DBus" , |
| 703 | gen_async = $gen_async, |
| 704 | gen_blocking = $gen_blocking, |
| 705 | )] |
| 706 | trait DBus { |
| 707 | /// Adds a match rule to match messages going through the message bus |
| 708 | #[zbus(name = "AddMatch" )] |
| 709 | fn add_match_rule(&self, rule: crate::MatchRule<'_>) -> Result<()>; |
| 710 | |
| 711 | /// Returns auditing data used by Solaris ADT, in an unspecified binary format. |
| 712 | fn get_adt_audit_session_data(&self, bus_name: BusName<'_>) -> Result<Vec<u8>>; |
| 713 | |
| 714 | /// Returns as many credentials as possible for the process connected to the server. |
| 715 | fn get_connection_credentials( |
| 716 | &self, |
| 717 | bus_name: BusName<'_>, |
| 718 | ) -> Result<ConnectionCredentials>; |
| 719 | |
| 720 | /// Returns the security context used by SELinux, in an unspecified format. |
| 721 | #[zbus(name = "GetConnectionSELinuxSecurityContext" )] |
| 722 | fn get_connection_selinux_security_context( |
| 723 | &self, |
| 724 | bus_name: BusName<'_>, |
| 725 | ) -> Result<Vec<u8>>; |
| 726 | |
| 727 | /// Returns the Unix process ID of the process connected to the server. |
| 728 | #[zbus(name = "GetConnectionUnixProcessID" )] |
| 729 | fn get_connection_unix_process_id(&self, bus_name: BusName<'_>) -> Result<u32>; |
| 730 | |
| 731 | /// Returns the Unix user ID of the process connected to the server. |
| 732 | fn get_connection_unix_user(&self, bus_name: BusName<'_>) -> Result<u32>; |
| 733 | |
| 734 | /// Gets the unique ID of the bus. |
| 735 | fn get_id(&self) -> Result<OwnedGuid>; |
| 736 | |
| 737 | /// Returns the unique connection name of the primary owner of the name given. |
| 738 | fn get_name_owner(&self, name: BusName<'_>) -> Result<OwnedUniqueName>; |
| 739 | |
| 740 | /// Returns the unique name assigned to the connection. |
| 741 | fn hello(&self) -> Result<OwnedUniqueName>; |
| 742 | |
| 743 | /// Returns a list of all names that can be activated on the bus. |
| 744 | fn list_activatable_names(&self) -> Result<Vec<OwnedBusName>>; |
| 745 | |
| 746 | /// Returns a list of all currently-owned names on the bus. |
| 747 | fn list_names(&self) -> Result<Vec<OwnedBusName>>; |
| 748 | |
| 749 | /// List the connections currently queued for a bus name. |
| 750 | fn list_queued_owners(&self, name: WellKnownName<'_>) -> Result<Vec<OwnedUniqueName>>; |
| 751 | |
| 752 | /// Checks if the specified name exists (currently has an owner). |
| 753 | fn name_has_owner(&self, name: BusName<'_>) -> Result<bool>; |
| 754 | |
| 755 | /// Ask the message bus to release the method caller's claim to the given name. |
| 756 | fn release_name(&self, name: WellKnownName<'_>) -> Result<ReleaseNameReply>; |
| 757 | |
| 758 | /// Reload server configuration. |
| 759 | fn reload_config(&self) -> Result<()>; |
| 760 | |
| 761 | /// Removes the first rule that matches. |
| 762 | #[zbus(name = "RemoveMatch" )] |
| 763 | fn remove_match_rule(&self, rule: crate::MatchRule<'_>) -> Result<()>; |
| 764 | |
| 765 | /// Ask the message bus to assign the given name to the method caller. |
| 766 | fn request_name( |
| 767 | &self, |
| 768 | name: WellKnownName<'_>, |
| 769 | flags: BitFlags<RequestNameFlags>, |
| 770 | ) -> Result<RequestNameReply>; |
| 771 | |
| 772 | /// Tries to launch the executable associated with a name (service |
| 773 | /// activation), as an explicit request. |
| 774 | fn start_service_by_name(&self, name: WellKnownName<'_>, flags: u32) -> Result<u32>; |
| 775 | |
| 776 | /// This method adds to or modifies that environment when activating services. |
| 777 | fn update_activation_environment(&self, environment: HashMap<&str, &str>) |
| 778 | -> Result<()>; |
| 779 | |
| 780 | /// This signal indicates that the owner of a name has |
| 781 | /// changed. It's also the signal to use to detect the appearance |
| 782 | /// of new names on the bus. |
| 783 | #[zbus(signal)] |
| 784 | fn name_owner_changed( |
| 785 | &self, |
| 786 | name: BusName<'_>, |
| 787 | old_owner: Optional<UniqueName<'_>>, |
| 788 | new_owner: Optional<UniqueName<'_>>, |
| 789 | ); |
| 790 | |
| 791 | /// This signal is sent to a specific application when it loses ownership of a name. |
| 792 | #[zbus(signal)] |
| 793 | fn name_lost(&self, name: BusName<'_>); |
| 794 | |
| 795 | /// This signal is sent to a specific application when it gains ownership of a name. |
| 796 | #[zbus(signal)] |
| 797 | fn name_acquired(&self, name: BusName<'_>); |
| 798 | |
| 799 | /// This property lists abstract “features” provided by the message bus, and can be used by |
| 800 | /// clients to detect the capabilities of the message bus with which they are communicating. |
| 801 | #[zbus(property)] |
| 802 | fn features(&self) -> Result<Vec<String>>; |
| 803 | |
| 804 | /// This property lists interfaces provided by the `/org/freedesktop/DBus` object, and can be |
| 805 | /// used by clients to detect the capabilities of the message bus with which they are |
| 806 | /// communicating. Unlike the standard Introspectable interface, querying this property does not |
| 807 | /// require parsing XML. This property was added in version 1.11.x of the reference |
| 808 | /// implementation of the message bus. |
| 809 | /// |
| 810 | /// The standard `org.freedesktop.DBus` and `org.freedesktop.DBus.Properties` interfaces are not |
| 811 | /// included in the value of this property, because their presence can be inferred from the fact |
| 812 | /// that a method call on `org.freedesktop.DBus.Properties` asking for properties of |
| 813 | /// `org.freedesktop.DBus` was successful. The standard `org.freedesktop.DBus.Peer` and |
| 814 | /// `org.freedesktop.DBus.Introspectable` interfaces are not included in the value of this |
| 815 | /// property either, because they do not indicate features of the message bus implementation. |
| 816 | #[zbus(property)] |
| 817 | fn interfaces(&self) -> Result<Vec<OwnedInterfaceName>>; |
| 818 | } |
| 819 | }; |
| 820 | } |
| 821 | |
| 822 | gen_dbus_proxy!(true, false); |
| 823 | assert_impl_all!(DBusProxy<'_>: Send, Sync, Unpin); |
| 824 | |
| 825 | /// Errors from <https://gitlab.freedesktop.org/dbus/dbus/-/blob/master/dbus/dbus-protocol.h> |
| 826 | #[derive (Clone, Debug, DBusError, PartialEq)] |
| 827 | #[zbus(prefix = "org.freedesktop.DBus.Error" , impl_display = true)] |
| 828 | #[allow (clippy::upper_case_acronyms)] |
| 829 | pub enum Error { |
| 830 | /// Unknown or fall-through zbus error. |
| 831 | #[zbus(error)] |
| 832 | ZBus(zbus::Error), |
| 833 | |
| 834 | /// A generic error; "something went wrong" - see the error message for more. |
| 835 | Failed(String), |
| 836 | |
| 837 | /// There was not enough memory to complete an operation. |
| 838 | NoMemory(String), |
| 839 | |
| 840 | /// The bus doesn't know how to launch a service to supply the bus name you wanted. |
| 841 | ServiceUnknown(String), |
| 842 | |
| 843 | /// The bus name you referenced doesn't exist (i.e. no application owns it). |
| 844 | NameHasNoOwner(String), |
| 845 | |
| 846 | /// No reply to a message expecting one, usually means a timeout occurred. |
| 847 | NoReply(String), |
| 848 | |
| 849 | /// Something went wrong reading or writing to a socket, for example. |
| 850 | IOError(String), |
| 851 | |
| 852 | /// A D-Bus bus address was malformed. |
| 853 | BadAddress(String), |
| 854 | |
| 855 | /// Requested operation isn't supported (like ENOSYS on UNIX). |
| 856 | NotSupported(String), |
| 857 | |
| 858 | /// Some limited resource is exhausted. |
| 859 | LimitsExceeded(String), |
| 860 | |
| 861 | /// Security restrictions don't allow doing what you're trying to do. |
| 862 | AccessDenied(String), |
| 863 | |
| 864 | /// Authentication didn't work. |
| 865 | AuthFailed(String), |
| 866 | |
| 867 | /// Unable to connect to server (probably caused by ECONNREFUSED on a socket). |
| 868 | NoServer(String), |
| 869 | |
| 870 | /// Certain timeout errors, possibly ETIMEDOUT on a socket. |
| 871 | /// Note that `TimedOut` is used for message reply timeouts. |
| 872 | Timeout(String), |
| 873 | |
| 874 | /// No network access (probably ENETUNREACH on a socket). |
| 875 | NoNetwork(String), |
| 876 | |
| 877 | /// Can't bind a socket since its address is in use (i.e. EADDRINUSE). |
| 878 | AddressInUse(String), |
| 879 | |
| 880 | /// The connection is disconnected and you're trying to use it. |
| 881 | Disconnected(String), |
| 882 | |
| 883 | /// Invalid arguments passed to a method call. |
| 884 | InvalidArgs(String), |
| 885 | |
| 886 | /// Missing file. |
| 887 | FileNotFound(String), |
| 888 | |
| 889 | /// Existing file and the operation you're using does not silently overwrite. |
| 890 | FileExists(String), |
| 891 | |
| 892 | /// Method name you invoked isn't known by the object you invoked it on. |
| 893 | UnknownMethod(String), |
| 894 | |
| 895 | /// Object you invoked a method on isn't known. |
| 896 | UnknownObject(String), |
| 897 | |
| 898 | /// Interface you invoked a method on isn't known by the object. |
| 899 | UnknownInterface(String), |
| 900 | |
| 901 | /// Property you tried to access isn't known by the object. |
| 902 | UnknownProperty(String), |
| 903 | |
| 904 | /// Property you tried to set is read-only. |
| 905 | PropertyReadOnly(String), |
| 906 | |
| 907 | /// Certain timeout errors, e.g. while starting a service. |
| 908 | TimedOut(String), |
| 909 | |
| 910 | /// Tried to remove or modify a match rule that didn't exist. |
| 911 | MatchRuleNotFound(String), |
| 912 | |
| 913 | /// The match rule isn't syntactically valid. |
| 914 | MatchRuleInvalid(String), |
| 915 | |
| 916 | /// While starting a new process, the exec() call failed. |
| 917 | #[zbus(name = "Spawn.ExecFailed" )] |
| 918 | SpawnExecFailed(String), |
| 919 | |
| 920 | /// While starting a new process, the fork() call failed. |
| 921 | #[zbus(name = "Spawn.ForkFailed" )] |
| 922 | SpawnForkFailed(String), |
| 923 | |
| 924 | /// While starting a new process, the child exited with a status code. |
| 925 | #[zbus(name = "Spawn.ChildExited" )] |
| 926 | SpawnChildExited(String), |
| 927 | |
| 928 | /// While starting a new process, the child exited on a signal. |
| 929 | #[zbus(name = "Spawn.ChildSignaled" )] |
| 930 | SpawnChildSignaled(String), |
| 931 | |
| 932 | /// While starting a new process, something went wrong. |
| 933 | #[zbus(name = "Spawn.Failed" )] |
| 934 | SpawnFailed(String), |
| 935 | |
| 936 | /// We failed to setup the environment correctly. |
| 937 | #[zbus(name = "Spawn.FailedToSetup" )] |
| 938 | SpawnFailedToSetup(String), |
| 939 | |
| 940 | /// We failed to setup the config parser correctly. |
| 941 | #[zbus(name = "Spawn.ConfigInvalid" )] |
| 942 | SpawnConfigInvalid(String), |
| 943 | |
| 944 | /// Bus name was not valid. |
| 945 | #[zbus(name = "Spawn.ServiceNotValid" )] |
| 946 | SpawnServiceNotValid(String), |
| 947 | |
| 948 | /// Service file not found in system-services directory. |
| 949 | #[zbus(name = "Spawn.ServiceNotFound" )] |
| 950 | SpawnServiceNotFound(String), |
| 951 | |
| 952 | /// Permissions are incorrect on the setuid helper. |
| 953 | #[zbus(name = "Spawn.PermissionsInvalid" )] |
| 954 | SpawnPermissionsInvalid(String), |
| 955 | |
| 956 | /// Service file invalid (Name, User or Exec missing). |
| 957 | #[zbus(name = "Spawn.FileInvalid" )] |
| 958 | SpawnFileInvalid(String), |
| 959 | |
| 960 | /// There was not enough memory to complete the operation. |
| 961 | #[zbus(name = "Spawn.NoMemory" )] |
| 962 | SpawnNoMemory(String), |
| 963 | |
| 964 | /// Tried to get a UNIX process ID and it wasn't available. |
| 965 | UnixProcessIdUnknown(String), |
| 966 | |
| 967 | /// A type signature is not valid. |
| 968 | InvalidSignature(String), |
| 969 | |
| 970 | /// A file contains invalid syntax or is otherwise broken. |
| 971 | InvalidFileContent(String), |
| 972 | |
| 973 | /// Asked for SELinux security context and it wasn't available. |
| 974 | SELinuxSecurityContextUnknown(String), |
| 975 | |
| 976 | /// Asked for ADT audit data and it wasn't available. |
| 977 | AdtAuditDataUnknown(String), |
| 978 | |
| 979 | /// There's already an object with the requested object path. |
| 980 | ObjectPathInUse(String), |
| 981 | |
| 982 | /// The message meta data does not match the payload. e.g. expected number of file descriptors |
| 983 | /// were not sent over the socket this message was received on. |
| 984 | InconsistentMessage(String), |
| 985 | |
| 986 | /// The message is not allowed without performing interactive authorization, but could have |
| 987 | /// succeeded if an interactive authorization step was allowed. |
| 988 | InteractiveAuthorizationRequired(String), |
| 989 | |
| 990 | /// The connection is not from a container, or the specified container instance does not exist. |
| 991 | NotContainer(String), |
| 992 | } |
| 993 | |
| 994 | assert_impl_all!(Error: Send, Sync, Unpin); |
| 995 | |
| 996 | /// Alias for a `Result` with the error type [`zbus::fdo::Error`]. |
| 997 | /// |
| 998 | /// [`zbus::fdo::Error`]: enum.Error.html |
| 999 | pub type Result<T> = std::result::Result<T, Error>; |
| 1000 | |
| 1001 | #[cfg (test)] |
| 1002 | mod tests { |
| 1003 | use crate::{fdo, message::Message, DBusError, Error}; |
| 1004 | use futures_util::StreamExt; |
| 1005 | use ntest::timeout; |
| 1006 | use test_log::test; |
| 1007 | use tokio::runtime; |
| 1008 | use zbus_names::WellKnownName; |
| 1009 | |
| 1010 | #[test ] |
| 1011 | fn error_from_zerror() { |
| 1012 | let m = Message::method("/" , "foo" ) |
| 1013 | .unwrap() |
| 1014 | .destination(":1.2" ) |
| 1015 | .unwrap() |
| 1016 | .build(&()) |
| 1017 | .unwrap(); |
| 1018 | let m = Message::method_error(&m, "org.freedesktop.DBus.Error.TimedOut" ) |
| 1019 | .unwrap() |
| 1020 | .build(&("so long" )) |
| 1021 | .unwrap(); |
| 1022 | let e: Error = m.into(); |
| 1023 | let e: fdo::Error = e.into(); |
| 1024 | assert_eq!(e, fdo::Error::TimedOut("so long" .to_string()),); |
| 1025 | assert_eq!(e.name(), "org.freedesktop.DBus.Error.TimedOut" ); |
| 1026 | assert_eq!(e.description(), Some("so long" )); |
| 1027 | } |
| 1028 | |
| 1029 | #[test ] |
| 1030 | #[timeout(15000)] |
| 1031 | fn signal() { |
| 1032 | // Multi-threaded scheduler. |
| 1033 | runtime::Runtime::new().unwrap().block_on(test_signal()); |
| 1034 | |
| 1035 | // single-threaded scheduler. |
| 1036 | runtime::Builder::new_current_thread() |
| 1037 | .enable_io() |
| 1038 | .build() |
| 1039 | .unwrap() |
| 1040 | .block_on(test_signal()); |
| 1041 | } |
| 1042 | |
| 1043 | async fn test_signal() { |
| 1044 | let conn = crate::Connection::session().await.unwrap(); |
| 1045 | let proxy = fdo::DBusProxy::new(&conn).await.unwrap(); |
| 1046 | |
| 1047 | // Register a well-known name with the session bus and ensure we get the appropriate |
| 1048 | // signals called for that. |
| 1049 | let well_known = "org.freedesktop.zbus.FdoSignalStreamTest" ; |
| 1050 | let unique_name = conn.unique_name().unwrap(); |
| 1051 | let owner_change_stream = proxy |
| 1052 | .receive_name_owner_changed_with_args(&[(0, well_known), (2, unique_name.as_str())]) |
| 1053 | .await |
| 1054 | .unwrap(); |
| 1055 | |
| 1056 | let name_acquired_stream = proxy |
| 1057 | .receive_name_acquired_with_args(&[(0, well_known)]) |
| 1058 | .await |
| 1059 | .unwrap(); |
| 1060 | let mut stream = owner_change_stream.zip(name_acquired_stream); |
| 1061 | |
| 1062 | let well_known: WellKnownName<'static> = well_known.try_into().unwrap(); |
| 1063 | proxy |
| 1064 | .request_name( |
| 1065 | well_known.as_ref(), |
| 1066 | fdo::RequestNameFlags::ReplaceExisting.into(), |
| 1067 | ) |
| 1068 | .await |
| 1069 | .unwrap(); |
| 1070 | |
| 1071 | let (name_owner_changed, name_acquired) = stream.next().await.unwrap(); |
| 1072 | assert_eq!(name_owner_changed.args().unwrap().name(), &well_known); |
| 1073 | assert_eq!( |
| 1074 | *name_owner_changed |
| 1075 | .args() |
| 1076 | .unwrap() |
| 1077 | .new_owner() |
| 1078 | .as_ref() |
| 1079 | .unwrap(), |
| 1080 | *unique_name |
| 1081 | ); |
| 1082 | assert_eq!(name_acquired.args().unwrap().name(), &well_known); |
| 1083 | |
| 1084 | let result = proxy.release_name(well_known.as_ref()).await.unwrap(); |
| 1085 | assert_eq!(result, fdo::ReleaseNameReply::Released); |
| 1086 | |
| 1087 | let result = proxy.release_name(well_known).await.unwrap(); |
| 1088 | assert_eq!(result, fdo::ReleaseNameReply::NonExistent); |
| 1089 | |
| 1090 | let _stream = proxy |
| 1091 | .receive_features_changed() |
| 1092 | .await |
| 1093 | .filter_map(|changed| async move { |
| 1094 | let v = changed.get().await.ok(); |
| 1095 | dbg!(v) |
| 1096 | }); |
| 1097 | } |
| 1098 | |
| 1099 | #[test ] |
| 1100 | #[timeout(15000)] |
| 1101 | fn no_object_manager_signals_before_hello() { |
| 1102 | use zbus::blocking; |
| 1103 | // We were emitting `InterfacesAdded` signals before `Hello` was called, which is wrong and |
| 1104 | // results in us getting disconnected by the bus. This test case ensures we don't do that |
| 1105 | // and also that the signals are eventually emitted. |
| 1106 | |
| 1107 | // Let's first create an interator to get the signals (it has to be another connection). |
| 1108 | let conn = blocking::Connection::session().unwrap(); |
| 1109 | let mut iterator = blocking::MessageIterator::for_match_rule( |
| 1110 | zbus::MatchRule::builder() |
| 1111 | .msg_type(zbus::MessageType::Signal) |
| 1112 | .interface("org.freedesktop.DBus.ObjectManager" ) |
| 1113 | .unwrap() |
| 1114 | .path("/org/zbus/NoObjectManagerSignalsBeforeHello" ) |
| 1115 | .unwrap() |
| 1116 | .build(), |
| 1117 | &conn, |
| 1118 | None, |
| 1119 | ) |
| 1120 | .unwrap(); |
| 1121 | |
| 1122 | // Now create the service side. |
| 1123 | struct TestObj; |
| 1124 | #[super::interface (name = "org.zbus.TestObj" )] |
| 1125 | impl TestObj { |
| 1126 | #[zbus(property)] |
| 1127 | fn test(&self) -> String { |
| 1128 | "test" .into() |
| 1129 | } |
| 1130 | } |
| 1131 | let _conn = blocking::connection::Builder::session() |
| 1132 | .unwrap() |
| 1133 | .name("org.zbus.NoObjectManagerSignalsBeforeHello" ) |
| 1134 | .unwrap() |
| 1135 | .serve_at("/org/zbus/NoObjectManagerSignalsBeforeHello/Obj" , TestObj) |
| 1136 | .unwrap() |
| 1137 | .serve_at( |
| 1138 | "/org/zbus/NoObjectManagerSignalsBeforeHello" , |
| 1139 | super::ObjectManager, |
| 1140 | ) |
| 1141 | .unwrap() |
| 1142 | .build() |
| 1143 | .unwrap(); |
| 1144 | |
| 1145 | // Let's see if the `InterfacesAdded` signal was emitted. |
| 1146 | let msg = iterator.next().unwrap().unwrap(); |
| 1147 | let signal = super::InterfacesAdded::from_message(msg).unwrap(); |
| 1148 | assert_eq!( |
| 1149 | signal.args().unwrap().interfaces_and_properties, |
| 1150 | vec![( |
| 1151 | "org.zbus.TestObj" , |
| 1152 | vec![("Test" , zvariant::Value::new("test" ))] |
| 1153 | .into_iter() |
| 1154 | .collect() |
| 1155 | )] |
| 1156 | .into_iter() |
| 1157 | .collect() |
| 1158 | ); |
| 1159 | } |
| 1160 | } |
| 1161 | |