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