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 | |