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