fn add_match_rule()

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 {