// This is a modified version of dbus/examples/adv_server.rs that uses code generated from dbus-codegen. // // Usually you would put imported code in a separate .rs file, but I've just copy-pasted it into // a module here, for simplicity. mod com_example_dbus_rs { // === Imported code start === // This code was autogenerated with dbus-codegen-rust, see https://github.com/diwic/dbus-rs #![allow(dead_code)] use dbus as dbus; use dbus::arg; use dbus_tree as tree; use dbus::ffidisp; pub trait Device { type Err; fn check(&self) -> Result<(), Self::Err>; fn get_checking(&self) -> Result; fn get_description(&self) -> Result; fn get_online(&self) -> Result; fn set_online(&self, value: bool) -> Result<(), Self::Err>; } impl<'a, C: ::std::ops::Deref> Device for ffidisp::ConnPath<'a, C> { type Err = dbus::Error; fn check(&self) -> Result<(), Self::Err> { let mut m = self.method_call_with_args(&"com.example.dbus.rs.device".into(), &"check".into(), |_| { })?; m.as_result()?; Ok(()) } fn get_checking(&self) -> Result { ::get(&self, "com.example.dbus.rs.device", "checking") } fn get_description(&self) -> Result { ::get(&self, "com.example.dbus.rs.device", "description") } fn get_online(&self) -> Result { ::get(&self, "com.example.dbus.rs.device", "online") } fn set_online(&self, value: bool) -> Result<(), Self::Err> { ::set(&self, "com.example.dbus.rs.device", "online", value) } } pub fn device_server(factory: &tree::Factory, D>, data: D::Interface, f: F) -> tree::Interface, D> where D: tree::DataType, D::Method: Default, D::Property: Default, D::Signal: Default, T: Device, F: 'static + for <'z> Fn(& 'z tree::MethodInfo, D>) -> & 'z T, { let i = factory.interface("com.example.dbus.rs.device", data); let f = ::std::sync::Arc::new(f); let fclone = f.clone(); let h = move |minfo: &tree::MethodInfo, D>| { let d = fclone(minfo); d.check()?; let rm = minfo.msg.method_return(); Ok(vec!(rm)) }; let m = factory.method("check", Default::default(), h); let i = i.add_m(m); let p = factory.property::("checking", Default::default()); let p = p.access(tree::Access::Read); let fclone = f.clone(); let p = p.on_get(move |a, pinfo| { let minfo = pinfo.to_method_info(); let d = fclone(&minfo); a.append(d.get_checking()?); Ok(()) }); let i = i.add_p(p); let p = factory.property::<&str, _>("description", Default::default()); let p = p.access(tree::Access::Read); let fclone = f.clone(); let p = p.on_get(move |a, pinfo| { let minfo = pinfo.to_method_info(); let d = fclone(&minfo); a.append(d.get_description()?); Ok(()) }); let i = i.add_p(p); let p = factory.property::("online", Default::default()); let p = p.access(tree::Access::ReadWrite); let fclone = f.clone(); let p = p.on_get(move |a, pinfo| { let minfo = pinfo.to_method_info(); let d = fclone(&minfo); a.append(d.get_online()?); Ok(()) }); let fclone = f.clone(); let p = p.on_set(move |iter, pinfo| { let minfo = pinfo.to_method_info(); let d = fclone(&minfo); d.set_online(iter.read()?)?; Ok(()) }); let i = i.add_p(p); let s = factory.signal("CheckComplete", Default::default()); let i = i.add_s(s); i } #[derive(Debug)] pub struct DeviceCheckComplete { } impl arg::AppendAll for DeviceCheckComplete { fn append(&self, _: &mut arg::IterAppend) { } } impl arg::ReadAll for DeviceCheckComplete { fn read(_: &mut arg::Iter) -> Result { Ok(DeviceCheckComplete { }) } } impl dbus::message::SignalArgs for DeviceCheckComplete { const NAME: &'static str = "CheckComplete"; const INTERFACE: &'static str = "com.example.dbus.rs.device"; } // === Imported code end === } use dbus::{ffidisp::Connection, Path}; use dbus_tree as tree; use dbus_tree::{Interface, MTFn, MethodErr}; use std::sync::Arc; use std::sync::mpsc; use std::cell::Cell; use std::thread; // Our storage device #[derive(Debug)] struct Device { description: String, path: Path<'static>, index: i32, online: Cell, checking: Cell, check_complete_sender: mpsc::Sender, } // Every storage device has its own object path. // We therefore create a link from the object path to the Device. #[derive(Copy, Clone, Default, Debug)] struct TData; impl tree::DataType for TData { type Tree = (); type ObjectPath = Arc; type Property = (); type Interface = (); type Method = (); type Signal = (); } impl Device { // Creates a "test" device (not a real one, since this is an example). fn new_bogus(index: i32, s: mpsc::Sender) -> Device { Device { description: format!("This is device {}, which is {}.", index, ["totally awesome", "really fancy", "still going strong"][(index as usize) % 3]), path: format!("/Device{}", index).into(), index: index, online: Cell::new(index % 2 == 0), checking: Cell::new(false), check_complete_sender: s, } } } // Here's where we implement the code for our interface. impl com_example_dbus_rs::Device for Device { type Err = tree::MethodErr; fn check(&self) -> Result<(), Self::Err> { if self.checking.get() { return Err(MethodErr::failed(&"Device currently under check, cannot start another check")) } if self.online.get() { return Err(MethodErr::failed(&"Device is currently online, cannot start check")) } self.checking.set(true); // Start some lengthy processing in a separate thread... let devindex = self.index; let ch = self.check_complete_sender.clone(); thread::spawn(move || { // Bogus check of device use std::time::Duration; thread::sleep(Duration::from_secs(15)); // Tell main thread that we finished ch.send(devindex).unwrap(); }); Ok(()) } fn get_checking(&self) -> Result { Ok(self.checking.get()) } fn get_description(&self) -> Result { Ok(self.description.clone()) } fn get_online(&self) -> Result { Ok(self.online.get()) } fn set_online(&self, value: bool) -> Result<(), Self::Err> { if value && self.checking.get() { Err(MethodErr::failed(&"Device currently under check, cannot bring online")) } else { self.online.set(value); Ok(()) } } } fn create_iface() -> Interface, TData> { let f = tree::Factory::new_fn(); com_example_dbus_rs::device_server(&f, (), |m| { // Just provide a link from MethodInfo (m) to the &Device // we should call. let a: &Arc = m.path.get_data(); let b: &Device = &a; b }) } fn create_tree(devices: &[Arc], iface: &Arc, TData>>) -> tree::Tree, TData> { let f = tree::Factory::new_fn(); let mut tree = f.tree(()); for dev in devices { tree = tree.add(f.object_path(dev.path.clone(), dev.clone()) .introspectable() .add(iface.clone()) ); } tree } fn run() -> Result<(), Box> { let (check_complete_s, check_complete_r) = mpsc::channel::(); // Create our bogus devices let devices: Vec> = (0..10).map(|i| Arc::new(Device::new_bogus(i, check_complete_s.clone())) ).collect(); // Create tree let iface = create_iface(); let tree = create_tree(&devices, &Arc::new(iface)); // Setup DBus connection let c = Connection::new_session()?; c.register_name("com.example.dbus.rs.advancedserverexample", 0)?; tree.set_registered(&c, true)?; // ...and serve incoming requests. c.add_handler(tree); loop { // Wait for incoming messages. This will block up to one second. // Discard the result - relevant messages have already been handled. c.incoming(1000).next(); // Do all other things we need to do in our main loop. if let Ok(idx) = check_complete_r.try_recv() { let dev = &devices[idx as usize]; dev.checking.set(false); let sig = com_example_dbus_rs::DeviceCheckComplete {}; use dbus::message::SignalArgs; c.send(sig.to_emit_message(&dev.path)) .map_err(|_| "Sending DBus signal failed")?; } } } fn main() { if let Err(e) = run() { println!("{}", e); } }