Coverage Report

Created: 2026-03-28 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}