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