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