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