/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dbus-0.9.7/src/channel.rs
Line | Count | Source |
1 | | //! Connection base / building block. |
2 | | //! |
3 | | //! Contains some helper structs and traits common to all Connection types.- |
4 | | |
5 | | use crate::{Message, to_c_str, c_str_to_slice, MessageType}; |
6 | | use crate::message::MatchRule; |
7 | | |
8 | | #[cfg(not(feature = "native-channel"))] |
9 | | mod ffichannel; |
10 | | #[cfg(not(feature = "native-channel"))] |
11 | | pub use ffichannel::Channel; |
12 | | |
13 | | #[cfg(feature = "native-channel")] |
14 | | mod nativechannel; |
15 | | #[cfg(feature = "native-channel")] |
16 | | pub use nativechannel::Channel; |
17 | | |
18 | | |
19 | | /// Which bus to connect to |
20 | | #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] |
21 | | pub 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)] |
32 | | pub type WatchFd = std::os::unix::io::RawFd; |
33 | | |
34 | | /// Platform-specific file descriptor type |
35 | | #[cfg(windows)] |
36 | | pub 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. |
40 | | pub 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 |
50 | | pub 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. |
58 | | impl Sender for std::cell::RefCell<Vec<Message>> { |
59 | 0 | fn send(&self, msg: Message) -> Result<u32, ()> { |
60 | 0 | self.borrow_mut().push(msg); |
61 | 0 | Ok(0) |
62 | 0 | } |
63 | | } |
64 | | |
65 | | /// Use in case you don't want the send the message, but just collect it instead. |
66 | | impl Sender for std::sync::Mutex<Vec<Message>> { |
67 | 0 | fn send(&self, msg: Message) -> Result<u32, ()> { |
68 | 0 | self.lock().unwrap().push(msg); |
69 | 0 | Ok(0) |
70 | 0 | } |
71 | | } |
72 | | |
73 | | /// Token used to identify a callback in the MatchingReceiver trait |
74 | | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
75 | | pub struct Token(pub usize); |
76 | | |
77 | | /// Abstraction over different connections that receive data |
78 | | pub 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 | | |
89 | | impl Sender for Channel { |
90 | 0 | 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. |
98 | 0 | pub fn default_reply(m: &Message) -> Option<Message> { |
99 | 0 | peer(&m).or_else(|| unknown_method(&m)) |
100 | 0 | } |
101 | | |
102 | | /// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None. |
103 | 0 | fn peer(m: &Message) -> Option<Message> { |
104 | 0 | if let Some(intf) = m.interface() { |
105 | 0 | if &*intf != "org.freedesktop.DBus.Peer" { return None; } |
106 | 0 | if let Some(method) = m.member() { |
107 | 0 | if &*method == "Ping" { return Some(m.method_return()) } |
108 | 0 | if &*method == "GetMachineId" { |
109 | 0 | let mut r = m.method_return(); |
110 | | unsafe { |
111 | 0 | let id = ffi::dbus_get_local_machine_id(); |
112 | 0 | if !id.is_null() { |
113 | 0 | r = r.append1(c_str_to_slice(&(id as *const _)).unwrap()); |
114 | 0 | ffi::dbus_free(id as *mut _); |
115 | 0 | return Some(r) |
116 | 0 | } |
117 | | } |
118 | 0 | return Some(m.error(&"org.freedesktop.DBus.Error.Failed".into(), &to_c_str("Failed to retreive UUID"))) |
119 | 0 | } |
120 | 0 | } |
121 | 0 | Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Method does not exist"))) |
122 | 0 | } else { None } |
123 | 0 | } |
124 | | |
125 | | /// For method calls, it replies that the method was unknown, otherwise returns None. |
126 | 0 | fn unknown_method(m: &Message) -> Option<Message> { |
127 | 0 | if m.msg_type() != MessageType::MethodCall { return None; } |
128 | | // if m.get_no_reply() { return None; } // The reference implementation does not do this? |
129 | 0 | Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist"))) |
130 | 0 | } |
131 | | |
132 | | #[test] |
133 | | fn 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] |
142 | | fn 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] |
171 | | fn 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] |
186 | | fn 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 | | } |