adv_server/
adv_server.rs

1// More advanced server example.
2
3// This is supposed to look like a D-Bus service that allows the user to manipulate storage devices.
4
5// Note: in the dbus-codegen/example directory, there is a version of this example where dbus-codegen
6// was used to create some boilerplate code - feel free to compare the two examples.
7
8use std::sync::Arc;
9use std::sync::mpsc;
10use std::cell::Cell;
11use std::thread;
12
13use dbus_tree as tree;
14use dbus::Path;
15use dbus_tree::{Interface, Signal, MTFn, Access, MethodErr, EmitsChangedSignal};
16use dbus::ffidisp::Connection;
17
18// Our storage device
19#[derive(Debug)]
20struct Device {
21    description: String,
22    path: Path<'static>,
23    index: i32,
24    online: Cell<bool>,
25    checking: Cell<bool>,
26}
27
28// Every storage device has its own object path.
29// We therefore create a link from the object path to the Device.
30#[derive(Copy, Clone, Default, Debug)]
31struct TData;
32impl tree::DataType for TData {
33    type Tree = ();
34    type ObjectPath = Arc<Device>;
35    type Property = ();
36    type Interface = ();
37    type Method = ();
38    type Signal = ();
39}
40
41
42impl Device {
43    // Creates a "test" device (not a real one, since this is an example).
44    fn new_bogus(index: i32) -> Device {
45        Device {
46            description: format!("This is device {}, which is {}.", index,
47                ["totally awesome", "really fancy", "still going strong"][(index as usize) % 3]),
48            path: format!("/Device{}", index).into(),
49            index: index,
50            online: Cell::new(index % 2 == 0),
51            checking: Cell::new(false),
52        }
53    }
54}
55
56// Here's where we implement the code for our interface.
57fn create_iface(check_complete_s: mpsc::Sender<i32>) -> (Interface<MTFn<TData>, TData>, Arc<Signal<TData>>) {
58    let f = tree::Factory::new_fn();
59
60    let check_complete = Arc::new(f.signal("CheckComplete", ()));
61
62    (f.interface("com.example.dbus.rs.device", ())
63        // The online property can be both set and get
64        .add_p(f.property::<bool,_>("online", ())
65            .access(Access::ReadWrite)
66            .on_get(|i, m| {
67                let dev: &Arc<Device> = m.path.get_data();
68                i.append(dev.online.get());
69                Ok(())
70            })
71            .on_set(|i, m| {
72                let dev: &Arc<Device> = m.path.get_data();
73                let b: bool = i.read()?;
74                if b && dev.checking.get() {
75                    return Err(MethodErr::failed(&"Device currently under check, cannot bring online"))
76                }
77                dev.online.set(b);
78                Ok(())
79            })
80        )
81        // The "checking" property is read only
82        .add_p(f.property::<bool,_>("checking", ())
83            .emits_changed(EmitsChangedSignal::False)
84            .on_get(|i, m| {
85                let dev: &Arc<Device> = m.path.get_data();
86                i.append(dev.checking.get());
87                Ok(())
88            })
89        )
90        // ...and so is the "description" property
91        .add_p(f.property::<&str,_>("description", ())
92            .emits_changed(EmitsChangedSignal::Const)
93            .on_get(|i, m| {
94                let dev: &Arc<Device> = m.path.get_data();
95                i.append(&dev.description);
96                Ok(())
97            })
98        )
99        // ...add a method for starting a device check...
100        .add_m(f.method("check", (), move |m| {
101            let dev: &Arc<Device> = m.path.get_data();
102            if dev.checking.get() {
103                return Err(MethodErr::failed(&"Device currently under check, cannot start another check"))
104            }
105            if dev.online.get() {
106                return Err(MethodErr::failed(&"Device is currently online, cannot start check"))
107            }
108            dev.checking.set(true);
109
110            // Start some lengthy processing in a separate thread...
111            let devindex = dev.index;
112            let ch = check_complete_s.clone();
113            thread::spawn(move || {
114
115                // Bogus check of device
116                use std::time::Duration;
117                thread::sleep(Duration::from_secs(15));
118
119                // Tell main thread that we finished
120                ch.send(devindex).unwrap();
121            });
122            Ok(vec!(m.msg.method_return()))
123        }))
124        // Indicate that we send a special signal once checking has completed.
125        .add_s(check_complete.clone())
126    , check_complete)
127}
128
129fn create_tree(devices: &[Arc<Device>], iface: &Arc<Interface<MTFn<TData>, TData>>)
130    -> tree::Tree<MTFn<TData>, TData> {
131
132    let f = tree::Factory::new_fn();
133    let mut tree = f.tree(());
134    for dev in devices {
135        tree = tree.add(f.object_path(dev.path.clone(), dev.clone())
136            .introspectable()
137            .add(iface.clone())
138        );
139    }
140    tree
141}
142
143fn run() -> Result<(), Box<dyn std::error::Error>> {
144    // Create our bogus devices
145    let devices: Vec<Arc<Device>> = (0..10).map(|i| Arc::new(Device::new_bogus(i))).collect();
146
147    // Create tree
148    let (check_complete_s, check_complete_r) = mpsc::channel::<i32>();
149    let (iface, sig) = create_iface(check_complete_s);
150    let tree = create_tree(&devices, &Arc::new(iface));
151
152    // Setup DBus connection
153    let c = Connection::new_session()?;
154    c.register_name("com.example.dbus.rs.advancedserverexample", 0)?;
155    tree.set_registered(&c, true)?;
156
157    // ...and serve incoming requests.
158    c.add_handler(tree);
159    loop {
160        // Wait for incoming messages. This will block up to one second.
161        // Discard the result - relevant messages have already been handled.
162        c.incoming(1000).next();
163
164        // Do all other things we need to do in our main loop.
165        if let Ok(idx) = check_complete_r.try_recv() {
166            let dev = &devices[idx as usize];
167            dev.checking.set(false);
168            c.send(sig.msg(&dev.path, &"com.example.dbus.rs.device".into())).map_err(|_| "Sending DBus signal failed")?;
169        }
170    }
171}
172
173fn main() {
174    if let Err(e) = run() {
175        println!("{}", e);
176    }
177}