/rust/registry/src/index.crates.io-6f17d22bba15001f/dbus-0.9.7/src/ffidisp.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! A connection that uses FFI callbacks to dispatch messages. |
2 | | //! |
3 | | //! This is the legacy design used up to 0.6.x. It is not recommended for new development. |
4 | | |
5 | | |
6 | | use super::{Error, ffi, Message, MessageType}; |
7 | | use crate::strings::{BusName, Path, Member, Interface}; |
8 | | use crate::arg::{AppendAll, ReadAll, IterAppend}; |
9 | | use crate::message::SignalArgs; |
10 | | |
11 | | pub mod stdintf; |
12 | | |
13 | | mod connection; |
14 | | |
15 | | pub use connection::{Connection, ConnMsgs}; |
16 | | |
17 | | /// A convenience struct that wraps connection, destination and path. |
18 | | /// |
19 | | /// Useful if you want to make many method calls to the same destination path. |
20 | 0 | #[derive(Clone, Debug)] |
21 | | pub struct ConnPath<'a, C> { |
22 | | /// Some way to access the connection, e g a &Connection or Rc<Connection> |
23 | | pub conn: C, |
24 | | /// Destination, i e what D-Bus service you're communicating with |
25 | | pub dest: BusName<'a>, |
26 | | /// Object path on the destination |
27 | | pub path: Path<'a>, |
28 | | /// Timeout in milliseconds for blocking method calls |
29 | | pub timeout: i32, |
30 | | } |
31 | | |
32 | | impl<'a, C: ::std::ops::Deref<Target=Connection>> ConnPath<'a, C> { |
33 | | /// Make a D-Bus method call, where you can append arguments inside the closure. |
34 | 0 | pub fn method_call_with_args<F: FnOnce(&mut Message)>(&self, i: &Interface, m: &Member, f: F) -> Result<Message, Error> { |
35 | 0 | let mut msg = Message::method_call(&self.dest, &self.path, i, m); |
36 | 0 | f(&mut msg); |
37 | 0 | self.conn.send_with_reply_and_block(msg, self.timeout) |
38 | 0 | } |
39 | | |
40 | | /// Emit a D-Bus signal, where you can append arguments inside the closure. |
41 | 0 | pub fn signal_with_args<F: FnOnce(&mut Message)>(&self, i: &Interface, m: &Member, f: F) -> Result<u32, Error> { |
42 | 0 | let mut msg = Message::signal(&self.path, i, m); |
43 | 0 | f(&mut msg); |
44 | 0 | self.conn.send(msg).map_err(|_| Error::new_failed("Sending signal failed")) |
45 | 0 | } |
46 | | |
47 | | /// Emit a D-Bus signal, where the arguments are in a struct. |
48 | 0 | pub fn emit<S: SignalArgs + AppendAll>(&self, signal: &S) -> Result<u32, Error> { |
49 | 0 | let msg = signal.to_emit_message(&self.path); |
50 | 0 | self.conn.send(msg).map_err(|_| Error::new_failed("Sending signal failed")) |
51 | 0 | } |
52 | | |
53 | | /// Make a method call using typed input and output arguments. |
54 | | /// |
55 | | /// # Example |
56 | | /// |
57 | | /// ``` |
58 | | /// use dbus::ffidisp::{Connection, BusType}; |
59 | | /// |
60 | | /// let conn = Connection::get_private(BusType::Session)?; |
61 | | /// let dest = conn.with_path("org.freedesktop.DBus", "/", 5000); |
62 | | /// let (has_owner,): (bool,) = dest.method_call("org.freedesktop.DBus", "NameHasOwner", ("dummy.name.without.owner",))?; |
63 | | /// assert_eq!(has_owner, false); |
64 | | /// # Ok::<(), Box<dyn std::error::Error>>(()) |
65 | | /// ``` |
66 | 0 | pub fn method_call<'i, 'm, R: ReadAll, A: AppendAll, I: Into<Interface<'i>>, M: Into<Member<'m>>>(&self, i: I, m: M, args: A) -> Result<R, Error> { |
67 | 0 | let mut r = self.method_call_with_args(&i.into(), &m.into(), |mut msg| { |
68 | 0 | args.append(&mut IterAppend::new(&mut msg)); |
69 | 0 | })?; |
70 | 0 | r.as_result()?; |
71 | 0 | Ok(R::read(&mut r.iter_init())?) |
72 | 0 | } |
73 | | } |
74 | | |
75 | | /// The type of function to use for replacing the message callback. |
76 | | /// |
77 | | /// See the documentation for Connection::replace_message_callback for more information. |
78 | | pub type MessageCallback = Box<dyn FnMut(&Connection, Message) -> bool + 'static>; |
79 | | |
80 | | pub use crate::ffi::DBusRequestNameReply as RequestNameReply; |
81 | | pub use crate::ffi::DBusReleaseNameReply as ReleaseNameReply; |
82 | | pub use crate::ffi::DBusBusType as BusType; |
83 | | |
84 | | mod watch; |
85 | | |
86 | | pub use self::watch::{Watch, WatchEvent}; |
87 | | use watch::WatchList; |
88 | | |
89 | | #[repr(C)] |
90 | 0 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] |
91 | | /// Flags to use for Connection::register_name. |
92 | | /// |
93 | | /// More than one flag can be specified, if so just add their values. |
94 | | pub enum NameFlag { |
95 | | /// Allow another service to become the primary owner if requested |
96 | | AllowReplacement = ffi::DBUS_NAME_FLAG_ALLOW_REPLACEMENT as isize, |
97 | | /// Request to replace the current primary owner |
98 | | ReplaceExisting = ffi::DBUS_NAME_FLAG_REPLACE_EXISTING as isize, |
99 | | /// If we can not become the primary owner do not place us in the queue |
100 | | DoNotQueue = ffi::DBUS_NAME_FLAG_DO_NOT_QUEUE as isize, |
101 | | } |
102 | | |
103 | | impl NameFlag { |
104 | | /// u32 value of flag. |
105 | 0 | pub fn value(self) -> u32 { self as u32 } |
106 | | } |
107 | | |
108 | | /// When listening for incoming events on the D-Bus, this enum will tell you what type |
109 | | /// of incoming event has happened. |
110 | 0 | #[derive(Debug)] |
111 | | pub enum ConnectionItem { |
112 | | /// No event between now and timeout |
113 | | Nothing, |
114 | | /// Incoming method call |
115 | | MethodCall(Message), |
116 | | /// Incoming signal |
117 | | Signal(Message), |
118 | | /// Incoming method return, including method return errors (mostly used for Async I/O) |
119 | | MethodReturn(Message), |
120 | | } |
121 | | |
122 | | impl From<Message> for ConnectionItem { |
123 | 0 | fn from(m: Message) -> Self { |
124 | 0 | let mtype = m.msg_type(); |
125 | 0 | match mtype { |
126 | 0 | MessageType::Signal => ConnectionItem::Signal(m), |
127 | 0 | MessageType::MethodReturn => ConnectionItem::MethodReturn(m), |
128 | 0 | MessageType::Error => ConnectionItem::MethodReturn(m), |
129 | 0 | MessageType::MethodCall => ConnectionItem::MethodCall(m), |
130 | | } |
131 | 0 | } |
132 | | } |
133 | | |
134 | | |
135 | | |
136 | | |
137 | 0 | #[derive(Clone, Debug)] |
138 | | /// Type of messages to be handled by a MsgHandler. |
139 | | /// |
140 | | /// Note: More variants can be added in the future; but unless you're writing your own D-Bus engine |
141 | | /// you should not have to match on these anyway. |
142 | | pub enum MsgHandlerType { |
143 | | /// Handle all messages |
144 | | All, |
145 | | /// Handle only messages of a specific type |
146 | | MsgType(MessageType), |
147 | | /// Handle only method replies with this serial number |
148 | | Reply(u32), |
149 | | } |
150 | | |
151 | | impl MsgHandlerType { |
152 | 0 | fn matches_msg(&self, m: &Message) -> bool { |
153 | 0 | match *self { |
154 | 0 | MsgHandlerType::All => true, |
155 | 0 | MsgHandlerType::MsgType(t) => m.msg_type() == t, |
156 | 0 | MsgHandlerType::Reply(serial) => { |
157 | 0 | let t = m.msg_type(); |
158 | 0 | ((t == MessageType::MethodReturn) || (t == MessageType::Error)) && (m.get_reply_serial() == Some(serial)) |
159 | | } |
160 | | } |
161 | 0 | } |
162 | | } |
163 | | |
164 | | /// A trait for handling incoming messages. |
165 | | pub trait MsgHandler { |
166 | | /// Type of messages for which the handler will be called |
167 | | /// |
168 | | /// Note: The return value of this function might be cached, so it must return the same value all the time. |
169 | | fn handler_type(&self) -> MsgHandlerType; |
170 | | |
171 | | /// Function to be called if the message matches the MsgHandlerType |
172 | 0 | fn handle_msg(&mut self, _msg: &Message) -> Option<MsgHandlerResult> { None } |
173 | | } |
174 | | |
175 | | /// The result from MsgHandler::handle. |
176 | 0 | #[derive(Debug, Default)] |
177 | | pub struct MsgHandlerResult { |
178 | | /// Indicates that the message has been dealt with and should not be processed further. |
179 | | pub handled: bool, |
180 | | /// Indicates that this MsgHandler no longer wants to receive messages and should be removed. |
181 | | pub done: bool, |
182 | | /// Messages to send (e g, a reply to a method call) |
183 | | pub reply: Vec<Message>, |
184 | | } |
185 | | |
186 | | |
187 | | type MsgHandlerList = Vec<Box<dyn MsgHandler>>; |
188 | | |
189 | | /// The struct returned from `Connection::send_and_reply`. |
190 | | /// |
191 | | /// It implements the `MsgHandler` trait so you can use `Connection::add_handler`. |
192 | | pub struct MessageReply<F>(Option<F>, u32); |
193 | | |
194 | | impl<'a, F: FnOnce(Result<&Message, Error>) + 'a> MsgHandler for MessageReply<F> { |
195 | 0 | fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::Reply(self.1) } |
196 | 0 | fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> { |
197 | 0 | let e = match msg.msg_type() { |
198 | 0 | MessageType::MethodReturn => Ok(msg), |
199 | 0 | MessageType::Error => Err(msg.set_error_from_msg().unwrap_err()), |
200 | 0 | _ => unreachable!(), |
201 | | }; |
202 | 0 | debug_assert_eq!(msg.get_reply_serial(), Some(self.1)); |
203 | 0 | self.0.take().unwrap()(e); |
204 | 0 | return Some(MsgHandlerResult { handled: true, done: true, reply: Vec::new() }) |
205 | 0 | } |
206 | | } |
207 | | |
208 | | #[cfg(test)] |
209 | | mod test { |
210 | | use super::{Connection, BusType, ConnectionItem, NameFlag, |
211 | | RequestNameReply, ReleaseNameReply}; |
212 | | use crate::Message; |
213 | | |
214 | | #[test] |
215 | | fn connection() { |
216 | | let c = Connection::get_private(BusType::Session).unwrap(); |
217 | | let n = c.unique_name(); |
218 | | assert!(n.starts_with(":1.")); |
219 | | println!("Connected to DBus, unique name: {}", n); |
220 | | } |
221 | | |
222 | | #[test] |
223 | | fn invalid_message() { |
224 | | let c = Connection::get_private(BusType::Session).unwrap(); |
225 | | let m = Message::new_method_call("foo.bar", "/", "foo.bar", "FooBar").unwrap(); |
226 | | let e = c.send_with_reply_and_block(m, 2000).err().unwrap(); |
227 | | assert!(e.name().unwrap() == "org.freedesktop.DBus.Error.ServiceUnknown"); |
228 | | } |
229 | | |
230 | | #[test] |
231 | | fn object_path() { |
232 | | use std::sync::mpsc; |
233 | | let (tx, rx) = mpsc::channel(); |
234 | | let thread = ::std::thread::spawn(move || { |
235 | | let c = Connection::get_private(BusType::Session).unwrap(); |
236 | | c.register_object_path("/hello").unwrap(); |
237 | | // println!("Waiting..."); |
238 | | tx.send(c.unique_name()).unwrap(); |
239 | | for n in c.iter(1000) { |
240 | | // println!("Found message... ({})", n); |
241 | | match n { |
242 | | ConnectionItem::MethodCall(ref m) => { |
243 | | let reply = Message::new_method_return(m).unwrap(); |
244 | | c.send(reply).unwrap(); |
245 | | break; |
246 | | } |
247 | | _ => {} |
248 | | } |
249 | | } |
250 | | c.unregister_object_path("/hello"); |
251 | | }); |
252 | | |
253 | | let c = Connection::get_private(BusType::Session).unwrap(); |
254 | | let n = rx.recv().unwrap(); |
255 | | let m = Message::new_method_call(&n, "/hello", "com.example.hello", "Hello").unwrap(); |
256 | | println!("Sending..."); |
257 | | let r = c.send_with_reply_and_block(m, 8000).unwrap(); |
258 | | let reply = r.get_items(); |
259 | | println!("{:?}", reply); |
260 | | thread.join().unwrap(); |
261 | | |
262 | | } |
263 | | |
264 | | #[test] |
265 | | fn register_name() { |
266 | | let c = Connection::get_private(BusType::Session).unwrap(); |
267 | | let n = format!("com.example.hello.test.register_name"); |
268 | | assert_eq!(c.register_name(&n, NameFlag::ReplaceExisting as u32).unwrap(), RequestNameReply::PrimaryOwner); |
269 | | assert_eq!(c.release_name(&n).unwrap(), ReleaseNameReply::Released); |
270 | | } |
271 | | |
272 | | #[test] |
273 | | fn signal() { |
274 | | let c = Connection::get_private(BusType::Session).unwrap(); |
275 | | let iface = "com.example.signaltest"; |
276 | | let mstr = format!("interface='{}',member='ThisIsASignal'", iface); |
277 | | c.add_match(&mstr).unwrap(); |
278 | | let m = Message::new_signal("/mysignal", iface, "ThisIsASignal").unwrap(); |
279 | | let uname = c.unique_name(); |
280 | | c.send(m).unwrap(); |
281 | | for n in c.iter(1000) { |
282 | | match n { |
283 | | ConnectionItem::Signal(s) => { |
284 | | let (p, i, m) = (s.path(), s.interface(), s.member()); |
285 | | match (&*p.unwrap(), &*i.unwrap(), &*m.unwrap()) { |
286 | | ("/mysignal", "com.example.signaltest", "ThisIsASignal") => { |
287 | | assert_eq!(&*s.sender().unwrap(), &*uname); |
288 | | break; |
289 | | }, |
290 | | (_, _, _) => println!("Other signal: {:?}", s), |
291 | | } |
292 | | } |
293 | | _ => {}, |
294 | | } |
295 | | } |
296 | | c.remove_match(&mstr).unwrap(); |
297 | | } |
298 | | |
299 | | |
300 | | #[test] |
301 | | fn watch() { |
302 | | let c = Connection::get_private(BusType::Session).unwrap(); |
303 | | let d = c.watch_fds(); |
304 | | assert!(d.len() > 0); |
305 | | println!("Fds to watch: {:?}", d); |
306 | | } |
307 | | } |