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