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}