1 #pragma once
2
3 #ifndef BOOST_COROUTINES_NO_DEPRECATION_WARNING
4 // users should define this if they directly include boost/asio/spawn.hpp,
5 // but by defining it here, warnings won't cause problems with a compile
6 #define BOOST_COROUTINES_NO_DEPRECATION_WARNING
7 #endif
8
9 #include <boost/callable_traits/args.hpp>
10 #include <boost/callable_traits/return_type.hpp>
11 #ifndef SDBUSPLUS_DISABLE_BOOST_COROUTINES
12 #include <boost/asio/detached.hpp>
13 #include <boost/asio/spawn.hpp>
14 #endif
15 #include <sdbusplus/asio/connection.hpp>
16 #include <sdbusplus/exception.hpp>
17 #include <sdbusplus/message/read.hpp>
18 #include <sdbusplus/message/types.hpp>
19 #include <sdbusplus/server.hpp>
20 #include <sdbusplus/utility/tuple_to_array.hpp>
21 #include <sdbusplus/utility/type_traits.hpp>
22
23 #include <any>
24 #include <functional>
25 #include <optional>
26 #include <utility>
27 #include <vector>
28
29 namespace sdbusplus
30 {
31 namespace asio
32 {
33
34 enum class SetPropertyReturnValue
35 {
36 fail = 0,
37 valueUpdated,
38 sameValueUpdated,
39 };
40
41 class dbus_interface;
42
43 class property_callback
44 {
45 public:
property_callback(dbus_interface & parent,const std::string & name,std::function<int (message_t &)> && on_get,std::function<SetPropertyReturnValue (message_t &)> && on_set_message,std::function<SetPropertyReturnValue (const std::any &)> && on_set_value,const char * signature,decltype(vtable_t::flags)flags)46 property_callback(
47 dbus_interface& parent, const std::string& name,
48 std::function<int(message_t&)>&& on_get,
49 std::function<SetPropertyReturnValue(message_t&)>&& on_set_message,
50 std::function<SetPropertyReturnValue(const std::any&)>&& on_set_value,
51 const char* signature, decltype(vtable_t::flags) flags) :
52 interface_(parent), name_(name), on_get_(std::move(on_get)),
53 on_set_message_(std::move(on_set_message)),
54 on_set_value_(std::move(on_set_value)), signature_(signature),
55 flags_(flags)
56 {}
57 dbus_interface& interface_;
58 std::string name_;
59 std::function<int(message_t&)> on_get_;
60 std::function<SetPropertyReturnValue(message_t&)> on_set_message_;
61 std::function<SetPropertyReturnValue(const std::any&)> on_set_value_;
62 const char* signature_;
63 decltype(vtable_t::flags) flags_;
64 };
65
66 class method_callback
67 {
68 public:
method_callback(const std::string & name,std::function<int (message_t &)> && call,const char * arg_signature,const char * return_signature,decltype(vtable_t::flags)flags)69 method_callback(const std::string& name,
70 std::function<int(message_t&)>&& call,
71 const char* arg_signature, const char* return_signature,
72 decltype(vtable_t::flags) flags) :
73 name_(name), call_(std::move(call)), arg_signature_(arg_signature),
74 return_signature_(return_signature), flags_(flags)
75 {}
76 std::string name_;
77 std::function<int(message_t&)> call_;
78 const char* arg_signature_;
79 const char* return_signature_;
80 decltype(vtable_t::flags) flags_;
81 };
82
83 class signal
84 {
85 public:
signal(const std::string & name,const char * signature)86 signal(const std::string& name, const char* signature) :
87 name_(name), signature_(signature)
88 {}
89
90 std::string name_;
91 const char* signature_;
92 };
93
94 #ifndef SDBUSPLUS_DISABLE_BOOST_COROUTINES
95 template <typename T>
96 inline const bool FirstArgIsYield_v =
97 std::is_same_v<utility::get_first_arg_t<utility::decay_tuple_t<
98 boost::callable_traits::args_t<T>>>,
99 boost::asio::yield_context>;
100
101 #else
102 template <typename T>
103 inline const bool FirstArgIsYield_v = false;
104 #endif
105
106 template <typename T>
107 inline const bool FirstArgIsMessage_v =
108 std::is_same_v<utility::get_first_arg_t<utility::decay_tuple_t<
109 boost::callable_traits::args_t<T>>>,
110 message_t>;
111
112 template <typename T>
113 inline const bool SecondArgIsMessage_v = std::is_same_v<
114 utility::get_first_arg_t<utility::strip_first_arg_t<
115 utility::decay_tuple_t<boost::callable_traits::args_t<T>>>>,
116 message_t>;
117
118 template <typename T>
119 static constexpr bool callbackWantsMessage =
120 FirstArgIsMessage_v<T> || SecondArgIsMessage_v<T>;
121
122 namespace details
123 {
124 // small helper class to count the number of non-dbus arguments
125 // to a registered dbus function (like message_t or yield_context)
126 // so the registered signature can omit them
127 template <typename FirstArg, typename... Rest>
128 struct NonDbusArgsCount;
129
130 template <>
131 struct NonDbusArgsCount<std::tuple<>>
132 {
133 constexpr static std::size_t size()
134 {
135 return 0;
136 }
137 };
138 template <typename FirstArg, typename... OtherArgs>
139 struct NonDbusArgsCount<std::tuple<FirstArg, OtherArgs...>>
140 {
sizesdbusplus::asio::details::NonDbusArgsCount141 constexpr static std::size_t size()
142 {
143 #ifndef SDBUSPLUS_DISABLE_BOOST_COROUTINES
144 if constexpr (std::is_same_v<FirstArg, message_t> ||
145 std::is_same_v<FirstArg, boost::asio::yield_context>)
146 {
147 return 1 + NonDbusArgsCount<std::tuple<OtherArgs...>>::size();
148 }
149 else
150 #endif
151 {
152 return NonDbusArgsCount<std::tuple<OtherArgs...>>::size();
153 }
154 }
155 };
156
157 template <typename PropertyType>
nop_get_value(const PropertyType & value)158 PropertyType nop_get_value(const PropertyType& value)
159 {
160 return value;
161 }
162
163 template <typename PropertyType>
nop_set_value(const PropertyType & req,PropertyType & old)164 bool nop_set_value(const PropertyType& req, PropertyType& old)
165 {
166 old = req;
167 return true;
168 }
169
170 } // namespace details
171
172 template <typename InputArgs, typename Callback>
callFunction(message_t & m,InputArgs & inputArgs,Callback && callback)173 void callFunction(message_t& m, InputArgs& inputArgs, Callback&& callback)
174 {
175 using ResultType = boost::callable_traits::return_type_t<Callback>;
176 if constexpr (std::is_void_v<ResultType>)
177 {
178 std::apply(callback, inputArgs);
179 }
180 else
181 {
182 auto r = std::apply(callback, inputArgs);
183 m.append(r);
184 }
185 }
186
187 template <typename CallbackType>
188 class callback_method_instance
189 {
190 private:
191 using CallbackSignature = boost::callable_traits::args_t<CallbackType>;
192 using InputTupleType = utility::decay_tuple_t<CallbackSignature>;
193
194 CallbackType func_;
195
196 public:
callback_method_instance(CallbackType && func)197 callback_method_instance(CallbackType&& func) : func_(func) {}
198
operator ()(message_t & m)199 int operator()(message_t& m)
200 {
201 using DbusTupleType = utility::strip_first_n_args_t<
202 details::NonDbusArgsCount<InputTupleType>::size(), InputTupleType>;
203
204 DbusTupleType dbusArgs;
205 if (m.is_method_error())
206 {
207 return -EINVAL;
208 }
209 std::apply([&m](auto&... x) { m.read(x...); }, dbusArgs);
210
211 auto ret = m.new_method_return();
212 if constexpr (callbackWantsMessage<CallbackType>)
213 {
214 InputTupleType inputArgs =
215 std::tuple_cat(std::forward_as_tuple(std::move(m)), dbusArgs);
216 callFunction(ret, inputArgs, func_);
217 }
218 else
219 {
220 callFunction(ret, dbusArgs, func_);
221 }
222 ret.method_return();
223 return 1;
224 }
225 };
226
227 #ifndef SDBUSPLUS_DISABLE_BOOST_COROUTINES
228 template <typename CallbackType>
229 class coroutine_method_instance
230 {
231 public:
232 using self_t = coroutine_method_instance<CallbackType>;
coroutine_method_instance(boost::asio::io_context & io,CallbackType && func)233 coroutine_method_instance(boost::asio::io_context& io,
234 CallbackType&& func) : io_(io), func_(func)
235 {}
236
operator ()(message_t & m)237 int operator()(message_t& m)
238 {
239 // make a copy of m to move into the coroutine
240 message_t b{m};
241
242 // spawn off a new coroutine to handle the method call
243 boost::asio::spawn(io_, std::bind_front(&self_t::after_spawn, this, b),
244 boost::asio::detached);
245
246 return 1;
247 }
248
249 private:
250 boost::asio::io_context& io_;
251 CallbackType func_;
after_spawn(message_t b,boost::asio::yield_context yield)252 void after_spawn(message_t b, boost::asio::yield_context yield)
253 {
254 using CallbackSignature = boost::callable_traits::args_t<CallbackType>;
255 using InputTupleType = utility::decay_tuple_t<CallbackSignature>;
256 using DbusTupleType = utility::strip_first_n_args_t<
257 details::NonDbusArgsCount<InputTupleType>::size(), InputTupleType>;
258 DbusTupleType dbusArgs;
259 try
260 {
261 std::apply([&b](auto&... x) { b.read(x...); }, dbusArgs);
262 }
263 catch (const exception::SdBusError& e)
264 {
265 auto ret = b.new_method_errno(e.get_errno(), e.get_error());
266 ret.method_return();
267 return;
268 }
269
270 try
271 {
272 auto ret = b.new_method_return();
273 if constexpr (callbackWantsMessage<CallbackType>)
274 {
275 InputTupleType inputArgs = std::tuple_cat(
276 std::forward_as_tuple(std::move(yield)),
277 std::forward_as_tuple(std::move(b)), dbusArgs);
278 callFunction(ret, inputArgs, func_);
279 }
280 else
281 {
282 InputTupleType inputArgs = std::tuple_cat(
283 std::forward_as_tuple(std::move(yield)), dbusArgs);
284 callFunction(ret, inputArgs, func_);
285 }
286 ret.method_return();
287 }
288 catch (const sdbusplus::exception::SdBusError& e)
289 {
290 // Catch D-Bus error explicitly called by method handler
291 message_t err = b.new_method_errno(e.get_errno(), e.get_error());
292 err.method_return();
293 }
294 catch (const sdbusplus::exception_t& e)
295 {
296 message_t err = b.new_method_error(e);
297 err.method_return();
298 }
299 catch (...)
300 {
301 message_t err = b.new_method_errno(-EIO);
302 err.method_return();
303 }
304 }
305 };
306 #endif
307
308 template <typename PropertyType, typename CallbackType>
309 class callback_get_instance
310 {
311 public:
callback_get_instance(const std::shared_ptr<PropertyType> & value,CallbackType && func)312 callback_get_instance(const std::shared_ptr<PropertyType>& value,
313 CallbackType&& func) :
314 value_(value), func_(std::forward<CallbackType>(func))
315 {}
operator ()(message_t & m)316 int operator()(message_t& m)
317 {
318 *value_ = func_(*value_);
319 m.append(*value_);
320 return 1;
321 }
322
323 private:
324 std::shared_ptr<PropertyType> value_;
325 CallbackType func_;
326 };
327
328 template <typename PropertyType>
329 class callback_set_message_instance
330 {
331 public:
callback_set_message_instance(const std::shared_ptr<PropertyType> & value,std::function<bool (const PropertyType &,PropertyType &)> && func)332 callback_set_message_instance(
333 const std::shared_ptr<PropertyType>& value,
334 std::function<bool(const PropertyType&, PropertyType&)>&& func) :
335 value_(value), func_(std::move(func))
336 {}
operator ()(message_t & m)337 SetPropertyReturnValue operator()(message_t& m)
338 {
339 auto input = m.unpack<PropertyType>();
340
341 PropertyType oldValue = *value_;
342 if (!func_(input, *value_))
343 {
344 return SetPropertyReturnValue::fail;
345 }
346 if (oldValue == *value_)
347 {
348 return SetPropertyReturnValue::sameValueUpdated;
349 }
350 return SetPropertyReturnValue::valueUpdated;
351 }
352
353 private:
354 std::shared_ptr<PropertyType> value_;
355 std::function<bool(const PropertyType&, PropertyType&)> func_;
356 };
357
358 template <typename PropertyType>
359 class callback_set_value_instance
360 {
361 public:
callback_set_value_instance(const std::shared_ptr<PropertyType> & value,std::function<bool (const PropertyType &,PropertyType &)> && func)362 callback_set_value_instance(
363 const std::shared_ptr<PropertyType>& value,
364 std::function<bool(const PropertyType&, PropertyType&)>&& func) :
365 value_(value), func_(std::move(func))
366 {}
operator ()(const std::any & value)367 SetPropertyReturnValue operator()(const std::any& value)
368 {
369 const PropertyType& newValue = std::any_cast<PropertyType>(value);
370 PropertyType oldValue = *value_;
371 if (func_(newValue, *value_) == false)
372 {
373 return SetPropertyReturnValue::fail;
374 }
375 if (oldValue == *value_)
376 {
377 return SetPropertyReturnValue::sameValueUpdated;
378 }
379 return SetPropertyReturnValue::valueUpdated;
380 }
381
382 private:
383 std::shared_ptr<PropertyType> value_;
384 std::function<bool(const PropertyType&, PropertyType&)> func_;
385 };
386
387 enum class PropertyPermission
388 {
389 readOnly,
390 readWrite
391 };
392
393 class dbus_interface
394 {
395 public:
dbus_interface(std::shared_ptr<sdbusplus::asio::connection> conn,const std::string & path,const std::string & name)396 dbus_interface(std::shared_ptr<sdbusplus::asio::connection> conn,
397 const std::string& path, const std::string& name) :
398 conn_(conn), path_(path), name_(name)
399
400 {}
401
402 dbus_interface(const dbus_interface&) = delete;
403 dbus_interface& operator=(const dbus_interface&) = delete;
404 dbus_interface(dbus_interface&&) = delete;
405 dbus_interface& operator=(dbus_interface&&) = delete;
406
~dbus_interface()407 ~dbus_interface()
408 {
409 conn_->emit_interfaces_removed(path_.c_str(),
410 std::vector<std::string>{name_});
411 }
412
413 template <typename PropertyType, typename CallbackTypeGet>
register_property_r(const std::string & name,const PropertyType & property,decltype(vtable_t::flags)flags,CallbackTypeGet && getFunction)414 bool register_property_r(const std::string& name,
415 const PropertyType& property,
416 decltype(vtable_t::flags) flags,
417 CallbackTypeGet&& getFunction)
418 {
419 // can only register once
420 if (is_initialized())
421 {
422 return false;
423 }
424 if (sd_bus_member_name_is_valid(name.c_str()) != 1)
425 {
426 return false;
427 }
428 static const auto type =
429 utility::tuple_to_array(message::types::type_id<PropertyType>());
430
431 auto propertyPtr = std::make_shared<PropertyType>(property);
432
433 property_callbacks_.emplace_back(
434 *this, name,
435 callback_get_instance<PropertyType, CallbackTypeGet>(
436 propertyPtr, std::move(getFunction)),
437 nullptr,
438 callback_set_value_instance<PropertyType>(
439 propertyPtr, details::nop_set_value<PropertyType>),
440 type.data(), flags);
441
442 return true;
443 }
444
445 template <typename PropertyType, typename CallbackTypeGet>
register_property_r(const std::string & name,decltype(vtable_t::flags)flags,CallbackTypeGet && getFunction)446 bool register_property_r(const std::string& name,
447 decltype(vtable_t::flags) flags,
448 CallbackTypeGet&& getFunction)
449 {
450 return register_property_r(name, PropertyType{}, flags,
451 std::forward<CallbackTypeGet>(getFunction));
452 }
453
454 template <typename PropertyType, typename CallbackTypeSet,
455 typename CallbackTypeGet>
register_property_rw(const std::string & name,const PropertyType & property,decltype(vtable_t::flags)flags,CallbackTypeSet && setFunction,CallbackTypeGet && getFunction)456 bool register_property_rw(
457 const std::string& name, const PropertyType& property,
458 decltype(vtable_t::flags) flags, CallbackTypeSet&& setFunction,
459 CallbackTypeGet&& getFunction)
460 {
461 // can only register once
462 if (is_initialized())
463 {
464 return false;
465 }
466 if (sd_bus_member_name_is_valid(name.c_str()) != 1)
467 {
468 return false;
469 }
470 static const auto type =
471 utility::tuple_to_array(message::types::type_id<PropertyType>());
472
473 auto propertyPtr = std::make_shared<PropertyType>(property);
474
475 property_callbacks_.emplace_back(
476 *this, name,
477 callback_get_instance<PropertyType, CallbackTypeGet>(
478 propertyPtr, std::move(getFunction)),
479 callback_set_message_instance<PropertyType>(
480 propertyPtr, CallbackTypeSet(setFunction)),
481 callback_set_value_instance<PropertyType>(propertyPtr,
482 std::move(setFunction)),
483
484 type.data(), flags);
485
486 return true;
487 }
488
489 template <typename PropertyType, typename CallbackTypeSet,
490 typename CallbackTypeGet>
register_property_rw(const std::string & name,decltype(vtable_t::flags)flags,CallbackTypeSet && setFunction,CallbackTypeGet && getFunction)491 bool register_property_rw(const std::string& name,
492 decltype(vtable_t::flags) flags,
493 CallbackTypeSet&& setFunction,
494 CallbackTypeGet&& getFunction)
495 {
496 return register_property_rw(name, PropertyType{}, flags,
497 std::forward<CallbackTypeSet>(setFunction),
498 std::forward<CallbackTypeGet>(getFunction));
499 }
500
501 // default getter and setter
502 template <typename PropertyType>
register_property(const std::string & name,const PropertyType & property,PropertyPermission access=PropertyPermission::readOnly)503 bool register_property(
504 const std::string& name, const PropertyType& property,
505 PropertyPermission access = PropertyPermission::readOnly)
506 {
507 if (access == PropertyPermission::readOnly)
508 {
509 return register_property_r(name, property,
510 vtable::property_::emits_change,
511 details::nop_get_value<PropertyType>);
512 }
513 else
514 {
515 return register_property_rw(
516 name, property, vtable::property_::emits_change,
517 details::nop_set_value<PropertyType>,
518 details::nop_get_value<PropertyType>);
519 }
520 }
521
522 // custom setter, sets take an input property and respond with an int status
523 template <typename PropertyType, typename CallbackTypeSet>
register_property(const std::string & name,const PropertyType & property,CallbackTypeSet && setFunction)524 bool register_property(const std::string& name,
525 const PropertyType& property,
526 CallbackTypeSet&& setFunction)
527 {
528 return register_property_rw(
529 name, property, vtable::property_::emits_change,
530 std::forward<CallbackTypeSet>(setFunction),
531 details::nop_get_value<PropertyType>);
532 }
533
534 // custom getter and setter, gets take an input of void and respond with a
535 // property. property is only passed for type deduction
536 template <typename PropertyType, typename CallbackTypeSet,
537 typename CallbackTypeGet>
register_property(const std::string & name,const PropertyType & property,CallbackTypeSet && setFunction,CallbackTypeGet && getFunction)538 bool register_property(const std::string& name,
539 const PropertyType& property,
540 CallbackTypeSet&& setFunction,
541 CallbackTypeGet&& getFunction)
542 {
543 return register_property_rw(
544 name, property, vtable::property_::emits_change,
545 std::forward<CallbackTypeSet>(setFunction),
546 std::forward<CallbackTypeGet>(getFunction));
547 }
548
549 template <typename PropertyType, bool changesOnly = false>
set_property(const std::string & name,const PropertyType & value)550 bool set_property(const std::string& name, const PropertyType& value)
551 {
552 if (!is_initialized())
553 {
554 return false;
555 }
556 auto func = std::find_if(
557 property_callbacks_.begin(), property_callbacks_.end(),
558 [&name](const auto& element) { return element.name_ == name; });
559 if (func != property_callbacks_.end())
560 {
561 SetPropertyReturnValue status = func->on_set_value_(value);
562 if ((status == SetPropertyReturnValue::valueUpdated) ||
563 (status == SetPropertyReturnValue::sameValueUpdated))
564 {
565 if (status != SetPropertyReturnValue::sameValueUpdated)
566 {
567 signal_property(name);
568 return true;
569 }
570 if constexpr (!changesOnly)
571 {
572 return true;
573 }
574 }
575 }
576 return false;
577 }
578
579 template <typename... SignalSignature>
register_signal(const std::string & name)580 bool register_signal(const std::string& name)
581 {
582 if (is_initialized())
583 {
584 return false;
585 }
586 if (sd_bus_member_name_is_valid(name.c_str()) != 1)
587 {
588 return false;
589 }
590
591 static constexpr auto signature = utility::tuple_to_array(
592 message::types::type_id<SignalSignature...>());
593
594 signals_.emplace_back(name, signature.data());
595 return true;
596 }
597
598 template <typename CallbackType>
register_method(const std::string & name,CallbackType && handler,decltype(vtable_t::flags)flags=0)599 bool register_method(const std::string& name, CallbackType&& handler,
600 decltype(vtable_t::flags) flags = 0)
601 {
602 using ActualSignature = boost::callable_traits::args_t<CallbackType>;
603 using CallbackSignature = utility::strip_first_n_args_t<
604 details::NonDbusArgsCount<ActualSignature>::size(),
605 ActualSignature>;
606 using InputTupleType = utility::decay_tuple_t<CallbackSignature>;
607 using ResultType = boost::callable_traits::return_type_t<CallbackType>;
608
609 if (is_initialized())
610 {
611 return false;
612 }
613 static const auto argType = utility::strip_ends(
614 utility::tuple_to_array(message::types::type_id<InputTupleType>()));
615 static const auto resultType =
616 utility::tuple_to_array(message::types::type_id<ResultType>());
617
618 std::function<int(message_t&)> func;
619 if constexpr (FirstArgIsYield_v<CallbackType>)
620 {
621 func = coroutine_method_instance<CallbackType>(
622 conn_->get_io_context(), std::move(handler));
623 }
624 else
625 {
626 func = callback_method_instance<CallbackType>(std::move(handler));
627 }
628 method_callbacks_.emplace_back(name, std::move(func), argType.data(),
629 resultType.data(), flags);
630
631 return true;
632 }
633
get_handler(sd_bus *,const char *,const char *,const char *,sd_bus_message * reply,void * userdata,sd_bus_error * error)634 static int get_handler(sd_bus* /*bus*/, const char* /*path*/,
635 const char* /*interface*/, const char* /*property*/,
636 sd_bus_message* reply, void* userdata,
637 sd_bus_error* error)
638 {
639 property_callback* func = static_cast<property_callback*>(userdata);
640 auto mesg = message_t(reply);
641 #ifdef __EXCEPTIONS
642 try
643 {
644 #endif
645 return func->on_get_(mesg);
646 #ifdef __EXCEPTIONS
647 }
648
649 catch (const sdbusplus::exception_t& e)
650 {
651 return e.set_error(error);
652 }
653 catch (...)
654 {
655 // hit default error below
656 }
657 #endif
658 return sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS,
659 nullptr);
660 }
661
set_handler(sd_bus *,const char *,const char *,const char *,sd_bus_message * value,void * userdata,sd_bus_error * error)662 static int set_handler(sd_bus* /*bus*/, const char* /*path*/,
663 const char* /*interface*/, const char* /*property*/,
664 sd_bus_message* value, void* userdata,
665 sd_bus_error* error)
666 {
667 property_callback* func = static_cast<property_callback*>(userdata);
668
669 auto mesg = message_t(value);
670 #ifdef __EXCEPTIONS
671 try
672 {
673 #endif
674 SetPropertyReturnValue status = func->on_set_message_(mesg);
675 if ((status == SetPropertyReturnValue::valueUpdated) ||
676 (status == SetPropertyReturnValue::sameValueUpdated))
677 {
678 if (status != SetPropertyReturnValue::sameValueUpdated)
679 {
680 func->interface_.signal_property(func->name_);
681 }
682 // There shouldn't be any other callbacks that want to
683 // handle the message so just return a positive integer.
684 return 1;
685 }
686 #ifdef __EXCEPTIONS
687 }
688
689 catch (const sdbusplus::exception_t& e)
690 {
691 return e.set_error(error);
692 }
693 catch (...)
694 {
695 // hit default error below
696 }
697 #endif
698 return sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS,
699 nullptr);
700 }
701
method_handler(sd_bus_message * m,void * userdata,sd_bus_error * error)702 static int method_handler(sd_bus_message* m, void* userdata,
703 sd_bus_error* error)
704 {
705 method_callback* func = static_cast<method_callback*>(userdata);
706 auto mesg = message_t(m);
707 #ifdef __EXCEPTIONS
708 try
709 {
710 #endif
711 int status = func->call_(mesg);
712 if (status == 1)
713 {
714 return status;
715 }
716 #ifdef __EXCEPTIONS
717 }
718
719 catch (const sdbusplus::exception_t& e)
720 {
721 return e.set_error(error);
722 }
723 catch (...)
724 {
725 // hit default error below
726 }
727 #endif
728 return sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS,
729 nullptr);
730 }
731
732 /** @brief Create a new signal message.
733 *
734 * @param[in] member - The signal name to create.
735 */
new_signal(const char * member)736 auto new_signal(const char* member)
737 {
738 if (!is_initialized())
739 {
740 return message_t(nullptr);
741 }
742 return interface_->new_signal(member);
743 }
744
initialize(const bool skipPropertyChangedSignal=false)745 bool initialize(const bool skipPropertyChangedSignal = false)
746 {
747 // can only register once
748 if (is_initialized())
749 {
750 return false;
751 }
752 vtable_.reserve(2 + property_callbacks_.size() +
753 method_callbacks_.size() + signals_.size());
754 vtable_.emplace_back(vtable::start());
755 property_callbacks_.shrink_to_fit();
756 for (auto& element : property_callbacks_)
757 {
758 if (element.on_set_message_)
759 {
760 vtable_.emplace_back(vtable::property_o(
761 element.name_.c_str(), element.signature_, get_handler,
762 set_handler, reinterpret_cast<size_t>(&element),
763 element.flags_ | SD_BUS_VTABLE_ABSOLUTE_OFFSET));
764 }
765 else
766 {
767 vtable_.emplace_back(vtable::property_o(
768 element.name_.c_str(), element.signature_, get_handler,
769 reinterpret_cast<size_t>(&element),
770 element.flags_ | SD_BUS_VTABLE_ABSOLUTE_OFFSET));
771 }
772 }
773
774 method_callbacks_.shrink_to_fit();
775 for (auto& element : method_callbacks_)
776 {
777 vtable_.emplace_back(vtable::method_o(
778 element.name_.c_str(), element.arg_signature_,
779 element.return_signature_, method_handler,
780 reinterpret_cast<size_t>(&element),
781 element.flags_ | SD_BUS_VTABLE_ABSOLUTE_OFFSET));
782 }
783
784 signals_.shrink_to_fit();
785 for (const auto& element : signals_)
786 {
787 vtable_.emplace_back(
788 vtable::signal(element.name_.c_str(), element.signature_));
789 }
790
791 vtable_.emplace_back(vtable::end());
792 vtable_.shrink_to_fit();
793
794 interface_.emplace(static_cast<sdbusplus::bus_t&>(*conn_),
795 path_.c_str(), name_.c_str(),
796 static_cast<const sd_bus_vtable*>(&vtable_[0]),
797 nullptr);
798 conn_->emit_interfaces_added(path_.c_str(),
799 std::vector<std::string>{name_});
800 if (!skipPropertyChangedSignal)
801 {
802 for (const auto& element : property_callbacks_)
803 {
804 signal_property(element.name_);
805 }
806 }
807 return true;
808 }
809
is_initialized()810 bool is_initialized()
811 {
812 return interface_.has_value();
813 }
814
signal_property(const std::string & name)815 bool signal_property(const std::string& name)
816 {
817 if (!is_initialized())
818 {
819 return false;
820 }
821 interface_->property_changed(name.c_str());
822 return true;
823 }
824
get_object_path(void)825 std::string get_object_path(void)
826 {
827 return path_;
828 }
829
get_interface_name(void)830 std::string get_interface_name(void)
831 {
832 return name_;
833 }
834
835 private:
836 std::shared_ptr<sdbusplus::asio::connection> conn_;
837 std::string path_;
838 std::string name_;
839
840 std::vector<signal> signals_;
841 std::vector<property_callback> property_callbacks_;
842 std::vector<method_callback> method_callbacks_;
843
844 std::vector<sd_bus_vtable> vtable_;
845 std::optional<sdbusplus::server::interface_t> interface_;
846 };
847
848 class object_server
849 {
850 public:
object_server(const std::shared_ptr<sdbusplus::asio::connection> & conn,const bool skipManager=false)851 object_server(const std::shared_ptr<sdbusplus::asio::connection>& conn,
852 const bool skipManager = false) : conn_(conn)
853 {
854 if (!skipManager)
855 {
856 add_manager("/");
857 }
858 }
859
add_interface(const std::string & path,const std::string & name)860 std::shared_ptr<dbus_interface> add_interface(const std::string& path,
861 const std::string& name)
862 {
863 auto dbusIface = std::make_shared<dbus_interface>(conn_, path, name);
864 interfaces_.emplace_back(dbusIface);
865 return dbusIface;
866 }
867
add_unique_interface(const std::string & path,const std::string & name)868 std::unique_ptr<dbus_interface> add_unique_interface(
869 const std::string& path, const std::string& name)
870 {
871 return std::make_unique<dbus_interface>(conn_, path, name);
872 }
873
874 /**
875 @brief creates initialized dbus_interface
876 @param path a string path to interface
877 @param name a string name of the interface
878 @param initializer a functor (void (dbus_interface&)) to be called before
879 call to dbus_interface::initialize
880 @return an unique_ptr to initialized dbus_interface
881 */
882 template <class Initializer>
add_unique_interface(const std::string & path,const std::string & name,Initializer && initializer)883 std::unique_ptr<dbus_interface> add_unique_interface(
884 const std::string& path, const std::string& name,
885 Initializer&& initializer)
886 {
887 auto dbusIface = std::make_unique<dbus_interface>(conn_, path, name);
888 initializer(*dbusIface);
889 dbusIface->initialize();
890 return dbusIface;
891 }
892
add_manager(const std::string & path)893 void add_manager(const std::string& path)
894 {
895 managers_.emplace_back(static_cast<sdbusplus::bus_t&>(*conn_),
896 path.c_str());
897 }
898
remove_interface(const std::shared_ptr<dbus_interface> & iface)899 bool remove_interface(const std::shared_ptr<dbus_interface>& iface)
900 {
901 auto findIface =
902 std::find(interfaces_.begin(), interfaces_.end(), iface);
903 if (findIface != interfaces_.end())
904 {
905 interfaces_.erase(findIface);
906 return true;
907 }
908 return false;
909 }
910
911 private:
912 std::shared_ptr<sdbusplus::asio::connection> conn_;
913 std::vector<std::shared_ptr<dbus_interface>> interfaces_;
914 std::vector<server::manager_t> managers_;
915 };
916
917 } // namespace asio
918 } // namespace sdbusplus
919