1 //! D-Bus bindings for Rust
2 //!
3 //! [D-Bus](http://dbus.freedesktop.org/) is a message bus, and is mainly used in Linux
4 //! for communication between processes. It is present by default on almost every
5 //! Linux distribution out there, and runs in two instances - one per session, and one
6 //! system-wide.
7 //!
8 //! In addition to the API documentation, which you're currently reading, you might want to
9 //! look in the examples directory, which contains many examples and an argument guide.
10 //! README.md also contain a few quick "getting started" examples.
11 //!
12 //! In addition to this crate, there are two companion crates, dbus-codegen for generating Rust
13 //! code from D-Bus introspection data, and dbus-tokio for integrating D-Bus with [Tokio](http://tokio.rs).
14 //! However, at the time of this writing, these are far less mature than this crate.
15 
16 #![warn(missing_docs)]
17 
18 extern crate libc;
19 
20 pub use ffi::DBusBusType as BusType;
21 pub use connection::DBusNameFlag as NameFlag;
22 pub use ffi::DBusRequestNameReply as RequestNameReply;
23 pub use ffi::DBusReleaseNameReply as ReleaseNameReply;
24 pub use ffi::DBusMessageType as MessageType;
25 
26 pub use message::{Message, MessageItem, MessageItemArray, FromMessageItem, OwnedFd, ArrayError, ConnPath};
27 pub use connection::{Connection, ConnectionItems, ConnectionItem, ConnMsgs, MsgHandler, MsgHandlerResult, MsgHandlerType, MessageCallback};
28 pub use prop::PropHandler;
29 pub use prop::Props;
30 pub use watch::{Watch, WatchEvent};
31 pub use signalargs::SignalArgs;
32 
33 /// A TypeSig describes the type of a MessageItem.
34 #[deprecated(note="Use Signature instead")]
35 pub type TypeSig<'a> = std::borrow::Cow<'a, str>;
36 
37 use std::ffi::{CString, CStr};
38 use std::ptr;
39 use std::os::raw::c_char;
40 
41 #[allow(missing_docs)]
42 extern crate libdbus_sys as ffi;
43 mod message;
44 mod prop;
45 mod watch;
46 mod connection;
47 mod signalargs;
48 
49 mod matchrule;
50 pub use matchrule::MatchRule;
51 
52 mod strings;
53 pub use strings::{Signature, Path, Interface, Member, ErrorName, BusName};
54 
55 pub mod arg;
56 
57 pub mod stdintf;
58 
59 pub mod tree;
60 
61 static INITDBUS: std::sync::Once = std::sync::ONCE_INIT;
62 
init_dbus()63 fn init_dbus() {
64     INITDBUS.call_once(|| {
65         if unsafe { ffi::dbus_threads_init_default() } == 0 {
66             panic!("Out of memory when trying to initialize D-Bus library!");
67         }
68     });
69 }
70 
71 /// D-Bus Error wrapper.
72 pub struct Error {
73     e: ffi::DBusError,
74 }
75 
76 unsafe impl Send for Error {}
77 
78 // Note! For this Sync impl to be safe, it requires that no functions that take &self,
79 // actually calls into FFI. All functions that call into FFI with a ffi::DBusError
80 // must take &mut self.
81 
82 unsafe impl Sync for Error {}
83 
c_str_to_slice(c: & *const c_char) -> Option<&str>84 fn c_str_to_slice(c: & *const c_char) -> Option<&str> {
85     if *c == ptr::null() { None }
86     else { std::str::from_utf8( unsafe { CStr::from_ptr(*c).to_bytes() }).ok() }
87 }
88 
to_c_str(n: &str) -> CString89 fn to_c_str(n: &str) -> CString { CString::new(n.as_bytes()).unwrap() }
90 
91 impl Error {
92 
93     /// Create a new custom D-Bus Error.
new_custom(name: &str, message: &str) -> Error94     pub fn new_custom(name: &str, message: &str) -> Error {
95         let n = to_c_str(name);
96         let m = to_c_str(&message.replace("%","%%"));
97         let mut e = Error::empty();
98 
99         unsafe { ffi::dbus_set_error(e.get_mut(), n.as_ptr(), m.as_ptr()) };
100         e
101     }
102 
empty() -> Error103     fn empty() -> Error {
104         init_dbus();
105         let mut e = ffi::DBusError {
106             name: ptr::null(),
107             message: ptr::null(),
108             dummy: 0,
109             padding1: ptr::null()
110         };
111         unsafe { ffi::dbus_error_init(&mut e); }
112         Error{ e: e }
113     }
114 
115     /// Error name/type, e g 'org.freedesktop.DBus.Error.Failed'
name(&self) -> Option<&str>116     pub fn name(&self) -> Option<&str> {
117         c_str_to_slice(&self.e.name)
118     }
119 
120     /// Custom message, e g 'Could not find a matching object path'
message(&self) -> Option<&str>121     pub fn message(&self) -> Option<&str> {
122         c_str_to_slice(&self.e.message)
123     }
124 
get_mut(&mut self) -> &mut ffi::DBusError125     fn get_mut(&mut self) -> &mut ffi::DBusError { &mut self.e }
126 }
127 
128 impl Drop for Error {
drop(&mut self)129     fn drop(&mut self) {
130         unsafe { ffi::dbus_error_free(&mut self.e); }
131     }
132 }
133 
134 impl std::fmt::Debug for Error {
fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>135     fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
136         write!(f, "D-Bus error: {} ({})", self.message().unwrap_or(""),
137             self.name().unwrap_or(""))
138     }
139 }
140 
141 impl std::error::Error for Error {
description(&self) -> &str142     fn description(&self) -> &str { "D-Bus error" }
143 }
144 
145 impl std::fmt::Display for Error {
fmt(&self, f: &mut std::fmt::Formatter) -> Result<(),std::fmt::Error>146     fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(),std::fmt::Error> {
147         if let Some(x) = self.message() {
148              write!(f, "{:?}", x.to_string())
149         } else { Ok(()) }
150     }
151 }
152 
153 impl From<arg::TypeMismatchError> for Error {
from(t: arg::TypeMismatchError) -> Error154     fn from(t: arg::TypeMismatchError) -> Error {
155         Error::new_custom("org.freedesktop.DBus.Error.Failed", &format!("{}", t))
156     }
157 }
158 
159 impl From<tree::MethodErr> for Error {
from(t: tree::MethodErr) -> Error160     fn from(t: tree::MethodErr) -> Error {
161         Error::new_custom(t.errorname(), t.description())
162     }
163 }
164 
165 #[cfg(test)]
166 mod test {
167     use super::{Connection, Message, BusType, MessageItem, ConnectionItem, NameFlag,
168         RequestNameReply, ReleaseNameReply};
169 
170     #[test]
connection()171     fn connection() {
172         let c = Connection::get_private(BusType::Session).unwrap();
173         let n = c.unique_name();
174         assert!(n.starts_with(":1."));
175         println!("Connected to DBus, unique name: {}", n);
176     }
177 
178     #[test]
invalid_message()179     fn invalid_message() {
180         let c = Connection::get_private(BusType::Session).unwrap();
181         let m = Message::new_method_call("foo.bar", "/", "foo.bar", "FooBar").unwrap();
182         let e = c.send_with_reply_and_block(m, 2000).err().unwrap();
183         assert!(e.name().unwrap() == "org.freedesktop.DBus.Error.ServiceUnknown");
184     }
185 
186     #[test]
message_listnames()187     fn message_listnames() {
188         let c = Connection::get_private(BusType::Session).unwrap();
189         let m = Message::method_call(&"org.freedesktop.DBus".into(), &"/".into(),
190             &"org.freedesktop.DBus".into(), &"ListNames".into());
191         let r = c.send_with_reply_and_block(m, 2000).unwrap();
192         let reply = r.get_items();
193         println!("{:?}", reply);
194     }
195 
196     #[test]
message_namehasowner()197     fn message_namehasowner() {
198         let c = Connection::get_private(BusType::Session).unwrap();
199         let mut m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "NameHasOwner").unwrap();
200         m.append_items(&[MessageItem::Str("org.freedesktop.DBus".to_string())]);
201         let r = c.send_with_reply_and_block(m, 2000).unwrap();
202         let reply = r.get_items();
203         println!("{:?}", reply);
204         assert_eq!(reply, vec!(MessageItem::Bool(true)));
205     }
206 
207     #[test]
object_path()208     fn object_path() {
209         use  std::sync::mpsc;
210         let (tx, rx) = mpsc::channel();
211         let thread = ::std::thread::spawn(move || {
212             let c = Connection::get_private(BusType::Session).unwrap();
213             c.register_object_path("/hello").unwrap();
214             // println!("Waiting...");
215             tx.send(c.unique_name()).unwrap();
216             for n in c.iter(1000) {
217                 // println!("Found message... ({})", n);
218                 match n {
219                     ConnectionItem::MethodCall(ref m) => {
220                         let reply = Message::new_method_return(m).unwrap();
221                         c.send(reply).unwrap();
222                         break;
223                     }
224                     _ => {}
225                 }
226             }
227             c.unregister_object_path("/hello");
228         });
229 
230         let c = Connection::get_private(BusType::Session).unwrap();
231         let n = rx.recv().unwrap();
232         let m = Message::new_method_call(&n, "/hello", "com.example.hello", "Hello").unwrap();
233         println!("Sending...");
234         let r = c.send_with_reply_and_block(m, 8000).unwrap();
235         let reply = r.get_items();
236         println!("{:?}", reply);
237         thread.join().unwrap();
238 
239     }
240 
241     #[test]
register_name()242     fn register_name() {
243         let c = Connection::get_private(BusType::Session).unwrap();
244         let n = format!("com.example.hello.test.register_name");
245         assert_eq!(c.register_name(&n, NameFlag::ReplaceExisting as u32).unwrap(), RequestNameReply::PrimaryOwner);
246         assert_eq!(c.release_name(&n).unwrap(), ReleaseNameReply::Released);
247     }
248 
249     #[test]
signal()250     fn signal() {
251         let c = Connection::get_private(BusType::Session).unwrap();
252         let iface = "com.example.signaltest";
253         let mstr = format!("interface='{}',member='ThisIsASignal'", iface);
254         c.add_match(&mstr).unwrap();
255         let m = Message::new_signal("/mysignal", iface, "ThisIsASignal").unwrap();
256         let uname = c.unique_name();
257         c.send(m).unwrap();
258         for n in c.iter(1000) {
259             match n {
260                 ConnectionItem::Signal(s) => {
261                     let (_, p, i, m) = s.headers();
262                     match (&*p.unwrap(), &*i.unwrap(), &*m.unwrap()) {
263                         ("/mysignal", "com.example.signaltest", "ThisIsASignal") => {
264                             assert_eq!(&*s.sender().unwrap(), &*uname);
265                             break;
266                         },
267                         (_, _, _) => println!("Other signal: {:?}", s.headers()),
268                     }
269                 }
270                 _ => {},
271             }
272         }
273         c.remove_match(&mstr).unwrap();
274     }
275 
276 
277     #[test]
watch()278     fn watch() {
279         let c = Connection::get_private(BusType::Session).unwrap();
280         let d = c.watch_fds();
281         assert!(d.len() > 0);
282         println!("Fds to watch: {:?}", d);
283     }
284 }
285