Coverage Report

Created: 2024-05-20 06:38

/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
}