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