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