| 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 | #[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 | pub fn method_call_with_args<F: FnOnce(&mut Message)>(&self, i: &Interface, m: &Member, f: F) -> Result<Message, Error> { |
| 35 | let mut msg = Message::method_call(&self.dest, &self.path, i, m); |
| 36 | f(&mut msg); |
| 37 | self.conn.send_with_reply_and_block(msg, self.timeout) |
| 38 | } |
| 39 | |
| 40 | /// Emit a D-Bus signal, where you can append arguments inside the closure. |
| 41 | pub fn signal_with_args<F: FnOnce(&mut Message)>(&self, i: &Interface, m: &Member, f: F) -> Result<u32, Error> { |
| 42 | let mut msg = Message::signal(&self.path, i, m); |
| 43 | f(&mut msg); |
| 44 | self.conn.send(msg).map_err(|_| Error::new_failed("Sending signal failed" )) |
| 45 | } |
| 46 | |
| 47 | /// Emit a D-Bus signal, where the arguments are in a struct. |
| 48 | pub fn emit<S: SignalArgs + AppendAll>(&self, signal: &S) -> Result<u32, Error> { |
| 49 | let msg = signal.to_emit_message(&self.path); |
| 50 | self.conn.send(msg).map_err(|_| Error::new_failed("Sending signal failed" )) |
| 51 | } |
| 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 | 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 | let mut r = self.method_call_with_args(&i.into(), &m.into(), |mut msg| { |
| 68 | args.append(&mut IterAppend::new(&mut msg)); |
| 69 | })?; |
| 70 | r.as_result()?; |
| 71 | Ok(R::read(&mut r.iter_init())?) |
| 72 | } |
| 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 | #[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 | 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 | #[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 | fn from(m: Message) -> Self { |
| 124 | let mtype: MessageType = m.msg_type(); |
| 125 | match mtype { |
| 126 | MessageType::Signal => ConnectionItem::Signal(m), |
| 127 | MessageType::MethodReturn => ConnectionItem::MethodReturn(m), |
| 128 | MessageType::Error => ConnectionItem::MethodReturn(m), |
| 129 | MessageType::MethodCall => ConnectionItem::MethodCall(m), |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | |
| 135 | |
| 136 | |
| 137 | #[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 | fn matches_msg(&self, m: &Message) -> bool { |
| 153 | match *self { |
| 154 | MsgHandlerType::All => true, |
| 155 | MsgHandlerType::MsgType(t: MessageType) => m.msg_type() == t, |
| 156 | MsgHandlerType::Reply(serial: u32) => { |
| 157 | let t: MessageType = m.msg_type(); |
| 158 | ((t == MessageType::MethodReturn) || (t == MessageType::Error)) && (m.get_reply_serial() == Some(serial)) |
| 159 | } |
| 160 | } |
| 161 | } |
| 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 | fn handle_msg(&mut self, _msg: &Message) -> Option<MsgHandlerResult> { None } |
| 173 | } |
| 174 | |
| 175 | /// The result from MsgHandler::handle. |
| 176 | #[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 | fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::Reply(self.1) } |
| 196 | fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> { |
| 197 | let e: Result<&Message, Error> = match msg.msg_type() { |
| 198 | MessageType::MethodReturn => Ok(msg), |
| 199 | MessageType::Error => Err(msg.set_error_from_msg().unwrap_err()), |
| 200 | _ => unreachable!(), |
| 201 | }; |
| 202 | debug_assert_eq!(msg.get_reply_serial(), Some(self.1)); |
| 203 | self.0.take().unwrap()(e); |
| 204 | return Some(MsgHandlerResult { handled: true, done: true, reply: Vec::new() }) |
| 205 | } |
| 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 | } |
| 308 | |