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: &ObjectPath<'_> = header.path()?.ok_or(err:crate::Error::MissingField)?;
57 let root: RwLockReadGuard<'_, Node> = server.root().read().await;
58 let node: &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: String = match std::fs::read_to_string(path:"/var/lib/dbus/machine-id") {
362 Ok(id: String) => id,
363 Err(e: Error) => {
364 if let Ok(id: String) = std::fs::read_to_string(path:"/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: usize = id.trim_end().len();
375 id.truncate(new_len: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}
1079