use crate::{
AsyncDrop, Basic, Connection, Error, MatchRule, MessageStream, ObjectPath, OwnedMatchRule,
OwnedVariant, Result, Str, Variant, as_variant,
fdo::{self, IntrospectableProxy, NameOwnerChanged, PropertiesChangedStream, PropertiesProxy},
log::{Instrument, debug, info_span, trace, warn},
message::{Flags, Message, Sequence, Type},
names::{BusName, InterfaceName, MemberName, UniqueName},
runtime::{Runtime, Task},
utils::fmt_bitflags,
};
use futures_core::{ready, stream};
use ordered_stream::{FromFuture, Join, Map, OrderedStream, PollResult, join as join_streams};
use std::{
collections::{HashMap, HashSet},
fmt,
future::Future,
num::NonZeroUsize,
ops::Deref,
pin::Pin,
sync::{Arc, OnceLock, RwLock, RwLockReadGuard},
task::{Context, Poll},
};
use zruntime::{Event, EventListener};
mod builder;
pub use builder::{Builder, CacheProperties};
mod defaults;
pub use defaults::Defaults;
#[derive(Clone, Debug)]
pub struct Proxy<'a> {
pub(crate) inner: Arc<ProxyInner<'a>>,
}
pub(crate) struct ProxyInnerStatic {
pub(crate) conn: Connection,
dest_owner_change_match_rule: OnceLock<OwnedMatchRule>,
}
impl fmt::Debug for ProxyInnerStatic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProxyInnerStatic")
.field(
"dest_owner_change_match_rule",
&self.dest_owner_change_match_rule,
)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub(crate) struct ProxyInner<'a> {
inner_without_borrows: ProxyInnerStatic,
pub(crate) destination: BusName<'a>,
pub(crate) path: ObjectPath<'a>,
pub(crate) interface: InterfaceName<'a>,
property_cache: Option<OnceLock<(Arc<PropertiesCache>, Task<()>)>>,
uncached_properties: HashSet<Str<'a>>,
}
impl Drop for ProxyInnerStatic {
fn drop(&mut self) {
if let Some(rule) = self.dest_owner_change_match_rule.take() {
self.conn.queue_remove_match(rule);
}
}
}
pub struct PropertyChanged<'a, T> {
name: &'a str,
properties: Arc<PropertiesCache>,
proxy: Proxy<'a>,
phantom: std::marker::PhantomData<T>,
}
impl<T> PropertyChanged<'_, T> {
pub fn name(&self) -> &str {
self.name
}
pub async fn get_raw(&self) -> Result<impl Deref<Target = Variant<'static>> + '_> {
struct Wrapper<'w> {
name: &'w str,
values: RwLockReadGuard<'w, HashMap<String, PropertyValue>>,
}
impl Deref for Wrapper<'_> {
type Target = Variant<'static>;
fn deref(&self) -> &Self::Target {
self.values
.get(self.name)
.expect("PropertyStream with no corresponding property")
.value
.as_ref()
.expect("PropertyStream with no corresponding property")
}
}
{
let values = self.properties.values.read().expect("lock poisoned");
if values
.get(self.name)
.expect("PropertyStream with no corresponding property")
.value
.is_some()
{
return Ok(Wrapper {
name: self.name,
values,
});
}
}
let properties_proxy = self.proxy.properties_proxy();
let value = properties_proxy
.get(self.proxy.inner.interface.clone(), self.name)
.await
.map_err(crate::Error::from)?;
{
let mut values = self.properties.values.write().expect("lock poisoned");
values
.get_mut(self.name)
.expect("PropertyStream with no corresponding property")
.value = Some(value);
}
Ok(Wrapper {
name: self.name,
values: self.properties.values.read().expect("lock poisoned"),
})
}
}
impl<T> PropertyChanged<'_, T>
where
T: serde::de::DeserializeOwned + crate::Type,
{
pub async fn get(&self) -> Result<T> {
self.get_raw()
.await
.and_then(|value| convert_property_value(&value))
}
}
#[derive(Debug)]
pub struct PropertyStream<'a, T> {
name: &'a str,
proxy: Proxy<'a>,
changed_listener: EventListener,
phantom: std::marker::PhantomData<T>,
}
impl<'a, T> stream::Stream for PropertyStream<'a, T>
where
T: Unpin,
{
type Item = PropertyChanged<'a, T>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let m = self.get_mut();
let properties = match m.proxy.get_property_cache() {
Some(properties) => properties.clone(),
None => return Poll::Ready(None),
};
ready!(Pin::new(&mut m.changed_listener).poll(cx));
m.changed_listener = properties
.values
.read()
.expect("lock poisoned")
.get(m.name)
.expect("PropertyStream with no corresponding property")
.event
.listen();
Poll::Ready(Some(PropertyChanged {
name: m.name,
properties,
proxy: m.proxy.clone(),
phantom: std::marker::PhantomData,
}))
}
}
#[derive(Debug)]
pub(crate) struct PropertiesCache {
values: RwLock<HashMap<String, PropertyValue>>,
caching_result: RwLock<CachingResult>,
}
#[derive(Debug)]
enum CachingResult {
Caching { ready: Event },
Cached { result: Result<()> },
}
impl PropertiesCache {
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, level = "trace"))]
fn new(
proxy: PropertiesProxy<'static>,
interface: InterfaceName<'static>,
runtime: &Runtime,
uncached_properties: HashSet<crate::Str<'static>>,
) -> (Arc<Self>, Task<()>) {
let cache = Arc::new(PropertiesCache {
values: Default::default(),
caching_result: RwLock::new(CachingResult::Caching {
ready: Event::new(),
}),
});
let cache_clone = cache.clone();
let task_name = format!("{interface} proxy caching");
let proxy_caching = async move {
let result = cache_clone
.init(proxy, interface, uncached_properties)
.await;
let (prop_changes, interface, uncached_properties) = {
let mut caching_result = cache_clone.caching_result.write().expect("lock poisoned");
let ready = match &*caching_result {
CachingResult::Caching { ready } => ready,
_ => unreachable!(),
};
match result {
Ok((prop_changes, interface, uncached_properties)) => {
ready.notify(usize::MAX);
*caching_result = CachingResult::Cached { result: Ok(()) };
(prop_changes, interface, uncached_properties)
}
Err(e) => {
warn!(
"Failed to populate properties cache via GetAll: {e}. \
Property change streams will not produce values."
);
ready.notify(usize::MAX);
*caching_result = CachingResult::Cached { result: Err(e) };
return;
}
}
};
if let Err(e) = cache_clone
.keep_updated(prop_changes, interface, uncached_properties)
.await
{
debug!("Error keeping properties cache updated: {e}");
}
}
.instrument(info_span!("{}", task_name));
let task = runtime.spawn(task_name, proxy_caching);
(cache, task)
}
async fn init(
&self,
proxy: PropertiesProxy<'static>,
interface: InterfaceName<'static>,
uncached_properties: HashSet<crate::Str<'static>>,
) -> Result<(
PropertiesChangedStream,
InterfaceName<'static>,
HashSet<crate::Str<'static>>,
)> {
use ordered_stream::OrderedStreamExt;
let prop_changes = proxy.receive_properties_changed().await?.map(Either::Left);
let get_all = proxy
.inner()
.connection()
.call_method_raw(
Some(proxy.inner().destination()),
proxy.inner().path(),
Some(proxy.inner().interface()),
"GetAll",
Flags::empty(),
&interface,
)
.await
.map(|r| FromFuture::from(r.expect("no reply")).map(Either::Right))?;
let mut join = join_streams(prop_changes, get_all);
loop {
match join.next().await {
Some(Either::Left(_update)) => {
}
Some(Either::Right(Err(Error::FDO(e)))) => match &*e {
fdo::Error::ServiceUnknown(_) => break,
_ => return Err(Error::FDO(e)),
},
Some(Either::Right(populate)) => {
populate?.body().deserialize().map(|values| {
self.update_cache(&uncached_properties, &values, &[], &interface);
})?;
break;
}
None => break,
}
}
if let Some((Either::Left(update), _)) = Pin::new(&mut join).take_buffered() {
if let Ok(args) = update.args()
&& args.interface_name == interface
{
self.update_cache(
&uncached_properties,
&args.changed_properties,
&args.invalidated_properties,
&interface,
);
}
}
let prop_changes = join.into_inner().0.into_inner();
Ok((prop_changes, interface, uncached_properties))
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, level = "trace"))]
async fn keep_updated(
&self,
mut prop_changes: PropertiesChangedStream,
interface: InterfaceName<'static>,
uncached_properties: HashSet<crate::Str<'static>>,
) -> Result<()> {
trace!("Listening for property changes on {interface}...");
while let Some(update) = crate::utils::next(&mut prop_changes).await {
if let Ok(args) = update.args()
&& args.interface_name == interface
{
self.update_cache(
&uncached_properties,
&args.changed_properties,
&args.invalidated_properties,
&interface,
);
}
}
Ok(())
}
fn update_cache(
&self,
uncached_properties: &HashSet<Str<'_>>,
changed: &HashMap<&str, Variant<'_>>,
invalidated: &[&str],
interface: &InterfaceName<'_>,
) {
let mut values = self.values.write().expect("lock poisoned");
for inval in invalidated {
if uncached_properties.contains(&Str::from(*inval)) {
debug!(
"Ignoring invalidation of uncached property `{}.{}`",
interface, inval
);
continue;
}
trace!("Property `{interface}.{inval}` invalidated");
if let Some(entry) = values.get_mut(*inval) {
entry.value = None;
entry.event.notify(usize::MAX);
}
}
for (property_name, value) in changed {
if uncached_properties.contains(&Str::from(*property_name)) {
debug!(
"Ignoring update of uncached property `{}.{}`",
interface, property_name
);
continue;
}
trace!("Property `{interface}.{property_name}` updated");
let entry = values.entry(property_name.to_string()).or_default();
let value = match OwnedVariant::try_from(value) {
Ok(value) => value,
Err(e) => {
debug!(
"Failed to convert property `{interface}.{property_name}` to \
OwnedVariant: {e}"
);
continue;
}
};
entry.value = Some(value);
entry.event.notify(usize::MAX);
}
}
pub(crate) async fn ready(&self) -> Result<()> {
let listener = match &*self.caching_result.read().expect("lock poisoned") {
CachingResult::Caching { ready } => ready.listen(),
CachingResult::Cached { result } => return result.clone(),
};
listener.await;
match &*self.caching_result.read().expect("lock poisoned") {
CachingResult::Caching { .. } => unreachable!(),
CachingResult::Cached { result } => result.clone(),
}
}
}
impl<'a> ProxyInner<'a> {
pub(crate) fn new(
conn: Connection,
destination: BusName<'a>,
path: ObjectPath<'a>,
interface: InterfaceName<'a>,
cache: CacheProperties,
uncached_properties: HashSet<Str<'a>>,
) -> Self {
let property_cache = match cache {
CacheProperties::Yes | CacheProperties::Lazily => Some(OnceLock::new()),
CacheProperties::No => None,
};
Self {
inner_without_borrows: ProxyInnerStatic {
conn,
dest_owner_change_match_rule: OnceLock::new(),
},
destination,
path,
interface,
property_cache,
uncached_properties,
}
}
pub(crate) async fn subscribe_dest_owner_change(&self) -> Result<()> {
if !self.inner_without_borrows.conn.is_bus() {
return Ok(());
}
let well_known_name = match &self.destination {
BusName::WellKnown(well_known_name) => well_known_name,
BusName::Unique(_) => return Ok(()),
};
if self
.inner_without_borrows
.dest_owner_change_match_rule
.get()
.is_some()
{
return Ok(());
}
let conn = &self.inner_without_borrows.conn;
let signal_rule: OwnedMatchRule = MatchRule::builder()
.msg_type(Type::Signal)
.sender("org.freedesktop.DBus")
.path("/org/freedesktop/DBus")
.interface("org.freedesktop.DBus")
.member("NameOwnerChanged")
.add_arg(well_known_name.as_str())
.build()?
.to_owned()
.into();
conn.add_match(
signal_rule.clone(),
Some(MAX_NAME_OWNER_CHANGED_SIGNALS_QUEUED),
)
.await?;
if self
.inner_without_borrows
.dest_owner_change_match_rule
.set(signal_rule.clone())
.is_err()
{
conn.remove_match(signal_rule).await?;
}
Ok(())
}
}
const MAX_NAME_OWNER_CHANGED_SIGNALS_QUEUED: NonZeroUsize = NonZeroUsize::new(8).unwrap();
impl<'a> Proxy<'a> {
pub async fn new<D, P, I>(
conn: &Connection,
destination: D,
path: P,
interface: I,
) -> Result<Proxy<'a>>
where
D: TryInto<BusName<'a>>,
P: TryInto<ObjectPath<'a>>,
I: TryInto<InterfaceName<'a>>,
D::Error: Into<Error>,
P::Error: Into<Error>,
I::Error: Into<Error>,
{
Builder::new(conn)
.destination(destination)
.path(path)
.interface(interface)
.build()
.await
}
pub async fn new_owned<D, P, I>(
conn: Connection,
destination: D,
path: P,
interface: I,
) -> Result<Proxy<'a>>
where
D: TryInto<BusName<'static>>,
P: TryInto<ObjectPath<'static>>,
I: TryInto<InterfaceName<'static>>,
D::Error: Into<Error>,
P::Error: Into<Error>,
I::Error: Into<Error>,
{
Builder::new(&conn)
.destination(destination)
.path(path)
.interface(interface)
.build()
.await
}
pub fn connection(&self) -> &Connection {
&self.inner.inner_without_borrows.conn
}
pub fn destination(&self) -> &BusName<'a> {
&self.inner.destination
}
pub fn path(&self) -> &ObjectPath<'a> {
&self.inner.path
}
pub fn interface(&self) -> &InterfaceName<'a> {
&self.inner.interface
}
pub async fn introspect(&self) -> fdo::Result<String> {
let proxy = IntrospectableProxy::builder(&self.inner.inner_without_borrows.conn)
.destination(&self.inner.destination)
.path(&self.inner.path)
.build()
.await?;
proxy.introspect().await
}
fn properties_proxy(&self) -> PropertiesProxy<'_> {
PropertiesProxy::builder(&self.inner.inner_without_borrows.conn)
.destination(self.inner.destination.as_ref())
.path(self.inner.path.as_ref())
.cache_properties(CacheProperties::No)
.build_internal()
.expect("invalid properties proxy")
.into()
}
fn owned_properties_proxy(&self) -> PropertiesProxy<'static> {
PropertiesProxy::builder(&self.inner.inner_without_borrows.conn)
.destination(self.inner.destination.to_owned())
.path(self.inner.path.to_owned())
.cache_properties(CacheProperties::No)
.build_internal()
.expect("invalid properties proxy")
.into()
}
pub(crate) fn get_property_cache(&self) -> Option<&Arc<PropertiesCache>> {
let cache = self.inner.property_cache.as_ref()?;
let (cache, _) = &cache.get_or_init(|| {
let proxy = self.owned_properties_proxy();
let interface = self.interface().to_owned();
let uncached_properties: HashSet<crate::Str<'static>> = self
.inner
.uncached_properties
.iter()
.map(|s| s.to_owned())
.collect();
let runtime = self.connection().runtime();
PropertiesCache::new(proxy, interface, runtime, uncached_properties)
});
Some(cache)
}
pub fn cached_property<T>(&self, property_name: &str) -> Result<Option<T>>
where
T: serde::de::DeserializeOwned + crate::Type,
{
self.cached_property_raw(property_name)
.as_deref()
.map(convert_property_value)
.transpose()
}
pub fn cached_property_raw<'p>(
&'p self,
property_name: &'p str,
) -> Option<impl Deref<Target = Variant<'static>> + 'p> {
if let Some(values) = self
.inner
.property_cache
.as_ref()
.and_then(OnceLock::get)
.map(|c| c.0.values.read().expect("lock poisoned"))
{
values
.get(property_name)
.and_then(|e| e.value.as_ref())?;
struct Wrapper<'a> {
values: RwLockReadGuard<'a, HashMap<String, PropertyValue>>,
property_name: &'a str,
}
impl Deref for Wrapper<'_> {
type Target = Variant<'static>;
fn deref(&self) -> &Self::Target {
self.values
.get(self.property_name)
.and_then(|e| e.value.as_ref())
.map(|v| v.deref())
.expect("inexistent property")
}
}
Some(Wrapper {
values,
property_name,
})
} else {
None
}
}
async fn get_proxy_property(&self, property_name: &str) -> Result<OwnedVariant> {
Ok(self
.properties_proxy()
.get(self.inner.interface.as_ref(), property_name)
.await?)
}
pub async fn get_property<T>(&self, property_name: &str) -> Result<T>
where
T: serde::de::DeserializeOwned + crate::Type,
{
if let Some(cache) = self.get_property_cache() {
cache.ready().await?;
}
if let Some(value) = self.cached_property(property_name)? {
return Ok(value);
}
let value = self.get_proxy_property(property_name).await?;
convert_property_value(&value)
}
pub async fn set_property<T>(&self, property_name: &str, value: T) -> fdo::Result<()>
where
T: serde::Serialize + crate::Type,
{
self.properties_proxy()
.inner()
.call::<_, _, ()>(
"Set",
&crate::DynamicTuple((
self.inner.interface.as_ref(),
property_name,
as_variant::Serialize(&value),
)),
)
.await?;
Ok(())
}
pub async fn call_method<'m, M, B>(&self, method_name: M, body: &B) -> Result<Message>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + crate::DynamicType,
{
self.inner
.inner_without_borrows
.conn
.call_method(
Some(&self.inner.destination),
self.inner.path.as_str(),
Some(&self.inner.interface),
method_name,
body,
)
.await
}
pub async fn call<'m, M, B, R>(&self, method_name: M, body: &B) -> Result<R>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + crate::DynamicType,
R: for<'d> crate::wire::DynamicDeserialize<'d>,
{
let reply = self.call_method(method_name, body).await?;
reply.body().deserialize()
}
pub async fn call_with_flags<'m, M, B, R>(
&self,
method_name: M,
flags: MethodFlags,
body: &B,
) -> Result<Option<R>>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + crate::DynamicType,
R: for<'d> crate::wire::DynamicDeserialize<'d>,
{
match self
.inner
.inner_without_borrows
.conn
.call_method_raw(
Some(self.destination()),
self.path(),
Some(self.interface()),
method_name,
flags.into(),
body,
)
.await?
{
Some(reply) => reply.await?.body().deserialize().map(Some),
None => Ok(None),
}
}
pub async fn call_noreply<'m, M, B>(&self, method_name: M, body: &B) -> Result<()>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + crate::DynamicType,
{
self.call_with_flags::<_, _, ()>(method_name, MethodFlags::NO_REPLY_EXPECTED, body)
.await?;
Ok(())
}
pub async fn receive_signal<'m, M>(&self, signal_name: M) -> Result<SignalStream<'m>>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
{
self.receive_signal_with_args(signal_name, &[]).await
}
pub async fn receive_signal_with_args<'m, M>(
&self,
signal_name: M,
args: &[(u8, &str)],
) -> Result<SignalStream<'m>>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
{
let signal_name = signal_name.try_into().map_err(Into::into)?;
self.receive_signals(Some(signal_name), args).await
}
async fn receive_signals<'m>(
&self,
signal_name: Option<MemberName<'m>>,
args: &[(u8, &str)],
) -> Result<SignalStream<'m>> {
self.inner.subscribe_dest_owner_change().await?;
SignalStream::new(self.clone(), signal_name, args).await
}
pub async fn receive_all_signals(&self) -> Result<SignalStream<'static>> {
self.receive_signals(None, &[]).await
}
pub async fn receive_property_changed<'name: 'a, T>(
&self,
name: &'name str,
) -> PropertyStream<'a, T> {
let properties = self.get_property_cache();
let changed_listener = if let Some(properties) = &properties {
let mut values = properties.values.write().expect("lock poisoned");
let entry = values
.entry(name.to_string())
.or_insert_with(PropertyValue::default);
let listener = entry.event.listen();
if entry.value.is_some() {
entry.event.notify(1);
}
listener
} else {
Event::new().listen()
};
PropertyStream {
name,
proxy: self.clone(),
changed_listener,
phantom: std::marker::PhantomData,
}
}
pub async fn receive_owner_changed(&self) -> Result<OwnerChangedStream<'a>> {
use ordered_stream::OrderedStreamExt;
let dbus_proxy = fdo::DBusProxy::builder(self.connection())
.cache_properties(CacheProperties::No)
.build()
.await?;
Ok(OwnerChangedStream {
stream: dbus_proxy
.receive_name_owner_changed_with_args(&[(0, self.destination().as_str())])
.await?
.map(Box::new(move |signal| {
let args = signal.args().unwrap();
args.new_owner().as_ref().map(|owner| owner.to_owned())
})),
name: self.destination().clone(),
})
}
}
#[derive(Debug, Default)]
struct PropertyValue {
value: Option<OwnedVariant>,
event: Event,
}
#[derive(
Copy,
Clone,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
crate::Type,
Variant,
OwnedVariant,
)]
pub struct MethodFlags(u8);
bitflags::bitflags! {
impl MethodFlags: u8 {
const NO_REPLY_EXPECTED = 0x1;
const NO_AUTO_START = 0x2;
const ALLOW_INTERACTIVE_AUTH = 0x4;
}
}
impl From<MethodFlags> for Flags {
fn from(method_flags: MethodFlags) -> Self {
Self::from_bits_truncate(method_flags.bits())
}
}
impl fmt::Debug for MethodFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_bitflags("MethodFlags", self, f)
}
}
impl Basic for MethodFlags {
const SIGNATURE_CHAR: char = u8::SIGNATURE_CHAR;
const SIGNATURE_STR: &'static str = u8::SIGNATURE_STR;
}
type OwnerChangedStreamMap = Map<
fdo::NameOwnerChangedStream,
Box<dyn FnMut(fdo::NameOwnerChanged) -> Option<UniqueName<'static>> + Send + Sync + Unpin>,
>;
pub struct OwnerChangedStream<'a> {
stream: OwnerChangedStreamMap,
name: BusName<'a>,
}
impl<'a> OwnerChangedStream<'a> {
pub fn name(&self) -> &BusName<'a> {
&self.name
}
}
impl stream::Stream for OwnerChangedStream<'_> {
type Item = Option<UniqueName<'static>>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
OrderedStream::poll_next_before(self, cx, None).map(|res| res.into_data())
}
}
impl OrderedStream for OwnerChangedStream<'_> {
type Data = Option<UniqueName<'static>>;
type Ordering = Sequence;
fn poll_next_before(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
before: Option<&Self::Ordering>,
) -> Poll<PollResult<Self::Ordering, Self::Data>> {
Pin::new(&mut self.get_mut().stream).poll_next_before(cx, before)
}
}
#[derive(Debug)]
pub struct SignalStream<'a> {
stream: Join<MessageStream, Option<MessageStream>>,
src_unique_name: Option<UniqueName<'static>>,
signal_name: Option<MemberName<'a>>,
}
impl<'a> SignalStream<'a> {
pub fn name(&self) -> Option<&MemberName<'a>> {
self.signal_name.as_ref()
}
async fn new(
proxy: Proxy<'_>,
signal_name: Option<MemberName<'a>>,
args: &[(u8, &str)],
) -> Result<SignalStream<'a>> {
let mut rule_builder = MatchRule::builder()
.msg_type(Type::Signal)
.sender(proxy.destination())
.path(proxy.path())
.interface(proxy.interface());
if let Some(name) = &signal_name {
rule_builder = rule_builder.member(name);
}
for (i, arg) in args {
rule_builder = rule_builder.arg(*i, *arg);
}
let signal_rule: OwnedMatchRule = rule_builder.build()?.to_owned().into();
let conn = proxy.connection();
let (src_unique_name, stream) = match proxy.destination().to_owned() {
BusName::Unique(name) => (
Some(name),
join_streams(
MessageStream::for_match_rule(signal_rule, conn, None).await?,
None,
),
),
BusName::WellKnown(name) => {
use ordered_stream::OrderedStreamExt;
let name_owner_changed_rule = MatchRule::builder()
.msg_type(Type::Signal)
.sender("org.freedesktop.DBus")
.path("/org/freedesktop/DBus")
.interface("org.freedesktop.DBus")
.member("NameOwnerChanged")
.add_arg(name.as_str())
.build()?;
let name_owner_changed_stream = MessageStream::for_match_rule(
name_owner_changed_rule,
conn,
Some(MAX_NAME_OWNER_CHANGED_SIGNALS_QUEUED),
)
.await?
.map(Either::Left);
let get_name_owner = conn
.call_method_raw(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetNameOwner",
Flags::empty(),
&name,
)
.await
.map(|r| FromFuture::from(r.expect("no reply")).map(Either::Right))?;
let mut join = join_streams(name_owner_changed_stream, get_name_owner);
let mut src_unique_name = loop {
match join.next().await {
Some(Either::Left(Ok(msg))) => {
let signal = NameOwnerChanged::from_message(msg)
.expect("`NameOwnerChanged` signal stream got wrong message");
{
break signal
.args()
.expect("`NameOwnerChanged` signal has no args")
.new_owner()
.as_ref()
.map(UniqueName::to_owned);
}
}
Some(Either::Left(Err(_))) => (),
Some(Either::Right(Ok(response))) => {
break Some(
response.body().deserialize::<UniqueName<'_>>()?.to_owned(),
);
}
Some(Either::Right(Err(e))) => {
debug!("Failed to get owner of {name}: {e}");
break None;
}
None => {
return Err(Error::InputOutput(
std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"connection closed",
)
.into(),
));
}
}
};
let (stream, _, queued) = join.into_inner();
if let Some(msg) = queued.and_then(|e| match e.0 {
Either::Left(Ok(msg)) => Some(msg),
Either::Left(Err(_)) | Either::Right(_) => None,
}) && let Some(signal) = NameOwnerChanged::from_message(msg)
&& let Ok(args) = signal.args()
{
match (args.name(), args.new_owner().deref()) {
(BusName::WellKnown(n), Some(new_owner)) if n == &name => {
src_unique_name = Some(new_owner.to_owned());
}
_ => (),
}
}
let name_owner_changed_stream = stream.into_inner();
let stream = join_streams(
MessageStream::for_match_rule(signal_rule, conn, None).await?,
Some(name_owner_changed_stream),
);
(src_unique_name, stream)
}
};
Ok(SignalStream {
stream,
src_unique_name,
signal_name,
})
}
fn filter(&mut self, msg: &Message) -> Result<bool> {
let header = msg.header();
let sender = header.sender();
if sender == self.src_unique_name.as_ref() {
return Ok(true);
}
if let Some(signal) = NameOwnerChanged::from_message(msg.clone()) {
let args = signal.args()?;
self.src_unique_name = args.new_owner().as_ref().map(|n| n.to_owned());
}
Ok(false)
}
}
impl stream::Stream for SignalStream<'_> {
type Item = Message;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
OrderedStream::poll_next_before(self, cx, None).map(|res| res.into_data())
}
}
impl OrderedStream for SignalStream<'_> {
type Data = Message;
type Ordering = Sequence;
fn poll_next_before(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
before: Option<&Self::Ordering>,
) -> Poll<PollResult<Self::Ordering, Self::Data>> {
let this = self.get_mut();
loop {
match ready!(OrderedStream::poll_next_before(
Pin::new(&mut this.stream),
cx,
before
)) {
PollResult::Item { data, ordering } => {
if let Ok(msg) = data
&& let Ok(true) = this.filter(&msg)
{
return Poll::Ready(PollResult::Item {
data: msg,
ordering,
});
}
}
PollResult::Terminated => return Poll::Ready(PollResult::Terminated),
PollResult::NoneBefore => return Poll::Ready(PollResult::NoneBefore),
}
}
}
}
impl stream::FusedStream for SignalStream<'_> {
fn is_terminated(&self) -> bool {
ordered_stream::FusedOrderedStream::is_terminated(&self.stream)
}
}
impl AsyncDrop for SignalStream<'_> {
async fn async_drop(self) {
let (signals, names, _buffered) = self.stream.into_inner();
signals.async_drop().await;
if let Some(names) = names {
names.async_drop().await;
}
}
}
pub trait ProxyImpl<'c>
where
Self: Sized,
{
fn builder(conn: &Connection) -> Builder<'c, Self>;
fn into_inner(self) -> Proxy<'c>;
fn inner(&self) -> &Proxy<'c>;
}
fn convert_property_value<T>(value: &Variant<'_>) -> Result<T>
where
T: serde::de::DeserializeOwned + crate::Type,
{
as_variant::deserialize_for_property(value)
}
enum Either<L, R> {
Left(L),
Right(R),
}
#[cfg(test)]
mod method_flags_tests {
use super::{Flags, MethodFlags};
use crate::Type as _;
#[test]
fn method_flags_convert_to_header_flags() {
for (name, method_flag) in MethodFlags::all().iter_names() {
assert_eq!(
Flags::from(method_flag),
Flags::from_name(name).unwrap(),
"{name}"
);
}
assert_eq!(Flags::from(MethodFlags::empty()), Flags::empty());
assert_eq!(
Flags::from(MethodFlags::from_bits_retain(0x81)),
Flags::NO_REPLY_EXPECTED
);
}
#[test]
fn method_flags_convert_from_borrowed_value() {
use crate::{
Variant,
wire::{LE, serialized::Context, to_bytes},
};
let flags = MethodFlags::NO_AUTO_START | MethodFlags::ALLOW_INTERACTIVE_AUTH;
let encoded = to_bytes(Context::new(LE, 0), &Variant::from(flags)).unwrap();
let (value, _): (Variant<'_>, _) = encoded.deserialize().unwrap();
assert_eq!(MethodFlags::try_from(value).unwrap(), flags);
}
#[test]
fn method_flags_signature_and_debug() {
assert_eq!(MethodFlags::SIGNATURE, "y");
assert_eq!(format!("{:?}", MethodFlags::empty()), "MethodFlags(0x0)");
assert_eq!(
format!(
"{:?}",
MethodFlags::NO_AUTO_START | MethodFlags::ALLOW_INTERACTIVE_AUTH
),
"MethodFlags(NO_AUTO_START | ALLOW_INTERACTIVE_AUTH)"
);
}
}
#[cfg(all(
test,
feature = "service",
any(feature = "default-rt", feature = "tokio")
))]
mod tests {
use super::*;
use crate::{connection, interface, object_server::SignalEmitter, proxy, utils::block_on};
use futures_util::StreamExt;
use ntest::timeout;
use test_log::test;
#[test]
#[timeout(15000)]
fn signal() {
block_on(test_signal()).unwrap();
}
async fn test_signal() -> Result<()> {
let conn = Connection::session().await?;
let dest_conn = Connection::session().await?;
let unique_name = dest_conn.unique_name().unwrap().clone();
let well_known = "org.freedesktop.zbus.async.ProxySignalStreamTest";
let proxy: Proxy<'_> = Builder::new(&conn)
.destination(well_known)
.path("/does/not/matter")
.interface("does.not.matter")
.build()
.await?;
let mut owner_changed_stream = proxy.receive_owner_changed().await?;
let proxy = fdo::DBusProxy::new(&dest_conn).await?;
let mut name_acquired_stream = proxy
.inner()
.receive_signal_with_args("NameAcquired", &[(0, well_known)])
.await?;
let prop_stream = proxy
.inner()
.receive_property_changed("SomeProp")
.await
.filter_map(|changed| async move {
let v: Option<u32> = changed.get().await.ok();
dbg!(v)
});
drop(proxy);
drop(prop_stream);
dest_conn.request_name(well_known).await?;
let (new_owner, acquired_signal) =
futures_util::join!(owner_changed_stream.next(), name_acquired_stream.next(),);
assert_eq!(&new_owner.unwrap().unwrap(), &*unique_name);
let acquired_signal = acquired_signal.unwrap();
assert_eq!(
acquired_signal.body().deserialize::<&str>().unwrap(),
well_known
);
let proxy = Proxy::new(&conn, &unique_name, "/does/not/matter", "does.not.matter").await?;
let mut unique_name_changed_stream = proxy.receive_owner_changed().await?;
drop(dest_conn);
name_acquired_stream.async_drop().await;
let new_owner = owner_changed_stream.next().await;
assert!(new_owner.unwrap().is_none());
let new_unique_owner = unique_name_changed_stream.next().await;
assert!(new_unique_owner.unwrap().is_none());
Ok(())
}
#[test]
#[timeout(15000)]
fn signal_stream_deadlock() {
block_on(test_signal_stream_deadlock()).unwrap();
}
async fn test_signal_stream_deadlock() -> Result<()> {
#[proxy(
default_path = "/org/zbus/Test",
default_service = "org.zbus.Test.MR501",
interface = "org.zbus.Test"
)]
trait Test {
#[zbus(signal)]
fn my_signal(&self, msg: &str) -> Result<()>;
}
struct TestIface;
#[interface(name = "org.zbus.Test")]
impl TestIface {
#[zbus(signal)]
async fn my_signal(context: &SignalEmitter<'_>, msg: &'static str) -> Result<()>;
}
let test_iface = TestIface;
let server_conn = connection::Builder::session()
.name("org.zbus.Test.MR501")
.serve_at("/org/zbus/Test", test_iface)
.build()
.await?;
let client_conn = connection::Builder::session()
.max_queued(NonZeroUsize::new(1).unwrap())
.build()
.await?;
let test_proxy = TestProxy::new(&client_conn).await?;
let test_prop_proxy = PropertiesProxy::builder(&client_conn)
.destination("org.zbus.Test.MR501")
.path("/org/zbus/Test")
.build()
.await?;
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
let handle = {
let tx = tx.clone();
let conn = server_conn.clone();
let server_fut = async move {
use std::time::Duration;
#[cfg(all(feature = "tokio", not(feature = "default-rt")))]
use tokio::time::sleep;
let iface_ref = conn
.object_server()
.interface::<_, TestIface>("/org/zbus/Test")
.await
.unwrap();
let context = iface_ref.signal_emitter();
while !tx.is_closed() {
for _ in 0..10 {
TestIface::my_signal(context, "This is a test")
.await
.unwrap();
}
#[cfg(feature = "default-rt")]
conn.runtime().sleep(Duration::from_millis(5)).await;
#[cfg(all(feature = "tokio", not(feature = "default-rt")))]
sleep(Duration::from_millis(5)).await;
}
};
server_conn.runtime().spawn("server_task", server_fut)
};
let signal_fut = async {
let mut signal_stream = test_proxy.receive_my_signal().await.unwrap();
tx.send(()).await.unwrap();
while let Some(_signal) = signal_stream.next().await {}
};
let prop_fut = async move {
rx.recv().await.unwrap();
let _prop_stream = test_prop_proxy.receive_properties_changed().await.unwrap();
};
futures_util::pin_mut!(signal_fut);
futures_util::pin_mut!(prop_fut);
futures_util::future::select(signal_fut, prop_fut).await;
handle.await?;
Ok(())
}
#[test]
#[timeout(15000)]
fn property_stream_of_service_started_later() {
block_on(test_property_stream_of_service_started_later()).unwrap();
}
async fn test_property_stream_of_service_started_later() -> Result<()> {
struct TestIface {
some_prop: u32,
}
#[interface(name = "org.zbus.Test.PropertyStream")]
impl TestIface {
#[zbus(property)]
fn some_prop(&self) -> u32 {
self.some_prop
}
}
let well_known = "org.zbus.Test.PropertyStreamLater";
let path = "/org/zbus/Test/PropertyStreamLater";
let client_conn = Connection::session().await?;
let proxy: Proxy<'_> = Builder::new(&client_conn)
.destination(well_known)
.path(path)
.interface("org.zbus.Test.PropertyStream")
.build()
.await?;
let mut stream = proxy.receive_property_changed::<u32>("SomeProp").await;
proxy
.get_property_cache()
.expect("proxy has no properties cache")
.ready()
.await?;
let service_conn = connection::Builder::session()
.name(well_known)
.serve_at(path, TestIface { some_prop: 0 })
.build()
.await?;
let iface_ref = service_conn
.object_server()
.interface::<_, TestIface>(path)
.await?;
{
let mut iface = iface_ref.get_mut().await;
iface.some_prop = 42;
iface.some_prop_changed(iface_ref.signal_emitter()).await?;
}
let change = stream.next().await.expect("property stream ended");
assert_eq!(change.get().await?, 42);
Ok(())
}
}