zbus/fdo.rs
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
6use enumflags2::{bitflags, BitFlags};
7use serde::{Deserialize, Serialize};
8use serde_repr::{Deserialize_repr, Serialize_repr};
9use static_assertions::assert_impl_all;
10use std::collections::HashMap;
11use zbus_names::{
12 BusName, InterfaceName, OwnedBusName, OwnedInterfaceName, OwnedUniqueName, UniqueName,
13 WellKnownName,
14};
15use zvariant::{
16 DeserializeDict, ObjectPath, Optional, OwnedObjectPath, OwnedValue, SerializeDict, Type, Value,
17};
18
19use crate::{
20 interface, message::Header, object_server::SignalContext, proxy, DBusError, ObjectServer,
21 OwnedGuid,
22};
23
24#[rustfmt::skip]
25macro_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
42gen_introspectable_proxy!(true, false);
43assert_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).
48pub(crate) struct Introspectable;
49
50#[interface(name = "org.freedesktop.DBus.Introspectable")]
51impl Introspectable {
52 async fn introspect(
53 &self,
54 #[zbus(object_server)] server: &ObjectServer,
55 #[zbus(header)] header: Header<'_>,
56 ) -> Result<String> {
57 let path = header.path().ok_or(crate::Error::MissingField)?;
58 let root = server.root().read().await;
59 let 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]
68macro_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
109gen_properties_proxy!(true, false);
110assert_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].
115pub struct Properties;
116
117assert_impl_all!(Properties: Send, Sync, Unpin);
118
119#[interface(name = "org.freedesktop.DBus.Properties")]
120impl 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.
223pub type ManagedObjects =
224 HashMap<OwnedObjectPath, HashMap<OwnedInterfaceName, HashMap<String, OwnedValue>>>;
225
226#[rustfmt::skip]
227macro_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
272gen_object_manager_proxy!(true, false);
273assert_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)]
290pub struct ObjectManager;
291
292#[interface(name = "org.freedesktop.DBus.ObjectManager")]
293impl ObjectManager {
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]
329macro_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
352gen_peer_proxy!(true, false);
353assert_impl_all!(PeerProxy<'_>: Send, Sync, Unpin);
354
355pub(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")]
361impl Peer {
362 fn ping(&self) {}
363
364 fn get_machine_id(&self) -> Result<String> {
365 let mut id = match std::fs::read_to_string("/var/lib/dbus/machine-id") {
366 Ok(id) => id,
367 Err(e) => {
368 if let Ok(id) = std::fs::read_to_string("/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 = id.trim_end().len();
379 id.truncate(len);
380 Ok(id)
381 }
382}
383
384#[rustfmt::skip]
385macro_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
425gen_monitoring_proxy!(true, false);
426assert_impl_all!(MonitoringProxy<'_>: Send, Sync, Unpin);
427
428#[rustfmt::skip]
429macro_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
460gen_stats_proxy!(true, false);
461assert_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)]
469pub 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
498assert_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)]
505pub 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
533assert_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)]
540pub 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
553assert_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}")]
566pub 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
583impl 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]
696macro_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
822gen_dbus_proxy!(true, false);
823assert_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)]
829pub 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
994assert_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
999pub type Result<T> = std::result::Result<T, Error>;
1000
1001#[cfg(test)]
1002mod 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}