1//! Connection base / building block.
2//!
3//! Contains some helper structs and traits common to all Connection types.-
4
5use crate::{Message, to_c_str, c_str_to_slice, MessageType};
6use crate::message::MatchRule;
7
8#[cfg(not(feature = "native-channel"))]
9mod ffichannel;
10#[cfg(not(feature = "native-channel"))]
11pub use ffichannel::Channel;
12
13#[cfg(feature = "native-channel")]
14mod nativechannel;
15#[cfg(feature = "native-channel")]
16pub use nativechannel::Channel;
17
18
19/// Which bus to connect to
20#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
21pub enum BusType {
22 /// The Session bus - local to every logged in session
23 Session = ffi::DBusBusType::Session as isize,
24 /// The system wide bus
25 System = ffi::DBusBusType::System as isize,
26 /// The bus that started us, if any
27 Starter = ffi::DBusBusType::Starter as isize,
28}
29
30/// Platform-specific file descriptor type
31#[cfg(unix)]
32pub type WatchFd = std::os::unix::io::RawFd;
33
34/// Platform-specific file descriptor type
35#[cfg(windows)]
36pub type WatchFd = std::os::windows::io::RawSocket;
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
39/// A file descriptor, and an indication whether it should be read from, written to, or both.
40pub struct Watch {
41 /// File descriptor
42 pub fd: WatchFd,
43 /// True if wakeup should happen when the file descriptor is ready for reading
44 pub read: bool,
45 /// True if wakeup should happen when the file descriptor is ready for writing
46 pub write: bool,
47}
48
49/// Abstraction over different connections that send data
50pub trait Sender {
51 /// Schedules a message for sending.
52 ///
53 /// Returns a serial number than can be used to match against a reply.
54 fn send(&self, msg: Message) -> Result<u32, ()>;
55}
56
57/// Use in case you don't want the send the message, but just collect it instead.
58impl Sender for std::cell::RefCell<Vec<Message>> {
59 fn send(&self, msg: Message) -> Result<u32, ()> {
60 self.borrow_mut().push(msg);
61 Ok(0)
62 }
63}
64
65/// Use in case you don't want the send the message, but just collect it instead.
66impl Sender for std::sync::Mutex<Vec<Message>> {
67 fn send(&self, msg: Message) -> Result<u32, ()> {
68 self.lock().unwrap().push(msg);
69 Ok(0)
70 }
71}
72
73/// Token used to identify a callback in the MatchingReceiver trait
74#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
75pub struct Token(pub usize);
76
77/// Abstraction over different connections that receive data
78pub trait MatchingReceiver {
79 /// Type of callback
80 type F;
81 /// Add a callback to be called in case a message matches.
82 ///
83 /// Returns an id that can be used to remove the callback.
84 fn start_receive(&self, m: MatchRule<'static>, f: Self::F) -> Token;
85 /// Remove a previously added callback.
86 fn stop_receive(&self, id: Token) -> Option<(MatchRule<'static>, Self::F)>;
87}
88
89impl Sender for Channel {
90 fn send(&self, msg: Message) -> Result<u32, ()> { Channel::send(self, msg) }
91}
92
93/// Handles what we need to be a good D-Bus citizen.
94///
95/// Call this if you have not handled the message yourself:
96/// * It handles calls to org.freedesktop.DBus.Peer.
97/// * For other method calls, it sends an error reply back that the method was unknown.
98pub fn default_reply(m: &Message) -> Option<Message> {
99 peer(&m).or_else(|| unknown_method(&m))
100}
101
102/// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None.
103fn peer(m: &Message) -> Option<Message> {
104 if let Some(intf: Interface<'_>) = m.interface() {
105 if &*intf != "org.freedesktop.DBus.Peer" { return None; }
106 if let Some(method: Member<'_>) = m.member() {
107 if &*method == "Ping" { return Some(m.method_return()) }
108 if &*method == "GetMachineId" {
109 let mut r: Message = m.method_return();
110 unsafe {
111 let id: *mut i8 = ffi::dbus_get_local_machine_id();
112 if !id.is_null() {
113 r = r.append1(c_str_to_slice(&(id as *const _)).unwrap());
114 ffi::dbus_free(memory:id as *mut _);
115 return Some(r)
116 }
117 }
118 return Some(m.error(&"org.freedesktop.DBus.Error.Failed".into(), &to_c_str("Failed to retreive UUID")))
119 }
120 }
121 Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Method does not exist")))
122 } else { None }
123}
124
125/// For method calls, it replies that the method was unknown, otherwise returns None.
126fn unknown_method(m: &Message) -> Option<Message> {
127 if m.msg_type() != MessageType::MethodCall { return None; }
128 // if m.get_no_reply() { return None; } // The reference implementation does not do this?
129 Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist")))
130}
131
132#[test]
133fn test_channel_send_sync() {
134 fn is_send<T: Send>(_: &T) {}
135 fn is_sync<T: Sync>(_: &T) {}
136 let c = Channel::get_private(BusType::Session).unwrap();
137 is_send(&c);
138 is_sync(&c);
139}
140
141#[test]
142fn channel_simple_test() {
143 let mut c = Channel::get_private(BusType::Session).unwrap();
144 assert!(c.is_connected());
145 c.set_watch_enabled(true);
146 let fd = c.watch();
147 println!("{:?}", fd);
148 let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap();
149 let reply = c.send(m).unwrap();
150 let my_name = c.unique_name().unwrap();
151 loop {
152 while let Some(mut msg) = c.pop_message() {
153 println!("{:?}", msg);
154 if msg.get_reply_serial() == Some(reply) {
155 let r = msg.as_result().unwrap();
156 let z: crate::arg::Array<&str, _> = r.get1().unwrap();
157 for n in z {
158 println!("{}", n);
159 if n == my_name { return; } // Hooray, we found ourselves!
160 }
161 assert!(false);
162 } else if let Some(r) = default_reply(&msg) {
163 c.send(r).unwrap();
164 }
165 }
166 c.read_write(Some(std::time::Duration::from_millis(100))).unwrap();
167 }
168}
169
170#[test]
171fn test_bus_type_is_compatible_with_set() {
172 use std::collections::HashSet;
173
174 let mut set: HashSet<BusType> = HashSet::new();
175 set.insert(BusType::Starter);
176 set.insert(BusType::Starter);
177
178 assert_eq!(set.len(), 1);
179 assert!(!set.contains(&BusType::Session));
180 assert!(!set.contains(&BusType::System));
181 assert!(set.contains(&BusType::Starter));
182}
183
184
185#[test]
186fn watchmap() {
187 let mut c = Channel::get_private(BusType::Session).unwrap();
188 c.set_watch_enabled(true);
189 let w = c.watch();
190 assert_eq!(w.write, false);
191 assert_eq!(w.read, true);
192 c.set_watch_enabled(false);
193 println!("{:?}", w);
194 c.set_watch_enabled(true);
195}
196