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