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 (!utility::read_into_tuple(dbusArgs, m))
206 {
207 return -EINVAL;
208 }
209 auto ret = m.new_method_return();
210 if constexpr (callbackWantsMessage<CallbackType>)
211 {
212 InputTupleType inputArgs =
213 std::tuple_cat(std::forward_as_tuple(std::move(m)), dbusArgs);
214 callFunction(ret, inputArgs, func_);
215 }
216 else
217 {
218 callFunction(ret, dbusArgs, func_);
219 }
220 ret.method_return();
221 return 1;
222 }
223 };
224
225 #ifndef SDBUSPLUS_DISABLE_BOOST_COROUTINES
226 template <typename CallbackType>
227 class coroutine_method_instance
228 {
229 public:
230 using self_t = coroutine_method_instance<CallbackType>;
coroutine_method_instance(boost::asio::io_context & io,CallbackType && func)231 coroutine_method_instance(boost::asio::io_context& io,
232 CallbackType&& func) : io_(io), func_(func)
233 {}
234
operator ()(message_t & m)235 int operator()(message_t& m)
236 {
237 // make a copy of m to move into the coroutine
238 message_t b{m};
239
240 // spawn off a new coroutine to handle the method call
241 boost::asio::spawn(io_, std::bind_front(&self_t::after_spawn, this, b),
242 boost::asio::detached);
243
244 return 1;
245 }
246
247 private:
248 boost::asio::io_context& io_;
249 CallbackType func_;
after_spawn(message_t b,boost::asio::yield_context yield)250 void after_spawn(message_t b, boost::asio::yield_context yield)
251 {
252 using CallbackSignature = boost::callable_traits::args_t<CallbackType>;
253 using InputTupleType = utility::decay_tuple_t<CallbackSignature>;
254 using DbusTupleType = utility::strip_first_n_args_t<
255 details::NonDbusArgsCount<InputTupleType>::size(), InputTupleType>;
256 DbusTupleType dbusArgs;
257 try
258 {
259 utility::read_into_tuple(dbusArgs, b);
260 }
261 catch (const exception::SdBusError& e)
262 {
263 auto ret = b.new_method_errno(e.get_errno(), e.get_error());
264 ret.method_return();
265 return;
266 }
267
268 try
269 {
270 auto ret = b.new_method_return();
271 if constexpr (callbackWantsMessage<CallbackType>)
272 {
273 InputTupleType inputArgs = std::tuple_cat(
274 std::forward_as_tuple(std::move(yield)),
275 std::forward_as_tuple(std::move(b)), dbusArgs);
276 callFunction(ret, inputArgs, func_);
277 }
278 else
279 {
280 InputTupleType inputArgs = std::tuple_cat(
281 std::forward_as_tuple(std::move(yield)), dbusArgs);
282 callFunction(ret, inputArgs, func_);
283 }
284 ret.method_return();
285 }
286 catch (const sdbusplus::exception::SdBusError& e)
287 {
288 // Catch D-Bus error explicitly called by method handler
289 message_t err = b.new_method_errno(e.get_errno(), e.get_error());
290 err.method_return();
291 }
292 catch (const sdbusplus::exception_t& e)
293 {
294 message_t err = b.new_method_error(e);
295 err.method_return();
296 }
297 catch (...)
298 {
299 message_t err = b.new_method_errno(-EIO);
300 err.method_return();
301 }
302 }
303 };
304 #endif
305
306 template <typename PropertyType, typename CallbackType>
307 class callback_get_instance
308 {
309 public:
callback_get_instance(const std::shared_ptr<PropertyType> & value,CallbackType && func)310 callback_get_instance(const std::shared_ptr<PropertyType>& value,
311 CallbackType&& func) :
312 value_(value), func_(std::forward<CallbackType>(func))
313 {}
operator ()(message_t & m)314 int operator()(message_t& m)
315 {
316 *value_ = func_(*value_);
317 m.append(*value_);
318 return 1;
319 }
320
321 private:
322 std::shared_ptr<PropertyType> value_;
323 CallbackType func_;
324 };
325
326 template <typename PropertyType>
327 class callback_set_message_instance
328 {
329 public:
callback_set_message_instance(const std::shared_ptr<PropertyType> & value,std::function<bool (const PropertyType &,PropertyType &)> && func)330 callback_set_message_instance(
331 const std::shared_ptr<PropertyType>& value,
332 std::function<bool(const PropertyType&, PropertyType&)>&& func) :
333 value_(value), func_(std::move(func))
334 {}
operator ()(message_t & m)335 SetPropertyReturnValue operator()(message_t& m)
336 {
337 PropertyType input;
338 m.read(input);
339 PropertyType oldValue = *value_;
340 if (!func_(input, *value_))
341 {
342 return SetPropertyReturnValue::fail;
343 }
344 if (oldValue == *value_)
345 {
346 return SetPropertyReturnValue::sameValueUpdated;
347 }
348 return SetPropertyReturnValue::valueUpdated;
349 }
350
351 private:
352 std::shared_ptr<PropertyType> value_;
353 std::function<bool(const PropertyType&, PropertyType&)> func_;
354 };
355
356 template <typename PropertyType>
357 class callback_set_value_instance
358 {
359 public:
callback_set_value_instance(const std::shared_ptr<PropertyType> & value,std::function<bool (const PropertyType &,PropertyType &)> && func)360 callback_set_value_instance(
361 const std::shared_ptr<PropertyType>& value,
362 std::function<bool(const PropertyType&, PropertyType&)>&& func) :
363 value_(value), func_(std::move(func))
364 {}
operator ()(const std::any & value)365 SetPropertyReturnValue operator()(const std::any& value)
366 {
367 const PropertyType& newValue = std::any_cast<PropertyType>(value);
368 PropertyType oldValue = *value_;
369 if (func_(newValue, *value_) == false)
370 {
371 return SetPropertyReturnValue::fail;
372 }
373 if (oldValue == *value_)
374 {
375 return SetPropertyReturnValue::sameValueUpdated;
376 }
377 return SetPropertyReturnValue::valueUpdated;
378 }
379
380 private:
381 std::shared_ptr<PropertyType> value_;
382 std::function<bool(const PropertyType&, PropertyType&)> func_;
383 };
384
385 enum class PropertyPermission
386 {
387 readOnly,
388 readWrite
389 };
390
391 class dbus_interface
392 {
393 public:
dbus_interface(std::shared_ptr<sdbusplus::asio::connection> conn,const std::string & path,const std::string & name)394 dbus_interface(std::shared_ptr<sdbusplus::asio::connection> conn,
395 const std::string& path, const std::string& name) :
396 conn_(conn), path_(path), name_(name)
397
398 {}
399
400 dbus_interface(const dbus_interface&) = delete;
401 dbus_interface& operator=(const dbus_interface&) = delete;
402 dbus_interface(dbus_interface&&) = delete;
403 dbus_interface& operator=(dbus_interface&&) = delete;
404
~dbus_interface()405 ~dbus_interface()
406 {
407 conn_->emit_interfaces_removed(path_.c_str(),
408 std::vector<std::string>{name_});
409 }
410
411 template <typename PropertyType, typename CallbackTypeGet>
register_property_r(const std::string & name,const PropertyType & property,decltype(vtable_t::flags)flags,CallbackTypeGet && getFunction)412 bool register_property_r(const std::string& name,
413 const PropertyType& property,
414 decltype(vtable_t::flags) flags,
415 CallbackTypeGet&& getFunction)
416 {
417 // can only register once
418 if (is_initialized())
419 {
420 return false;
421 }
422 if (sd_bus_member_name_is_valid(name.c_str()) != 1)
423 {
424 return false;
425 }
426 static const auto type =
427 utility::tuple_to_array(message::types::type_id<PropertyType>());
428
429 auto propertyPtr = std::make_shared<PropertyType>(property);
430
431 property_callbacks_.emplace_back(
432 *this, name,
433 callback_get_instance<PropertyType, CallbackTypeGet>(
434 propertyPtr, std::move(getFunction)),
435 nullptr,
436 callback_set_value_instance<PropertyType>(
437 propertyPtr, details::nop_set_value<PropertyType>),
438 type.data(), flags);
439
440 return true;
441 }
442
443 template <typename PropertyType, typename CallbackTypeGet>
register_property_r(const std::string & name,decltype(vtable_t::flags)flags,CallbackTypeGet && getFunction)444 bool register_property_r(const std::string& name,
445 decltype(vtable_t::flags) flags,
446 CallbackTypeGet&& getFunction)
447 {
448 return register_property_r(name, PropertyType{}, flags,
449 std::forward<CallbackTypeGet>(getFunction));
450 }
451
452 template <typename PropertyType, typename CallbackTypeSet,
453 typename CallbackTypeGet>
register_property_rw(const std::string & name,const PropertyType & property,decltype(vtable_t::flags)flags,CallbackTypeSet && setFunction,CallbackTypeGet && getFunction)454 bool register_property_rw(
455 const std::string& name, const PropertyType& property,
456 decltype(vtable_t::flags) flags, CallbackTypeSet&& setFunction,
457 CallbackTypeGet&& getFunction)
458 {
459 // can only register once
460 if (is_initialized())
461 {
462 return false;
463 }
464 if (sd_bus_member_name_is_valid(name.c_str()) != 1)
465 {
466 return false;
467 }
468 static const auto type =
469 utility::tuple_to_array(message::types::type_id<PropertyType>());
470
471 auto propertyPtr = std::make_shared<PropertyType>(property);
472
473 property_callbacks_.emplace_back(
474 *this, name,
475 callback_get_instance<PropertyType, CallbackTypeGet>(
476 propertyPtr, std::move(getFunction)),
477 callback_set_message_instance<PropertyType>(
478 propertyPtr, CallbackTypeSet(setFunction)),
479 callback_set_value_instance<PropertyType>(propertyPtr,
480 std::move(setFunction)),
481
482 type.data(), flags);
483
484 return true;
485 }
486
487 template <typename PropertyType, typename CallbackTypeSet,
488 typename CallbackTypeGet>
register_property_rw(const std::string & name,decltype(vtable_t::flags)flags,CallbackTypeSet && setFunction,CallbackTypeGet && getFunction)489 bool register_property_rw(const std::string& name,
490 decltype(vtable_t::flags) flags,
491 CallbackTypeSet&& setFunction,
492 CallbackTypeGet&& getFunction)
493 {
494 return register_property_rw(name, PropertyType{}, flags,
495 std::forward<CallbackTypeSet>(setFunction),
496 std::forward<CallbackTypeGet>(getFunction));
497 }
498
499 // default getter and setter
500 template <typename PropertyType>
register_property(const std::string & name,const PropertyType & property,PropertyPermission access=PropertyPermission::readOnly)501 bool register_property(
502 const std::string& name, const PropertyType& property,
503 PropertyPermission access = PropertyPermission::readOnly)
504 {
505 if (access == PropertyPermission::readOnly)
506 {
507 return register_property_r(name, property,
508 vtable::property_::emits_change,
509 details::nop_get_value<PropertyType>);
510 }
511 else
512 {
513 return register_property_rw(
514 name, property, vtable::property_::emits_change,
515 details::nop_set_value<PropertyType>,
516 details::nop_get_value<PropertyType>);
517 }
518 }
519
520 // custom setter, sets take an input property and respond with an int status
521 template <typename PropertyType, typename CallbackTypeSet>
register_property(const std::string & name,const PropertyType & property,CallbackTypeSet && setFunction)522 bool register_property(const std::string& name,
523 const PropertyType& property,
524 CallbackTypeSet&& setFunction)
525 {
526 return register_property_rw(
527 name, property, vtable::property_::emits_change,
528 std::forward<CallbackTypeSet>(setFunction),
529 details::nop_get_value<PropertyType>);
530 }
531
532 // custom getter and setter, gets take an input of void and respond with a
533 // property. property is only passed for type deduction
534 template <typename PropertyType, typename CallbackTypeSet,
535 typename CallbackTypeGet>
register_property(const std::string & name,const PropertyType & property,CallbackTypeSet && setFunction,CallbackTypeGet && getFunction)536 bool register_property(const std::string& name,
537 const PropertyType& property,
538 CallbackTypeSet&& setFunction,
539 CallbackTypeGet&& getFunction)
540 {
541 return register_property_rw(
542 name, property, vtable::property_::emits_change,
543 std::forward<CallbackTypeSet>(setFunction),
544 std::forward<CallbackTypeGet>(getFunction));
545 }
546
547 template <typename PropertyType, bool changesOnly = false>
set_property(const std::string & name,const PropertyType & value)548 bool set_property(const std::string& name, const PropertyType& value)
549 {
550 if (!is_initialized())
551 {
552 return false;
553 }
554 auto func = std::find_if(
555 property_callbacks_.begin(), property_callbacks_.end(),
556 [&name](const auto& element) { return element.name_ == name; });
557 if (func != property_callbacks_.end())
558 {
559 SetPropertyReturnValue status = func->on_set_value_(value);
560 if ((status == SetPropertyReturnValue::valueUpdated) ||
561 (status == SetPropertyReturnValue::sameValueUpdated))
562 {
563 if (status != SetPropertyReturnValue::sameValueUpdated)
564 {
565 signal_property(name);
566 return true;
567 }
568 if constexpr (!changesOnly)
569 {
570 return true;
571 }
572 }
573 }
574 return false;
575 }
576
577 template <typename... SignalSignature>
register_signal(const std::string & name)578 bool register_signal(const std::string& name)
579 {
580 if (is_initialized())
581 {
582 return false;
583 }
584 if (sd_bus_member_name_is_valid(name.c_str()) != 1)
585 {
586 return false;
587 }
588
589 static constexpr auto signature = utility::tuple_to_array(
590 message::types::type_id<SignalSignature...>());
591
592 signals_.emplace_back(name, signature.data());
593 return true;
594 }
595
596 template <typename CallbackType>
register_method(const std::string & name,CallbackType && handler,decltype(vtable_t::flags)flags=0)597 bool register_method(const std::string& name, CallbackType&& handler,
598 decltype(vtable_t::flags) flags = 0)
599 {
600 using ActualSignature = boost::callable_traits::args_t<CallbackType>;
601 using CallbackSignature = utility::strip_first_n_args_t<
602 details::NonDbusArgsCount<ActualSignature>::size(),
603 ActualSignature>;
604 using InputTupleType = utility::decay_tuple_t<CallbackSignature>;
605 using ResultType = boost::callable_traits::return_type_t<CallbackType>;
606
607 if (is_initialized())
608 {
609 return false;
610 }
611 static const auto argType = utility::strip_ends(
612 utility::tuple_to_array(message::types::type_id<InputTupleType>()));
613 static const auto resultType =
614 utility::tuple_to_array(message::types::type_id<ResultType>());
615
616 std::function<int(message_t&)> func;
617 if constexpr (FirstArgIsYield_v<CallbackType>)
618 {
619 func = coroutine_method_instance<CallbackType>(
620 conn_->get_io_context(), std::move(handler));
621 }
622 else
623 {
624 func = callback_method_instance<CallbackType>(std::move(handler));
625 }
626 method_callbacks_.emplace_back(name, std::move(func), argType.data(),
627 resultType.data(), flags);
628
629 return true;
630 }
631
get_handler(sd_bus *,const char *,const char *,const char *,sd_bus_message * reply,void * userdata,sd_bus_error * error)632 static int get_handler(sd_bus* /*bus*/, const char* /*path*/,
633 const char* /*interface*/, const char* /*property*/,
634 sd_bus_message* reply, void* userdata,
635 sd_bus_error* error)
636 {
637 property_callback* func = static_cast<property_callback*>(userdata);
638 auto mesg = message_t(reply);
639 #ifdef __EXCEPTIONS
640 try
641 {
642 #endif
643 return func->on_get_(mesg);
644 #ifdef __EXCEPTIONS
645 }
646
647 catch (const sdbusplus::exception_t& e)
648 {
649 return e.set_error(error);
650 }
651 catch (...)
652 {
653 // hit default error below
654 }
655 #endif
656 return sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS,
657 nullptr);
658 }
659
set_handler(sd_bus *,const char *,const char *,const char *,sd_bus_message * value,void * userdata,sd_bus_error * error)660 static int set_handler(sd_bus* /*bus*/, const char* /*path*/,
661 const char* /*interface*/, const char* /*property*/,
662 sd_bus_message* value, void* userdata,
663 sd_bus_error* error)
664 {
665 property_callback* func = static_cast<property_callback*>(userdata);
666
667 auto mesg = message_t(value);
668 #ifdef __EXCEPTIONS
669 try
670 {
671 #endif
672 SetPropertyReturnValue status = func->on_set_message_(mesg);
673 if ((status == SetPropertyReturnValue::valueUpdated) ||
674 (status == SetPropertyReturnValue::sameValueUpdated))
675 {
676 if (status != SetPropertyReturnValue::sameValueUpdated)
677 {
678 func->interface_.signal_property(func->name_);
679 }
680 // There shouldn't be any other callbacks that want to
681 // handle the message so just return a positive integer.
682 return 1;
683 }
684 #ifdef __EXCEPTIONS
685 }
686
687 catch (const sdbusplus::exception_t& e)
688 {
689 return e.set_error(error);
690 }
691 catch (...)
692 {
693 // hit default error below
694 }
695 #endif
696 return sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS,
697 nullptr);
698 }
699
method_handler(sd_bus_message * m,void * userdata,sd_bus_error * error)700 static int method_handler(sd_bus_message* m, void* userdata,
701 sd_bus_error* error)
702 {
703 method_callback* func = static_cast<method_callback*>(userdata);
704 auto mesg = message_t(m);
705 #ifdef __EXCEPTIONS
706 try
707 {
708 #endif
709 int status = func->call_(mesg);
710 if (status == 1)
711 {
712 return status;
713 }
714 #ifdef __EXCEPTIONS
715 }
716
717 catch (const sdbusplus::exception_t& e)
718 {
719 return e.set_error(error);
720 }
721 catch (...)
722 {
723 // hit default error below
724 }
725 #endif
726 return sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS,
727 nullptr);
728 }
729
730 /** @brief Create a new signal message.
731 *
732 * @param[in] member - The signal name to create.
733 */
new_signal(const char * member)734 auto new_signal(const char* member)
735 {
736 if (!is_initialized())
737 {
738 return message_t(nullptr);
739 }
740 return interface_->new_signal(member);
741 }
742
initialize(const bool skipPropertyChangedSignal=false)743 bool initialize(const bool skipPropertyChangedSignal = false)
744 {
745 // can only register once
746 if (is_initialized())
747 {
748 return false;
749 }
750 vtable_.reserve(2 + property_callbacks_.size() +
751 method_callbacks_.size() + signals_.size());
752 vtable_.emplace_back(vtable::start());
753 property_callbacks_.shrink_to_fit();
754 for (auto& element : property_callbacks_)
755 {
756 if (element.on_set_message_)
757 {
758 vtable_.emplace_back(vtable::property_o(
759 element.name_.c_str(), element.signature_, get_handler,
760 set_handler, reinterpret_cast<size_t>(&element),
761 element.flags_ | SD_BUS_VTABLE_ABSOLUTE_OFFSET));
762 }
763 else
764 {
765 vtable_.emplace_back(vtable::property_o(
766 element.name_.c_str(), element.signature_, get_handler,
767 reinterpret_cast<size_t>(&element),
768 element.flags_ | SD_BUS_VTABLE_ABSOLUTE_OFFSET));
769 }
770 }
771
772 method_callbacks_.shrink_to_fit();
773 for (auto& element : method_callbacks_)
774 {
775 vtable_.emplace_back(vtable::method_o(
776 element.name_.c_str(), element.arg_signature_,
777 element.return_signature_, method_handler,
778 reinterpret_cast<size_t>(&element),
779 element.flags_ | SD_BUS_VTABLE_ABSOLUTE_OFFSET));
780 }
781
782 signals_.shrink_to_fit();
783 for (const auto& element : signals_)
784 {
785 vtable_.emplace_back(
786 vtable::signal(element.name_.c_str(), element.signature_));
787 }
788
789 vtable_.emplace_back(vtable::end());
790 vtable_.shrink_to_fit();
791
792 interface_.emplace(static_cast<sdbusplus::bus_t&>(*conn_),
793 path_.c_str(), name_.c_str(),
794 static_cast<const sd_bus_vtable*>(&vtable_[0]),
795 nullptr);
796 conn_->emit_interfaces_added(path_.c_str(),
797 std::vector<std::string>{name_});
798 if (!skipPropertyChangedSignal)
799 {
800 for (const auto& element : property_callbacks_)
801 {
802 signal_property(element.name_);
803 }
804 }
805 return true;
806 }
807
is_initialized()808 bool is_initialized()
809 {
810 return interface_.has_value();
811 }
812
signal_property(const std::string & name)813 bool signal_property(const std::string& name)
814 {
815 if (!is_initialized())
816 {
817 return false;
818 }
819 interface_->property_changed(name.c_str());
820 return true;
821 }
822
get_object_path(void)823 std::string get_object_path(void)
824 {
825 return path_;
826 }
827
get_interface_name(void)828 std::string get_interface_name(void)
829 {
830 return name_;
831 }
832
833 private:
834 std::shared_ptr<sdbusplus::asio::connection> conn_;
835 std::string path_;
836 std::string name_;
837
838 std::vector<signal> signals_;
839 std::vector<property_callback> property_callbacks_;
840 std::vector<method_callback> method_callbacks_;
841
842 std::vector<sd_bus_vtable> vtable_;
843 std::optional<sdbusplus::server::interface_t> interface_;
844 };
845
846 class object_server
847 {
848 public:
object_server(const std::shared_ptr<sdbusplus::asio::connection> & conn,const bool skipManager=false)849 object_server(const std::shared_ptr<sdbusplus::asio::connection>& conn,
850 const bool skipManager = false) : conn_(conn)
851 {
852 if (!skipManager)
853 {
854 add_manager("/");
855 }
856 }
857
add_interface(const std::string & path,const std::string & name)858 std::shared_ptr<dbus_interface> add_interface(const std::string& path,
859 const std::string& name)
860 {
861 auto dbusIface = std::make_shared<dbus_interface>(conn_, path, name);
862 interfaces_.emplace_back(dbusIface);
863 return dbusIface;
864 }
865
add_unique_interface(const std::string & path,const std::string & name)866 std::unique_ptr<dbus_interface> add_unique_interface(
867 const std::string& path, const std::string& name)
868 {
869 return std::make_unique<dbus_interface>(conn_, path, name);
870 }
871
872 /**
873 @brief creates initialized dbus_interface
874 @param path a string path to interface
875 @param name a string name of the interface
876 @param initializer a functor (void (dbus_interface&)) to be called before
877 call to dbus_interface::initialize
878 @return an unique_ptr to initialized dbus_interface
879 */
880 template <class Initializer>
add_unique_interface(const std::string & path,const std::string & name,Initializer && initializer)881 std::unique_ptr<dbus_interface> add_unique_interface(
882 const std::string& path, const std::string& name,
883 Initializer&& initializer)
884 {
885 auto dbusIface = std::make_unique<dbus_interface>(conn_, path, name);
886 initializer(*dbusIface);
887 dbusIface->initialize();
888 return dbusIface;
889 }
890
add_manager(const std::string & path)891 void add_manager(const std::string& path)
892 {
893 managers_.emplace_back(static_cast<sdbusplus::bus_t&>(*conn_),
894 path.c_str());
895 }
896
remove_interface(const std::shared_ptr<dbus_interface> & iface)897 bool remove_interface(const std::shared_ptr<dbus_interface>& iface)
898 {
899 auto findIface =
900 std::find(interfaces_.begin(), interfaces_.end(), iface);
901 if (findIface != interfaces_.end())
902 {
903 interfaces_.erase(findIface);
904 return true;
905 }
906 return false;
907 }
908
909 private:
910 std::shared_ptr<sdbusplus::asio::connection> conn_;
911 std::vector<std::shared_ptr<dbus_interface>> interfaces_;
912 std::vector<server::manager_t> managers_;
913 };
914
915 } // namespace asio
916 } // namespace sdbusplus
917