#![deny(rust_2018_idioms)]
#![doc(
html_logo_url = "https://storage.googleapis.com/fdo-gitlab-uploads/project/avatar/3213/zbus-logomark.png"
)]
#[cfg(doctest)]
mod doctests {
doc_comment::doctest!("../../README.md");
doc_comment::doctest!("../../book/src/client.md");
doc_comment::doctest!("../../book/src/concepts.md");
doc_comment::doctest!("../../book/src/connection.md");
doc_comment::doctest!("../../book/src/contributors.md");
doc_comment::doctest!("../../book/src/introduction.md");
doc_comment::doctest!("../../book/src/server.md");
}
mod error;
pub use error::*;
mod address;
mod guid;
pub use guid::*;
mod message;
pub use message::*;
mod message_header;
pub use message_header::*;
mod message_field;
pub use message_field::*;
mod message_fields;
pub use message_fields::*;
mod connection;
pub use connection::*;
mod proxy;
pub use proxy::*;
mod signal_receiver;
pub use signal_receiver::*;
mod owned_fd;
pub use owned_fd::*;
mod utils;
mod object_server;
pub use object_server::*;
pub mod fdo;
mod raw;
pub mod azync;
mod handshake;
pub mod xml;
pub use zbus_macros::{dbus_interface, dbus_proxy, DBusError};
extern crate self as zbus;
#[doc(hidden)]
pub mod export {
pub use zvariant;
}
#[cfg(test)]
mod tests {
use std::{
collections::HashMap,
convert::TryInto,
fs::File,
os::unix::io::{AsRawFd, FromRawFd},
sync::{Arc, Condvar, Mutex},
};
use enumflags2::BitFlags;
use ntest::timeout;
use serde_repr::{Deserialize_repr, Serialize_repr};
use zvariant::{derive::Type, Fd, OwnedValue, Type};
use crate::{azync, Connection, Message, MessageFlags, Result};
#[test]
fn msg() {
let mut m = Message::method(
None,
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus.Peer"),
"GetMachineId",
&(),
)
.unwrap();
m.modify_primary_header(|primary| {
primary.set_flags(BitFlags::from(MessageFlags::NoAutoStart));
primary.set_serial_num(11);
Ok(())
})
.unwrap();
let primary = m.primary_header().unwrap();
assert!(primary.serial_num() == 11);
assert!(primary.flags() == MessageFlags::NoAutoStart);
}
#[test]
fn basic_connection() {
let connection = crate::Connection::new_session()
.map_err(|e| {
println!("error: {}", e);
e
})
.unwrap();
match connection.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"Hello",
&(),
) {
Err(crate::Error::MethodError(_, _, _)) => (),
Err(e) => panic!("{}", e),
_ => panic!(),
};
}
#[test]
fn basic_connection_async() {
pollster::block_on(test_basic_connection()).unwrap();
}
async fn test_basic_connection() -> Result<()> {
let connection = azync::Connection::new_session().await?;
match connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"Hello",
&(),
)
.await
{
Err(crate::Error::MethodError(_, _, _)) => (),
Err(e) => panic!("{}", e),
_ => panic!(),
};
Ok(())
}
#[test]
fn fdpass_systemd() {
let connection = crate::Connection::new_system().unwrap();
let mut reply = connection
.call_method(
Some("org.freedesktop.systemd1"),
"/org/freedesktop/systemd1",
Some("org.freedesktop.systemd1.Manager"),
"DumpByFileDescriptor",
&(),
)
.unwrap();
assert!(reply
.body_signature()
.map(|s| s == <Fd>::signature())
.unwrap());
let fd: Fd = reply.body().unwrap();
reply.disown_fds();
assert!(fd.as_raw_fd() >= 0);
let f = unsafe { File::from_raw_fd(fd.as_raw_fd()) };
f.metadata().unwrap();
}
#[repr(u32)]
#[derive(Type, BitFlags, Debug, PartialEq, Copy, Clone)]
enum RequestNameFlags {
AllowReplacement = 0x01,
ReplaceExisting = 0x02,
DoNotQueue = 0x04,
}
#[repr(u32)]
#[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)]
enum RequestNameReply {
PrimaryOwner = 0x01,
InQueue = 0x02,
Exists = 0x03,
AlreadyOwner = 0x04,
}
#[test]
fn freedesktop_api() {
let connection = crate::Connection::new_session()
.map_err(|e| {
println!("error: {}", e);
e
})
.unwrap();
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"RequestName",
&(
"org.freedesktop.zbus.sync",
BitFlags::from(RequestNameFlags::ReplaceExisting),
),
)
.unwrap();
assert!(reply.body_signature().map(|s| s == "u").unwrap());
let reply: RequestNameReply = reply.body().unwrap();
assert_eq!(reply, RequestNameReply::PrimaryOwner);
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetId",
&(),
)
.unwrap();
assert!(reply
.body_signature()
.map(|s| s == <&str>::signature())
.unwrap());
let id: &str = reply.body().unwrap();
println!("Unique ID of the bus: {}", id);
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"NameHasOwner",
&"org.freedesktop.zbus.sync",
)
.unwrap();
assert!(reply
.body_signature()
.map(|s| s == bool::signature())
.unwrap());
assert!(reply.body::<bool>().unwrap());
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetNameOwner",
&"org.freedesktop.zbus.sync",
)
.unwrap();
assert!(reply
.body_signature()
.map(|s| s == <&str>::signature())
.unwrap());
assert_eq!(
Some(reply.body::<&str>().unwrap()),
connection.unique_name()
);
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetConnectionCredentials",
&"org.freedesktop.DBus",
)
.unwrap();
assert!(reply.body_signature().map(|s| s == "a{sv}").unwrap());
let hashmap: HashMap<&str, OwnedValue> = reply.body().unwrap();
let pid: u32 = (&hashmap["ProcessID"]).try_into().unwrap();
println!("DBus bus PID: {}", pid);
let uid: u32 = (&hashmap["UnixUserID"]).try_into().unwrap();
println!("DBus bus UID: {}", uid);
}
#[test]
fn freedesktop_api_async() {
pollster::block_on(test_freedesktop_api()).unwrap();
}
async fn test_freedesktop_api() -> Result<()> {
let connection = azync::Connection::new_session().await?;
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"RequestName",
&(
"org.freedesktop.zbus.async",
BitFlags::from(RequestNameFlags::ReplaceExisting),
),
)
.await
.unwrap();
assert!(reply.body_signature().map(|s| s == "u").unwrap());
let reply: RequestNameReply = reply.body().unwrap();
assert_eq!(reply, RequestNameReply::PrimaryOwner);
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetId",
&(),
)
.await
.unwrap();
assert!(reply
.body_signature()
.map(|s| s == <&str>::signature())
.unwrap());
let id: &str = reply.body().unwrap();
println!("Unique ID of the bus: {}", id);
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"NameHasOwner",
&"org.freedesktop.zbus.async",
)
.await
.unwrap();
assert!(reply
.body_signature()
.map(|s| s == bool::signature())
.unwrap());
assert!(reply.body::<bool>().unwrap());
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetNameOwner",
&"org.freedesktop.zbus.async",
)
.await
.unwrap();
assert!(reply
.body_signature()
.map(|s| s == <&str>::signature())
.unwrap());
assert_eq!(
Some(reply.body::<&str>().unwrap()),
connection.unique_name()
);
let reply = connection
.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetConnectionCredentials",
&"org.freedesktop.DBus",
)
.await
.unwrap();
assert!(reply.body_signature().map(|s| s == "a{sv}").unwrap());
let hashmap: HashMap<&str, OwnedValue> = reply.body().unwrap();
let pid: u32 = (&hashmap["ProcessID"]).try_into().unwrap();
println!("DBus bus PID: {}", pid);
let uid: u32 = (&hashmap["UnixUserID"]).try_into().unwrap();
println!("DBus bus UID: {}", uid);
Ok(())
}
#[test]
#[timeout(1000)]
fn issue_68() {
let conn = Connection::new_session().unwrap();
let client_conn = Connection::new_session().unwrap();
let msg = Message::method(
None,
conn.unique_name(),
"/org/freedesktop/Issue68",
Some("org.freedesktop.Issue68"),
"Ping",
&(),
)
.unwrap();
let serial = client_conn.send_message(msg).unwrap();
crate::fdo::DBusProxy::new(&conn).unwrap().get_id().unwrap();
loop {
let msg = conn.receive_message().unwrap();
if msg.primary_header().unwrap().serial_num() == serial {
break;
}
}
}
#[test]
#[timeout(1000)]
fn issue104() {
use std::{cell::RefCell, convert::TryFrom, rc::Rc};
use zvariant::{ObjectPath, OwnedObjectPath, Value};
let conn = Connection::new_session().unwrap();
let service_name = conn.unique_name().unwrap().to_string();
let mut object_server = super::ObjectServer::new(&conn);
struct Secret(Rc<RefCell<bool>>);
#[super::dbus_interface(name = "org.freedesktop.Secret.Service")]
impl Secret {
fn open_session(
&self,
_algorithm: &str,
input: Value<'_>,
) -> zbus::fdo::Result<(OwnedValue, OwnedObjectPath)> {
*self.0.borrow_mut() = true;
Ok((
OwnedValue::from(input),
ObjectPath::try_from("/org/freedesktop/secrets/Blah")
.unwrap()
.into(),
))
}
}
let quit = Rc::new(RefCell::new(false));
let secret = Secret(quit.clone());
object_server
.at(&"/org/freedesktop/secrets".try_into().unwrap(), secret)
.unwrap();
let child = std::thread::spawn(move || {
let conn = Connection::new_session().unwrap();
#[super::dbus_proxy(interface = "org.freedesktop.Secret.Service")]
trait Secret {
fn open_session(
&self,
algorithm: &str,
input: &zvariant::Value<'_>,
) -> zbus::Result<(zvariant::OwnedValue, zvariant::OwnedObjectPath)>;
}
let proxy =
SecretProxy::new_for(&conn, &service_name, "/org/freedesktop/secrets").unwrap();
proxy.open_session("plain", &Value::from("")).unwrap();
2u32
});
loop {
let m = conn.receive_message().unwrap();
if let Err(e) = object_server.dispatch_message(&m) {
eprintln!("{}", e);
}
if *quit.borrow() {
break;
}
}
let val = child.join().expect("failed to join");
assert_eq!(val, 2);
}
#[test]
fn connection_is_send_and_sync() {
accept_send_and_sync::<Connection>();
}
fn accept_send_and_sync<C: Send + Sync>() {}
#[test]
#[ignore]
fn issue_121() {
use crate::dbus_proxy;
#[dbus_proxy(interface = "org.freedesktop.IBus")]
trait IBus {
#[dbus_proxy(property)]
fn current_input_context(&self) -> zbus::Result<zvariant::OwnedObjectPath>;
#[dbus_proxy(property)]
fn engines(&self) -> zbus::Result<Vec<zvariant::OwnedValue>>;
}
}
#[test]
fn issue_122() {
let conn = Connection::new_session().unwrap();
let conn_clone = conn.clone();
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = Arc::clone(&pair);
let child = std::thread::spawn(move || {
{
let (lock, cvar) = &*pair2;
let mut started = lock.lock().unwrap();
*started = true;
cvar.notify_one();
}
while let Ok(msg) = conn_clone.receive_message() {
let hdr = msg.header().unwrap();
if hdr.member().unwrap() == Some("ZBusIssue122") {
break;
}
}
});
let (lock, cvar) = &*pair;
let mut started = lock.lock().unwrap();
while !*started {
started = cvar.wait(started).unwrap();
}
std::thread::sleep(std::time::Duration::from_millis(100));
let msg = Message::method(
None,
conn.unique_name(),
"/does/not/matter",
None,
"ZBusIssue122",
&(),
)
.unwrap();
conn.send_message(msg).unwrap();
child.join().unwrap();
}
}