// More advanced server example. // This is supposed to look like a D-Bus service that allows the user to manipulate storage devices. // Note: in the dbus-codegen/example directory, there is a version of this example where dbus-codegen // was used to create some boilerplate code - feel free to compare the two examples. extern crate dbus; use std::sync::Arc; use std::sync::mpsc; use std::cell::Cell; use std::thread; use dbus::{Connection, BusType, tree, Path}; use dbus::tree::{Interface, Signal, MTFn, Access, MethodErr, EmitsChangedSignal}; // Our storage device #[derive(Debug)] struct Device { description: String, path: Path<'static>, index: i32, online: Cell, checking: Cell, } // 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) -> 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), } } } // Here's where we implement the code for our interface. fn create_iface(check_complete_s: mpsc::Sender) -> (Interface, TData>, Arc>) { let f = tree::Factory::new_fn(); let check_complete = Arc::new(f.signal("CheckComplete", ())); (f.interface("com.example.dbus.rs.device", ()) // The online property can be both set and get .add_p(f.property::("online", ()) .access(Access::ReadWrite) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(dev.online.get()); Ok(()) }) .on_set(|i, m| { let dev: &Arc = m.path.get_data(); let b: bool = try!(i.read()); if b && dev.checking.get() { return Err(MethodErr::failed(&"Device currently under check, cannot bring online")) } dev.online.set(b); Ok(()) }) ) // The "checking" property is read only .add_p(f.property::("checking", ()) .emits_changed(EmitsChangedSignal::False) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(dev.checking.get()); Ok(()) }) ) // ...and so is the "description" property .add_p(f.property::<&str,_>("description", ()) .emits_changed(EmitsChangedSignal::Const) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(&dev.description); Ok(()) }) ) // ...add a method for starting a device check... .add_m(f.method("check", (), move |m| { let dev: &Arc = m.path.get_data(); if dev.checking.get() { return Err(MethodErr::failed(&"Device currently under check, cannot start another check")) } if dev.online.get() { return Err(MethodErr::failed(&"Device is currently online, cannot start check")) } dev.checking.set(true); // Start some lengthy processing in a separate thread... let devindex = dev.index; let ch = check_complete_s.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(vec!(m.msg.method_return())) })) // Indicate that we send a special signal once checking has completed. .add_s(check_complete.clone()) , check_complete) } 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> { // Create our bogus devices let devices: Vec> = (0..10).map(|i| Arc::new(Device::new_bogus(i))).collect(); // Create tree let (check_complete_s, check_complete_r) = mpsc::channel::(); let (iface, sig) = create_iface(check_complete_s); let tree = create_tree(&devices, &Arc::new(iface)); // Setup DBus connection let c = try!(Connection::get_private(BusType::Session)); try!(c.register_name("com.example.dbus.rs.advancedserverexample", 0)); try!(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); try!(c.send(sig.msg(&dev.path, &"com.example.dbus.rs.device".into())).map_err(|_| "Sending DBus signal failed")); } } } fn main() { if let Err(e) = run() { println!("{}", e); } }