Revision control

Copy as Markdown

Other Tools

//! Connection base / building block.
//!
//! Contains some helper structs and traits common to all Connection types.-
use crate::{Message, to_c_str, c_str_to_slice, MessageType};
use crate::message::MatchRule;
#[cfg(not(feature = "native-channel"))]
mod ffichannel;
#[cfg(not(feature = "native-channel"))]
pub use ffichannel::Channel;
#[cfg(feature = "native-channel")]
mod nativechannel;
#[cfg(feature = "native-channel")]
pub use nativechannel::Channel;
/// Which bus to connect to
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub enum BusType {
/// The Session bus - local to every logged in session
Session = ffi::DBusBusType::Session as isize,
/// The system wide bus
System = ffi::DBusBusType::System as isize,
/// The bus that started us, if any
Starter = ffi::DBusBusType::Starter as isize,
}
/// Platform-specific file descriptor type
#[cfg(unix)]
pub type WatchFd = std::os::unix::io::RawFd;
/// Platform-specific file descriptor type
#[cfg(windows)]
pub type WatchFd = std::os::windows::io::RawSocket;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
/// A file descriptor, and an indication whether it should be read from, written to, or both.
pub struct Watch {
/// File descriptor
pub fd: WatchFd,
/// True if wakeup should happen when the file descriptor is ready for reading
pub read: bool,
/// True if wakeup should happen when the file descriptor is ready for writing
pub write: bool,
}
/// Abstraction over different connections that send data
pub trait Sender {
/// Schedules a message for sending.
///
/// Returns a serial number than can be used to match against a reply.
fn send(&self, msg: Message) -> Result<u32, ()>;
}
/// Use in case you don't want the send the message, but just collect it instead.
impl Sender for std::cell::RefCell<Vec<Message>> {
fn send(&self, msg: Message) -> Result<u32, ()> {
self.borrow_mut().push(msg);
Ok(0)
}
}
/// Use in case you don't want the send the message, but just collect it instead.
impl Sender for std::sync::Mutex<Vec<Message>> {
fn send(&self, msg: Message) -> Result<u32, ()> {
self.lock().unwrap().push(msg);
Ok(0)
}
}
/// Token used to identify a callback in the MatchingReceiver trait
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Token(pub usize);
/// Abstraction over different connections that receive data
pub trait MatchingReceiver {
/// Type of callback
type F;
/// Add a callback to be called in case a message matches.
///
/// Returns an id that can be used to remove the callback.
fn start_receive(&self, m: MatchRule<'static>, f: Self::F) -> Token;
/// Remove a previously added callback.
fn stop_receive(&self, id: Token) -> Option<(MatchRule<'static>, Self::F)>;
}
impl Sender for Channel {
fn send(&self, msg: Message) -> Result<u32, ()> { Channel::send(self, msg) }
}
/// Handles what we need to be a good D-Bus citizen.
///
/// Call this if you have not handled the message yourself:
/// * It handles calls to org.freedesktop.DBus.Peer.
/// * For other method calls, it sends an error reply back that the method was unknown.
pub fn default_reply(m: &Message) -> Option<Message> {
peer(&m).or_else(|| unknown_method(&m))
}
/// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None.
fn peer(m: &Message) -> Option<Message> {
if let Some(intf) = m.interface() {
if &*intf != "org.freedesktop.DBus.Peer" { return None; }
if let Some(method) = m.member() {
if &*method == "Ping" { return Some(m.method_return()) }
if &*method == "GetMachineId" {
let mut r = m.method_return();
unsafe {
let id = ffi::dbus_get_local_machine_id();
if !id.is_null() {
r = r.append1(c_str_to_slice(&(id as *const _)).unwrap());
ffi::dbus_free(id as *mut _);
return Some(r)
}
}
return Some(m.error(&"org.freedesktop.DBus.Error.Failed".into(), &to_c_str("Failed to retreive UUID")))
}
}
Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Method does not exist")))
} else { None }
}
/// For method calls, it replies that the method was unknown, otherwise returns None.
fn unknown_method(m: &Message) -> Option<Message> {
if m.msg_type() != MessageType::MethodCall { return None; }
// if m.get_no_reply() { return None; } // The reference implementation does not do this?
Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist")))
}
#[test]
fn test_channel_send_sync() {
fn is_send<T: Send>(_: &T) {}
fn is_sync<T: Sync>(_: &T) {}
let c = Channel::get_private(BusType::Session).unwrap();
is_send(&c);
is_sync(&c);
}
#[test]
fn channel_simple_test() {
let mut c = Channel::get_private(BusType::Session).unwrap();
assert!(c.is_connected());
c.set_watch_enabled(true);
let fd = c.watch();
println!("{:?}", fd);
let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap();
let reply = c.send(m).unwrap();
let my_name = c.unique_name().unwrap();
loop {
while let Some(mut msg) = c.pop_message() {
println!("{:?}", msg);
if msg.get_reply_serial() == Some(reply) {
let r = msg.as_result().unwrap();
let z: crate::arg::Array<&str, _> = r.get1().unwrap();
for n in z {
println!("{}", n);
if n == my_name { return; } // Hooray, we found ourselves!
}
assert!(false);
} else if let Some(r) = default_reply(&msg) {
c.send(r).unwrap();
}
}
c.read_write(Some(std::time::Duration::from_millis(100))).unwrap();
}
}
#[test]
fn test_bus_type_is_compatible_with_set() {
use std::collections::HashSet;
let mut set: HashSet<BusType> = HashSet::new();
set.insert(BusType::Starter);
set.insert(BusType::Starter);
assert_eq!(set.len(), 1);
assert!(!set.contains(&BusType::Session));
assert!(!set.contains(&BusType::System));
assert!(set.contains(&BusType::Starter));
}
#[test]
fn watchmap() {
let mut c = Channel::get_private(BusType::Session).unwrap();
c.set_watch_enabled(true);
let w = c.watch();
assert_eq!(w.write, false);
assert_eq!(w.read, true);
c.set_watch_enabled(false);
println!("{:?}", w);
c.set_watch_enabled(true);
}