1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
// 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.
use std::sync::Arc;
use std::sync::mpsc;
use std::cell::Cell;
use std::thread;
use dbus_tree as tree;
use dbus::Path;
use dbus_tree::{Interface, Signal, MTFn, Access, MethodErr, EmitsChangedSignal};
use dbus::ffidisp::Connection;
// Our storage device
#[derive(Debug)]
struct Device {
description: String,
path: Path<'static>,
index: i32,
online: Cell<bool>,
checking: Cell<bool>,
}
// 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<Device>;
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<i32>) -> (Interface<MTFn<TData>, TData>, Arc<Signal<TData>>) {
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::<bool,_>("online", ())
.access(Access::ReadWrite)
.on_get(|i, m| {
let dev: &Arc<Device> = m.path.get_data();
i.append(dev.online.get());
Ok(())
})
.on_set(|i, m| {
let dev: &Arc<Device> = m.path.get_data();
let b: bool = 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::<bool,_>("checking", ())
.emits_changed(EmitsChangedSignal::False)
.on_get(|i, m| {
let dev: &Arc<Device> = 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<Device> = 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<Device> = 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<Device>], iface: &Arc<Interface<MTFn<TData>, TData>>)
-> tree::Tree<MTFn<TData>, 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<dyn std::error::Error>> {
// Create our bogus devices
let devices: Vec<Arc<Device>> = (0..10).map(|i| Arc::new(Device::new_bogus(i))).collect();
// Create tree
let (check_complete_s, check_complete_r) = mpsc::channel::<i32>();
let (iface, sig) = create_iface(check_complete_s);
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);
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);
}
}
|