in crates/zbus/src/fdo.rs [709:1119]
fn add_match_rule(&self, rule: crate::MatchRule<'_>) -> Result<()>;
/// Return auditing data used by Solaris ADT, in an unspecified binary format.
fn get_adt_audit_session_data(&self, bus_name: BusName<'_>) -> Result<Vec<u8>>;
/// Return as many credentials as possible for the process connected to the server.
fn get_connection_credentials(
&self,
bus_name: BusName<'_>,
) -> Result<ConnectionCredentials>;
/// Return the security context used by SELinux, in an unspecified format.
#[zbus(name = "GetConnectionSELinuxSecurityContext")]
fn get_connection_selinux_security_context(
&self,
bus_name: BusName<'_>,
) -> Result<Vec<u8>>;
/// Return the Unix process ID of the process connected to the server.
#[zbus(name = "GetConnectionUnixProcessID")]
fn get_connection_unix_process_id(&self, bus_name: BusName<'_>) -> Result<u32>;
/// Return the Unix user ID of the process connected to the server.
fn get_connection_unix_user(&self, bus_name: BusName<'_>) -> Result<u32>;
/// Get the unique ID of the bus.
fn get_id(&self) -> Result<OwnedGuid>;
/// Return the unique connection name of the primary owner of the name given.
fn get_name_owner(&self, name: BusName<'_>) -> Result<OwnedUniqueName>;
/// Return the unique name assigned to the connection.
fn hello(&self) -> Result<OwnedUniqueName>;
/// Return a list of all names that can be activated on the bus.
fn list_activatable_names(&self) -> Result<Vec<OwnedBusName>>;
/// Return a list of all currently-owned names on the bus.
fn list_names(&self) -> Result<Vec<OwnedBusName>>;
/// List the connections currently queued for a bus name.
fn list_queued_owners(&self, name: WellKnownName<'_>) -> Result<Vec<OwnedUniqueName>>;
/// Check if the specified name exists (currently has an owner).
fn name_has_owner(&self, name: BusName<'_>) -> Result<bool>;
/// Ask the message bus to release the method caller's claim to the given name.
fn release_name(&self, name: WellKnownName<'_>) -> Result<ReleaseNameReply>;
/// Reload server configuration.
fn reload_config(&self) -> Result<()>;
/// Remove the first rule that matches.
#[zbus(name = "RemoveMatch")]
fn remove_match_rule(&self, rule: crate::MatchRule<'_>) -> Result<()>;
/// Ask the message bus to assign the given name to the method caller.
fn request_name(
&self,
name: WellKnownName<'_>,
flags: BitFlags<RequestNameFlags>,
) -> Result<RequestNameReply>;
/// Try to launch the executable associated with a name (service
/// activation), as an explicit request.
fn start_service_by_name(&self, name: WellKnownName<'_>, flags: u32) -> Result<u32>;
/// This method adds to or modifies that environment when activating services.
fn update_activation_environment(&self, environment: HashMap<&str, &str>)
-> Result<()>;
/// This signal indicates that the owner of a name has
/// changed. It's also the signal to use to detect the appearance
/// of new names on the bus.
#[zbus(signal)]
fn name_owner_changed(
&self,
name: BusName<'_>,
old_owner: Optional<UniqueName<'_>>,
new_owner: Optional<UniqueName<'_>>,
);
/// This signal is sent to a specific application when it loses ownership of a name.
#[zbus(signal)]
fn name_lost(&self, name: BusName<'_>);
/// This signal is sent to a specific application when it gains ownership of a name.
#[zbus(signal)]
fn name_acquired(&self, name: BusName<'_>);
/// This property lists abstract “features” provided by the message bus, and can be used by
/// clients to detect the capabilities of the message bus with which they are communicating.
#[zbus(property)]
fn features(&self) -> Result<Vec<String>>;
/// This property lists interfaces provided by the `/org/freedesktop/DBus` object, and can be
/// used by clients to detect the capabilities of the message bus with which they are
/// communicating. Unlike the standard Introspectable interface, querying this property does not
/// require parsing XML. This property was added in version 1.11.x of the reference
/// implementation of the message bus.
///
/// The standard `org.freedesktop.DBus` and `org.freedesktop.DBus.Properties` interfaces are not
/// included in the value of this property, because their presence can be inferred from the fact
/// that a method call on `org.freedesktop.DBus.Properties` asking for properties of
/// `org.freedesktop.DBus` was successful. The standard `org.freedesktop.DBus.Peer` and
/// `org.freedesktop.DBus.Introspectable` interfaces are not included in the value of this
/// property either, because they do not indicate features of the message bus implementation.
#[zbus(property)]
fn interfaces(&self) -> Result<Vec<OwnedInterfaceName>>;
}
};
}
gen_dbus_proxy!(true, false);
assert_impl_all!(DBusProxy<'_>: Send, Sync, Unpin);
/// Errors from <https://gitlab.freedesktop.org/dbus/dbus/-/blob/master/dbus/dbus-protocol.h>
#[derive(Clone, Debug, DBusError, PartialEq)]
#[zbus(prefix = "org.freedesktop.DBus.Error", impl_display = true)]
#[allow(clippy::upper_case_acronyms)]
pub enum Error {
/// Unknown or fall-through zbus error.
#[zbus(error)]
ZBus(zbus::Error),
/// A generic error; "something went wrong" - see the error message for more.
Failed(String),
/// There was not enough memory to complete an operation.
NoMemory(String),
/// The bus doesn't know how to launch a service to supply the bus name you wanted.
ServiceUnknown(String),
/// The bus name you referenced doesn't exist (i.e. no application owns it).
NameHasNoOwner(String),
/// No reply to a message expecting one, usually means a timeout occurred.
NoReply(String),
/// Something went wrong reading or writing to a socket, for example.
IOError(String),
/// A D-Bus bus address was malformed.
BadAddress(String),
/// Requested operation isn't supported (like ENOSYS on UNIX).
NotSupported(String),
/// Some limited resource is exhausted.
LimitsExceeded(String),
/// Security restrictions don't allow doing what you're trying to do.
AccessDenied(String),
/// Authentication didn't work.
AuthFailed(String),
/// Unable to connect to server (probably caused by ECONNREFUSED on a socket).
NoServer(String),
/// Certain timeout errors, possibly ETIMEDOUT on a socket.
/// Note that `TimedOut` is used for message reply timeouts.
Timeout(String),
/// No network access (probably ENETUNREACH on a socket).
NoNetwork(String),
/// Can't bind a socket since its address is in use (i.e. EADDRINUSE).
AddressInUse(String),
/// The connection is disconnected and you're trying to use it.
Disconnected(String),
/// Invalid arguments passed to a method call.
InvalidArgs(String),
/// Missing file.
FileNotFound(String),
/// Existing file and the operation you're using does not silently overwrite.
FileExists(String),
/// Method name you invoked isn't known by the object you invoked it on.
UnknownMethod(String),
/// Object you invoked a method on isn't known.
UnknownObject(String),
/// Interface you invoked a method on isn't known by the object.
UnknownInterface(String),
/// Property you tried to access isn't known by the object.
UnknownProperty(String),
/// Property you tried to set is read-only.
PropertyReadOnly(String),
/// Certain timeout errors, e.g. while starting a service.
TimedOut(String),
/// Tried to remove or modify a match rule that didn't exist.
MatchRuleNotFound(String),
/// The match rule isn't syntactically valid.
MatchRuleInvalid(String),
/// While starting a new process, the exec() call failed.
#[zbus(name = "Spawn.ExecFailed")]
SpawnExecFailed(String),
/// While starting a new process, the fork() call failed.
#[zbus(name = "Spawn.ForkFailed")]
SpawnForkFailed(String),
/// While starting a new process, the child exited with a status code.
#[zbus(name = "Spawn.ChildExited")]
SpawnChildExited(String),
/// While starting a new process, the child exited on a signal.
#[zbus(name = "Spawn.ChildSignaled")]
SpawnChildSignaled(String),
/// While starting a new process, something went wrong.
#[zbus(name = "Spawn.Failed")]
SpawnFailed(String),
/// We failed to set up the environment correctly.
#[zbus(name = "Spawn.FailedToSetup")]
SpawnFailedToSetup(String),
/// We failed to set up the config parser correctly.
#[zbus(name = "Spawn.ConfigInvalid")]
SpawnConfigInvalid(String),
/// Bus name was not valid.
#[zbus(name = "Spawn.ServiceNotValid")]
SpawnServiceNotValid(String),
/// Service file not found in system-services directory.
#[zbus(name = "Spawn.ServiceNotFound")]
SpawnServiceNotFound(String),
/// Permissions are incorrect on the setuid helper.
#[zbus(name = "Spawn.PermissionsInvalid")]
SpawnPermissionsInvalid(String),
/// Service file invalid (Name, User or Exec missing).
#[zbus(name = "Spawn.FileInvalid")]
SpawnFileInvalid(String),
/// There was not enough memory to complete the operation.
#[zbus(name = "Spawn.NoMemory")]
SpawnNoMemory(String),
/// Tried to get a UNIX process ID and it wasn't available.
UnixProcessIdUnknown(String),
/// A type signature is not valid.
InvalidSignature(String),
/// A file contains invalid syntax or is otherwise broken.
InvalidFileContent(String),
/// Asked for SELinux security context and it wasn't available.
SELinuxSecurityContextUnknown(String),
/// Asked for ADT audit data and it wasn't available.
AdtAuditDataUnknown(String),
/// There's already an object with the requested object path.
ObjectPathInUse(String),
/// The message metadata does not match the payload. e.g. expected number of file descriptors
/// were not sent over the socket this message was received on.
InconsistentMessage(String),
/// The message is not allowed without performing interactive authorization, but could have
/// succeeded if an interactive authorization step was allowed.
InteractiveAuthorizationRequired(String),
/// The connection is not from a container, or the specified container instance does not exist.
NotContainer(String),
}
assert_impl_all!(Error: Send, Sync, Unpin);
/// Alias for a `Result` with the error type [`zbus::fdo::Error`].
///
/// [`zbus::fdo::Error`]: enum.Error.html
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use futures_util::StreamExt;
use ntest::timeout;
use test_log::test;
use tokio::runtime;
use zbus_names::WellKnownName;
use crate::message::Message;
use crate::{
DBusError,
Error,
fdo,
};
#[test]
fn error_from_zerror() {
let m = Message::method("/", "foo")
.unwrap()
.destination(":1.2")
.unwrap()
.build(&())
.unwrap();
let m = Message::method_error(&m, "org.freedesktop.DBus.Error.TimedOut")
.unwrap()
.build(&("so long"))
.unwrap();
let e: Error = m.into();
let e: fdo::Error = e.into();
assert_eq!(e, fdo::Error::TimedOut("so long".to_string()),);
assert_eq!(e.name(), "org.freedesktop.DBus.Error.TimedOut");
assert_eq!(e.description(), Some("so long"));
}
#[ignore = "fails in ci"]
#[test]
#[timeout(15000)]
fn signal() {
// Multi-threaded scheduler.
runtime::Runtime::new().unwrap().block_on(test_signal());
// single-threaded scheduler.
runtime::Builder::new_current_thread()
.enable_io()
.build()
.unwrap()
.block_on(test_signal());
}
async fn test_signal() {
let conn = crate::Connection::session().await.unwrap();
let proxy = fdo::DBusProxy::new(&conn).await.unwrap();
// Register a well-known name with the session bus and ensure we get the appropriate
// signals called for that.
let well_known = "org.freedesktop.zbus.FdoSignalStreamTest";
let unique_name = conn.unique_name().unwrap();
let owner_change_stream = proxy
.receive_name_owner_changed_with_args(&[(0, well_known), (2, unique_name.as_str())])
.await
.unwrap();
let name_acquired_stream = proxy.receive_name_acquired_with_args(&[(0, well_known)]).await.unwrap();
let mut stream = owner_change_stream.zip(name_acquired_stream);
let well_known: WellKnownName<'static> = well_known.try_into().unwrap();
proxy
.request_name(well_known.as_ref(), fdo::RequestNameFlags::ReplaceExisting.into())
.await
.unwrap();
let (name_owner_changed, name_acquired) = stream.next().await.unwrap();
assert_eq!(name_owner_changed.args().unwrap().name(), &well_known);
assert_eq!(
*name_owner_changed.args().unwrap().new_owner().as_ref().unwrap(),
*unique_name
);
assert_eq!(name_acquired.args().unwrap().name(), &well_known);
let result = proxy.release_name(well_known.as_ref()).await.unwrap();
assert_eq!(result, fdo::ReleaseNameReply::Released);
let result = proxy.release_name(well_known).await.unwrap();
assert_eq!(result, fdo::ReleaseNameReply::NonExistent);
let _stream = proxy
.receive_features_changed()
.await
.filter_map(|changed| async move { changed.get().await.ok() });
}
#[ignore = "fails in ci"]
#[test]
#[timeout(15000)]
fn no_object_manager_signals_before_hello() {
use zbus::blocking;
// We were emitting `InterfacesAdded` signals before `Hello` was called, which is wrong and
// results in us getting disconnected by the bus. This test case ensures we don't do that
// and also that the signals are eventually emitted.
// Let's first create an interator to get the signals (it has to be another connection).
let conn = blocking::Connection::session().unwrap();
let mut iterator = blocking::MessageIterator::for_match_rule(
zbus::MatchRule::builder()
.msg_type(zbus::MessageType::Signal)
.interface("org.freedesktop.DBus.ObjectManager")
.unwrap()
.path("/org/zbus/NoObjectManagerSignalsBeforeHello")
.unwrap()
.build(),
&conn,
None,
)
.unwrap();
// Now create the service side.
struct TestObj;
#[super::interface(name = "org.zbus.TestObj")]
impl TestObj {