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