1use crate::utils::Dbg;
2use std::sync::Mutex;
3use std::sync::Arc;
4use dbus::channel::Sender;
5use std::collections::HashMap;
6use crate::{IfaceToken, Crossroads, Context, MethodErr};
7use dbus::arg::{Variant, RefArg, Arg, Append, PropMap};
8use std::marker::PhantomData;
9use crate::ifacedesc::EMITS_CHANGED;
10
11fn introspect(cr: &Crossroads, path: &dbus::Path<'static>) -> String {
12 let mut children = cr.get_children(path, true);
13 let mut childstr = String::new();
14 children.sort_unstable();
15 for c in children {
16 childstr += &format!(" <node name=\"{}\"/>\n", c);
17 }
18 let (reg, ifaces) = cr.registry_and_ifaces(path);
19 let ifacestr = reg.introspect(ifaces);
20
21 let nodestr = format!(
22r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
23 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
24<node name="{}">
25{}{}</node>"##, path, ifacestr, childstr);
26 nodestr
27}
28
29pub fn introspectable(cr: &mut Crossroads) -> IfaceToken<()> {
30 cr.register("org.freedesktop.DBus.Introspectable", |b| {
31 b.method_with_cr("Introspect", (), ("xml_data",), |ctx, cr, _: ()| {
32 Ok((introspect(cr, ctx.path()),))
33 });
34 })
35}
36
37
38pub (crate) fn make_emits_message<V: dbus::arg::Arg + dbus::arg::Append>(prop_name: &str, emits_changed: &str, ctx: &Context, v: &V) -> Option<dbus::Message> {
39 let arr = [prop_name];
40 let (d, i) = match emits_changed {
41 "false" => return None,
42 "invalidates" => (None, &arr[..]),
43 "true" => (Some((arr[0], Variant(v))), &[][..]),
44 _ => panic!("Invalid value of EmitsChangedSignal: {:?}", emits_changed)
45 };
46
47 use dbus::message::SignalArgs;
48 use dbus::blocking::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged as PPC;
49 let s: &str = ctx.message().read1().unwrap();
50 Some(dbus::Message::signal(ctx.path(), &PPC::INTERFACE.into(), &PPC::NAME.into())
51 .append3(s, dbus::arg::Dict::new(d), i))
52}
53
54#[derive(Debug)]
55pub struct PropContext {
59 path: dbus::Path<'static>,
60 interface: dbus::strings::Interface<'static>,
61 name: String,
62 context: Option<Context>,
63
64 iface_token: usize,
65 emits_changed: Option<&'static str>,
66 get_all: Option<Arc<Mutex<PropAllCtx>>>,
67}
68
69impl PropContext {
70 pub fn path(&self) -> &dbus::Path<'static> { &self.path }
72
73 pub fn interface(&self) -> &dbus::strings::Interface<'static> { &self.interface }
75
76 pub fn name(&self) -> &str { &self.name }
78
79 pub fn message(&self) -> Option<&dbus::Message> { self.context.as_ref().map(|ctx| ctx.message()) }
81
82 pub fn reply<A: Arg + RefArg + Send + Append + 'static>(&mut self, reply: Result<A, MethodErr>) -> PhantomData<A> {
86 if let Some(ec) = &self.emits_changed {
87 let mut emit_msg = None;
88 if let Ok(v) = &reply {
89 if let Some(ctx) = &self.context {
90 emit_msg = make_emits_message(&self.name, ec, &ctx, v);
91 }
92 }
93 self.reply_noemit(reply.map(|_| ()));
94 emit_msg.map(|emit_msg| self.context.as_mut().map(|ctx| { ctx.push_msg(emit_msg) }));
95 } else {
96 if let Some(ga) = &self.get_all {
97 ga.lock().unwrap().add_reply(self.name.clone(), reply.ok().map(|a| Box::new(a) as Box<(dyn RefArg + Send)>));
98 } else {
99 self.context.as_mut().map(|ctx| ctx.reply(reply.map(|a| (Variant(a),))));
100 }
101 }
102 PhantomData
103 }
104
105 pub fn reply_noemit(&mut self, reply: Result<(), MethodErr>) {
109 debug_assert!(self.emits_changed.is_some());
110 self.context.as_mut().map(|ctx| ctx.reply(reply));
111 }
112
113 pub (crate) fn set_send_on_drop(&mut self, value: Arc<dyn Sender + Send + Sync>) {
114 if let Some(get_all) = &self.get_all {
115 let mut pactx = get_all.lock().unwrap();
116 if let Some(ref mut propctx) = pactx.propctx {
117 propctx.context.as_mut().map(|ctx| ctx.set_send_on_drop(value));
118 return;
119 }
120 }
121 self.context.as_mut().map(|ctx| ctx.set_send_on_drop(value));
122 }
123
124 fn new(cr: &Crossroads, path: dbus::Path<'static>, interface: String, name: String) -> Result<Self, MethodErr> {
125 let interface = dbus::strings::Interface::new(interface).map_err(|s| MethodErr::no_interface(&s))?;
126 let iface_token = cr.find_iface_token(&path, Some(&interface))?;
127 Ok(PropContext {
128 path,
129 iface_token,
130 interface,
131 name,
132 get_all: None,
133 context: None,
134 emits_changed: None
135 })
136 }
137
138 fn call_prop(mut self, cr: &mut Crossroads, is_set: bool) -> Option<Self> {
139 let token = self.iface_token;
140 let name = self.name.clone();
141 let mut cb = match self.check(|_| {
142 cr.registry().take_prop(token, &name, is_set)
143 }) {
144 Ok(cb) => cb,
145 Err(_) => return Some(self)
146 };
147 let octx = cb(self, cr);
148 cr.registry().give_prop(token, &name, cb, is_set);
149 octx
151 }
152
153 fn call_all_props<F: FnOnce(&mut PropAllCtx) + Send + 'static>(self, cr: &mut Crossroads, f: F) -> Option<Self> {
154 let token = self.iface_token;
155 let pactx = Arc::new(Mutex::new(PropAllCtx {
156 remaining: 0,
157 answers: HashMap::new(),
158 donefn: Some(Dbg(Box::new(f))),
159 propctx: Some(self),
160 }));
161 let mut pb = pactx.lock().unwrap();
162 let pctxs: Vec<_> = cr.registry().prop_names_readable(token).map(|prop_name| {
163 pb.remaining += 1;
164 let parent = pb.propctx.as_ref().unwrap();
165 PropContext {
166 path: parent.path.clone(),
167 iface_token: parent.iface_token,
168 interface: parent.interface().clone(),
169 name: prop_name.into(),
170 get_all: Some(pactx.clone()),
171 context: None,
172 emits_changed: None
173 }
174 }).collect();
175 drop(pb);
176 for pctx in pctxs {
177 pctx.call_prop(cr, false);
178 }
179 let mut temp = pactx.lock().unwrap();
180 if temp.check_finished() { Some(temp.propctx.take().unwrap()) } else { None }
182 }
183
184 pub fn check<R, F: FnOnce(Option<&mut Context>) -> Result<R, MethodErr>>(&mut self, f: F) -> Result<R, ()> {
186 match &mut self.context {
187 Some(ctx) => ctx.check(|ctx| f(Some(ctx))),
188 None => match f(None) {
189 Ok(r) => Ok(r),
190 Err(_) => { todo!() },
191 },
192 }
193 }
194}
195
196#[derive(Debug)]
197struct PropAllCtx {
198 remaining: usize,
199 answers: PropMap,
200 donefn: Option<Dbg<Box<dyn FnOnce(&mut Self) + Send + 'static>>>,
201 propctx: Option<PropContext>,
202}
203
204impl PropAllCtx {
205 fn check_finished(&mut self) -> bool {
206 if self.remaining > 0 { return false; }
207 if let Some(donefn) = self.donefn.take() {
208 (donefn.0)(self);
209 }
210 true
211 }
212
213 fn add_reply(&mut self, prop_name: String, prop_value: Option<Box<dyn RefArg + Send>>) {
214 if let Some(v) = prop_value {
215 self.answers.insert(prop_name, Variant(v));
216 }
217 self.remaining -= 1;
218 self.check_finished();
219 }
220}
221
222fn get(mut ctx: Context, cr: &mut Crossroads, (interface_name, property_name): (String, String)) -> Option<Context> {
223 let mut propctx = match ctx.check(|ctx| { PropContext::new(cr, ctx.path().clone(), interface_name, property_name)}) {
224 Ok(p) => p,
225 Err(_) => return Some(ctx),
226 };
227 propctx.context = Some(ctx);
228 propctx.call_prop(cr, false).map(|propctx| { propctx.context.unwrap() })
229}
230
231fn getall_all(ctx: Context, cr: &mut Crossroads) -> Option<Context> {
232 get_all_for_path(&ctx.path().clone(), cr, Some(ctx), move |ictx, octx| {
233 let props: HashMap<_, _> = ictx.ifaces.values().flatten().collect();
234 octx.as_mut().unwrap().do_reply(|msg| {
235 msg.append_all((props,));
236 });
237 })
238}
239
240fn getall(mut ctx: Context, cr: &mut Crossroads, (interface_name,): (String,)) -> Option<Context> {
241 if interface_name == "" {
242 return getall_all(ctx, cr);
243 }
244 let mut propctx = match ctx.check(|ctx| { PropContext::new(cr, ctx.path().clone(), interface_name, "".into())}) {
245 Ok(p) => p,
246 Err(_) => return Some(ctx),
247 };
248 propctx.context = Some(ctx);
249 propctx.call_all_props(cr, move |pactx| {
250 let pctx = pactx.propctx.as_mut().unwrap();
251 let answers = &pactx.answers;
252 pctx.context.as_mut().unwrap().do_reply(|msg| {
253 msg.append_all((answers,));
254 });
255 }).map(|propctx| { propctx.context.unwrap() })
256}
257
258fn set(mut ctx: Context, cr: &mut Crossroads, (interface_name, property_name, _value): (String, String, Variant<Box<dyn RefArg>>)) -> Option<Context> {
259 let mut propctx = match ctx.check(|ctx| { PropContext::new(cr, ctx.path().clone(), interface_name, property_name) }) {
260 Ok(p) => p,
261 Err(_) => return Some(ctx),
262 };
263 let ann = cr.registry()
264 .find_annotation(propctx.iface_token, EMITS_CHANGED, Some(&propctx.name));
265 propctx.emits_changed = match ann {
266 Some("const") => Some("const"),
267 Some("false") => Some("false"),
268 Some("invalidates") => Some("invalidates"),
269 _ => Some("true"),
270 };
271 propctx.context = Some(ctx);
272 propctx.call_prop(cr, true).map(|propctx| { propctx.context.unwrap() })
273}
274
275pub fn properties(cr: &mut Crossroads) -> IfaceToken<()> {
276 cr.register("org.freedesktop.DBus.Properties", |b| {
277 b.method_with_cr_custom::<_, (Variant<u8>,), _, _>("Get", ("interface_name", "property_name"), ("value",), get);
278 b.method_with_cr_custom::<_, (PropMap,), _, _>("GetAll", ("interface_name",), ("properties",), getall);
279 b.method_with_cr_custom::<_, (), _, _>("Set", ("interface_name", "property_name", "value"), (), set);
280 b.signal::<(String, PropMap, Vec<String>), _>("PropertiesChanged",
281 ("interface_name", "changed_properties", "invalidated_properties"));
282 })
283}
284
285#[derive(Debug, Default)]
286struct IfaceContext {
287 remaining: usize,
288 ifaces: IfacePropMap,
289 donefn: Option<Dbg<Box<dyn FnOnce(&mut IfaceContext, &mut Option<Context>) + Send + 'static>>>,
290}
291
292type IfacePropMap = HashMap<String, PropMap>;
293type PathPropMap = HashMap<dbus::Path<'static>, IfacePropMap>;
294
295fn get_all_for_path<F>(path: &dbus::Path<'static>, cr: &mut Crossroads, octx: Option<Context>, f: F) -> Option<Context>
296where F: FnOnce(&mut IfaceContext, &mut Option<Context>) + Send + 'static {
297 let (_reg, ifaces) = cr.registry_and_ifaces(&path);
298 let all: Vec<usize> = ifaces.into_iter().map(|token| *token).collect();
299 for_each_interface_with_properties(path, all, cr, octx, f)
300}
301
302fn for_each_interface_with_properties<F: FnOnce(&mut IfaceContext, &mut Option<Context>) + Send + 'static, I: IntoIterator<Item = usize>>
303(path: &dbus::Path<'static>, ifaces: I, cr: &mut Crossroads, mut octx: Option<Context>, f: F) -> Option<Context> {
304 let ictx: Arc<Mutex<IfaceContext>> = Default::default();
305 let (reg, _all_ifaces) = cr.registry_and_ifaces(&path);
306 let all: Vec<_> = ifaces.into_iter().filter_map(|token| {
307 let iface_name = reg.get_intf_name(token)?;
308 Some(PropContext {
309 context: None,
310 emits_changed: None,
311 get_all: None,
312 iface_token: token,
313 interface: iface_name.clone(),
314 path: path.clone(),
315 name: "".into(),
316 })
317 }).collect();
318
319 if all.len() == 0 {
320 f(&mut *ictx.lock().unwrap(), &mut octx);
321 return octx;
322 }
323
324 let mut ic = ictx.lock().unwrap();
325 ic.remaining = all.len();
326 ic.donefn = Some(Dbg(Box::new(f)));
327 drop(ic);
328 let amoctx = Arc::new(Mutex::new(octx));
329 for mut pctx in all.into_iter() {
330 let iclone = ictx.clone();
331 let amoclone = amoctx.clone();
332 pctx.context = amoctx.lock().unwrap().take();
333 pctx.call_all_props(cr, move |pactx| {
334 let mut ic = iclone.lock().unwrap();
335 let mut amoctx = amoclone.lock().unwrap();
336 if amoctx.is_none() {
337 *amoctx = pactx.propctx.as_mut().unwrap().context.take();
338 }
339 let answers = std::mem::replace(&mut pactx.answers, HashMap::new());
340 ic.ifaces.insert(pactx.propctx.as_ref().unwrap().interface.to_string(), answers);
342 ic.remaining -= 1;
343 if ic.remaining == 0 {
344 let donefn = ic.donefn.take().unwrap().0;
345 (donefn)(&mut *ic, &mut amoctx)
346 }
347 }).map(|mut pctx| {
348 let mut amoctx = amoctx.lock().unwrap();
349 if amoctx.is_none() {
350 *amoctx = pctx.context.take();
351 }
352 });
353 }
354 let mut amoctx = amoctx.lock().unwrap();
355 amoctx.take()
356}
357
358fn get_managed_objects(mut ctx: Context, cr: &mut Crossroads, _: ()) -> Option<Context> {
360 let parent = ctx.path();
362 let children: Vec<dbus::Path<'static>> =
363 cr.get_children(ctx.path(), false).into_iter().map(|child_path| {
364 let mut x = String::from(&**parent);
365 if !x.ends_with('/') {
366 x.push_str("/");
367 }
368 x.push_str(child_path);
369 dbus::Path::from(x).into_static()
370 }).collect();
371
372 if children.len() == 0 {
373 ctx.do_reply(|msg| {
374 let x: PathPropMap = Default::default();
375 msg.append_all((x,));
376 });
377 return Some(ctx);
378 }
379
380 #[derive(Debug)]
381 struct Temp {
382 remaining: usize,
383 temp_map: PathPropMap,
384 octx: Option<Context>,
385 }
386 let r = Arc::new(Mutex::new(Temp {
387 remaining: children.len(),
388 temp_map: HashMap::new(),
389 octx: Some(ctx),
390 }));
391 for subpath in children {
392 let rclone = r.clone();
393 let subpath_clone = subpath.clone();
394 let octx = r.lock().unwrap().octx.take();
395 get_all_for_path(&subpath, cr, octx, move |ictx, octx| {
396 let mut rr = rclone.lock().unwrap();
397 if rr.octx.is_none() { rr.octx = octx.take(); }
398 let ifaces = std::mem::replace(&mut ictx.ifaces, HashMap::new());
399 rr.temp_map.insert(subpath_clone, ifaces);
400 rr.remaining -= 1;
401 if rr.remaining > 0 { return; }
403 let mut octx = rr.octx.take().unwrap();
404 octx.do_reply(|msg| {
405 msg.append_all((&rr.temp_map,));
406 });
407 rr.octx = Some(octx);
408 }).map(|octx| {
409 let mut rr = r.lock().unwrap();
410 if rr.octx.is_none() { rr.octx = Some(octx); }
411 });
412 }
413 let mut rr = r.lock().unwrap();
414 rr.octx.take()
415}
416
417pub fn object_manager(cr: &mut Crossroads) -> IfaceToken<()> {
418 cr.register("org.freedesktop.DBus.ObjectManager", |b| {
419 b.method_with_cr_custom::<(), (PathPropMap,), _, _>
420 ("GetManagedObjects", (), ("objpath_interfaces_and_properties",), get_managed_objects);
421 b.signal::<(dbus::Path<'static>, IfacePropMap), _>("InterfacesAdded",
422 ("object_path", "interfaces_and_properties"));
423 b.signal::<(dbus::Path<'static>, Vec<String>), _>("InterfacesRemoved",
424 ("object_path", "interfaces"));
425 })
426}
427
428fn object_manager_parents<F: FnMut(dbus::Path<'static>, &mut Crossroads)>(name: &dbus::Path<'static>, cr: &mut Crossroads, mut f: F) {
429 for idx in 0..name.len()-1 {
430 if name.as_bytes()[idx] != b'/' { continue; }
431 let parent = dbus::Path::from(&name[0..(if idx == 0 { idx + 1 } else {idx})]).into_static();
432 if !cr.has_interface(&parent, cr.object_manager::<()>()) { continue; }
433 f(parent, cr)
434 }
435}
436
437pub fn object_manager_path_added(sender: Arc<dyn Sender + Send + Sync>, name: &dbus::Path<'static>, cr: &mut Crossroads) {
438 object_manager_parents(name, cr, |parent, cr| {
439 let n = name.clone();
440 let s = sender.clone();
441 get_all_for_path(&name, cr, None, move |ictx, _| {
442 let x = dbus::blocking::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesAdded {
443 object: n,
444 interfaces: std::mem::replace(&mut ictx.ifaces, HashMap::new()),
445 };
446 let _ = s.send(dbus::message::SignalArgs::to_emit_message(&x, &parent));
447 });
448 });
449}
450
451pub fn object_manager_path_removed(sender: Arc<dyn Sender + Send + Sync>, name: &dbus::Path<'static>, cr: &mut Crossroads) {
452 object_manager_parents(name, cr, |parent, cr| {
453 let (reg, ifaces) = cr.registry_and_ifaces(&name);
454
455 let x = dbus::blocking::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved {
456 object: name.clone(),
457 interfaces: ifaces.into_iter()
458 .filter_map(|iface| reg.get_intf_name(*iface))
459 .map(|iface| String::from(&**iface))
460 .collect(),
461 };
462 let _ = sender.send(dbus::message::SignalArgs::to_emit_message(&x, &parent));
463 });
464}
465
466pub fn object_manager_interface_added(sender: Arc<dyn Sender + Send + Sync>, name: &dbus::Path<'static>, itoken: usize, cr: &mut Crossroads) {
467 object_manager_parents(name, cr, |parent, cr| {
468 let n = name.clone();
469 let s = sender.clone();
470
471 for_each_interface_with_properties(&name, vec![itoken], cr, None, move |ictx, _| {
472 let x = dbus::blocking::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesAdded {
473 object: n,
474 interfaces: std::mem::replace(&mut ictx.ifaces, HashMap::new()),
475 };
476 let _ = s.send(dbus::message::SignalArgs::to_emit_message(&x, &parent));
477 });
478 });
479}
480
481pub fn object_manager_interface_removed(sender: Arc<dyn Sender + Send + Sync>, name: &dbus::Path<'static>, itoken: usize, cr: &mut Crossroads) {
482 object_manager_parents(name, cr, |parent, cr| {
483 let (reg, _ifaces) = cr.registry_and_ifaces(&name);
484
485 if let Some(iface) = reg.get_intf_name(itoken) {
486 let x = dbus::blocking::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved {
487 object: name.clone(),
488 interfaces: vec![iface.to_string()],
489 };
490 let _ = sender.send(dbus::message::SignalArgs::to_emit_message(&x, &parent));
491 }
492 });
493}