| #pragma once |
| |
| #include <boost/any.hpp> |
| #include <boost/container/flat_map.hpp> |
| #include <list> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/exception.hpp> |
| #include <sdbusplus/message/read.hpp> |
| #include <sdbusplus/message/types.hpp> |
| #include <sdbusplus/server.hpp> |
| #include <sdbusplus/utility/tuple_to_array.hpp> |
| |
| namespace sdbusplus |
| { |
| namespace asio |
| { |
| |
| constexpr const char *PropertyNameAllowedCharacters = |
| "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; |
| class callback |
| { |
| public: |
| virtual int call(message::message &m) = 0; |
| }; |
| class callback_set |
| { |
| public: |
| virtual int call(message::message &m) = 0; |
| virtual int set(const boost::any &value) = 0; |
| }; |
| |
| template <typename T> |
| using FirstArgIsMessage = |
| std::is_same<typename utility::get_first_arg<typename utility::decay_tuple< |
| boost::callable_traits::args_t<T>>::type>::type, |
| message::message>; |
| |
| template <typename CallbackType> |
| class callback_method_instance : public callback |
| { |
| public: |
| callback_method_instance(CallbackType &&func) : func_(std::move(func)) |
| { |
| } |
| int call(message::message &m) override |
| { |
| return expandCall<CallbackType>(m); |
| } |
| |
| private: |
| using CallbackSignature = boost::callable_traits::args_t<CallbackType>; |
| using InputTupleType = |
| typename utility::decay_tuple<CallbackSignature>::type; |
| using ResultType = boost::callable_traits::return_type_t<CallbackType>; |
| CallbackType func_; |
| template <typename T> |
| std::enable_if_t<!std::is_void<T>::value, void> |
| callFunction(message::message &m, InputTupleType &inputArgs) |
| { |
| ResultType r = std::experimental::apply(func_, inputArgs); |
| m.append(r); |
| } |
| template <typename T> |
| std::enable_if_t<std::is_void<T>::value, void> |
| callFunction(message::message &m, InputTupleType &inputArgs) |
| { |
| std::experimental::apply(func_, inputArgs); |
| } |
| // optional message-first-argument callback |
| template <typename T> |
| std::enable_if_t<FirstArgIsMessage<T>::value, int> |
| expandCall(message::message &m) |
| { |
| using DbusTupleType = |
| typename utility::strip_first_arg<InputTupleType>::type; |
| DbusTupleType dbusArgs; |
| if (!utility::read_into_tuple(dbusArgs, m)) |
| { |
| return -EINVAL; |
| } |
| |
| auto ret = m.new_method_return(); |
| InputTupleType inputArgs = |
| std::tuple_cat(std::forward_as_tuple(std::move(m)), dbusArgs); |
| callFunction<ResultType>(ret, inputArgs); |
| ret.method_return(); |
| return 1; |
| }; |
| // normal dbus-types-only callback |
| template <typename T> |
| std::enable_if_t<!FirstArgIsMessage<T>::value, int> |
| expandCall(message::message &m) |
| { |
| InputTupleType inputArgs; |
| if (!utility::read_into_tuple(inputArgs, m)) |
| { |
| return -EINVAL; |
| } |
| |
| auto ret = m.new_method_return(); |
| callFunction<ResultType>(ret, inputArgs); |
| ret.method_return(); |
| return 1; |
| }; |
| }; |
| |
| template <typename PropertyType, typename CallbackType> |
| class callback_get_instance : public callback |
| { |
| public: |
| callback_get_instance(const std::shared_ptr<PropertyType> &value, |
| CallbackType &&func) : |
| value_(value), |
| func_(std::move(func)) |
| { |
| } |
| int call(message::message &m) override |
| { |
| auto r = func_(*value_); |
| m.append(r); |
| return 1; |
| } |
| |
| private: |
| std::shared_ptr<PropertyType> value_; |
| CallbackType func_; |
| }; |
| |
| template <typename PropertyType, typename CallbackType> |
| class callback_set_instance : public callback_set |
| { |
| public: |
| callback_set_instance(const std::shared_ptr<PropertyType> &value, |
| CallbackType &&func) : |
| value_(value), |
| func_(std::move(func)) |
| { |
| } |
| int call(message::message &m) override |
| { |
| PropertyType input; |
| m.read(input); |
| |
| return func_(input, *value_); |
| } |
| int set(const boost::any &value) |
| { |
| return func_(boost::any_cast<PropertyType>(value), *value_); |
| } |
| |
| private: |
| std::shared_ptr<PropertyType> value_; |
| CallbackType func_; |
| }; |
| |
| enum class PropertyPermission |
| { |
| readOnly, |
| readWrite |
| }; |
| class dbus_interface |
| { |
| public: |
| dbus_interface(std::shared_ptr<sdbusplus::asio::connection> conn, |
| const std::string &path, const std::string &name) : |
| conn_(conn), |
| path_(path), name_(name) |
| |
| { |
| vtable_.emplace_back(vtable::start()); |
| } |
| ~dbus_interface() |
| { |
| conn_->emit_interfaces_removed(path_.c_str(), |
| std::vector<std::string>{name_}); |
| } |
| |
| // default getter and setter |
| template <typename PropertyType> |
| bool register_property( |
| const std::string &name, const PropertyType &property, |
| PropertyPermission access = PropertyPermission::readOnly) |
| { |
| // can only register once |
| if (initialized_) |
| { |
| return false; |
| } |
| if (name.find_first_not_of(PropertyNameAllowedCharacters) != |
| std::string::npos) |
| { |
| return false; |
| } |
| static const auto type = |
| utility::tuple_to_array(message::types::type_id<PropertyType>()); |
| |
| auto nameItr = propertyNames_.emplace(propertyNames_.end(), name); |
| auto propertyPtr = std::make_shared<PropertyType>(property); |
| |
| callbacksGet_[name] = std::make_unique<callback_get_instance< |
| PropertyType, std::function<PropertyType(const PropertyType &)>>>( |
| propertyPtr, [](const PropertyType &value) { return value; }); |
| callbacksSet_[name] = std::make_unique<callback_set_instance< |
| PropertyType, |
| std::function<int(const PropertyType &, PropertyType &)>>>( |
| propertyPtr, [](const PropertyType &req, PropertyType &old) { |
| old = req; |
| return 1; |
| }); |
| |
| if (access == PropertyPermission::readOnly) |
| { |
| vtable_.emplace_back( |
| vtable::property(nameItr->c_str(), type.data(), get_handler, |
| vtable::property_::emits_change)); |
| } |
| else |
| { |
| vtable_.emplace_back( |
| vtable::property(nameItr->c_str(), type.data(), get_handler, |
| set_handler, vtable::property_::emits_change)); |
| } |
| return true; |
| } |
| |
| // custom setter, sets take an input property and respond with an int status |
| template <typename PropertyType, typename CallbackType> |
| bool register_property(const std::string &name, |
| const PropertyType &property, |
| CallbackType &&setFunction) |
| { |
| // can only register once |
| if (initialized_) |
| { |
| return false; |
| } |
| if (name.find_first_not_of(PropertyNameAllowedCharacters) != |
| std::string::npos) |
| { |
| return false; |
| } |
| static const auto type = |
| utility::tuple_to_array(message::types::type_id<PropertyType>()); |
| |
| auto nameItr = propertyNames_.emplace(propertyNames_.end(), name); |
| auto propertyPtr = std::make_shared<PropertyType>(property); |
| |
| callbacksGet_[name] = std::make_unique<callback_get_instance< |
| PropertyType, std::function<PropertyType(const PropertyType &)>>>( |
| propertyPtr, [](const PropertyType &value) { return value; }); |
| |
| callbacksSet_[name] = |
| std::make_unique<callback_set_instance<PropertyType, CallbackType>>( |
| propertyPtr, std::move(setFunction)); |
| vtable_.emplace_back(vtable::property(nameItr->c_str(), type.data(), |
| get_handler, set_handler, |
| vtable::property_::emits_change)); |
| |
| return true; |
| } |
| |
| // custom getter and setter, gets take an input of void and respond with a |
| // property. property is only passed for type deduction |
| template <typename PropertyType, typename CallbackType, |
| typename CallbackTypeGet> |
| bool register_property(const std::string &name, |
| const PropertyType &property, |
| CallbackType &&setFunction, |
| CallbackTypeGet &&getFunction) |
| { |
| // can only register once |
| if (initialized_) |
| { |
| return false; |
| } |
| if (name.find_first_not_of(PropertyNameAllowedCharacters) != |
| std::string::npos) |
| { |
| return false; |
| } |
| static const auto type = |
| utility::tuple_to_array(message::types::type_id<PropertyType>()); |
| |
| auto nameItr = propertyNames_.emplace(propertyNames_.end(), name); |
| auto propertyPtr = std::make_shared<PropertyType>(property); |
| |
| callbacksGet_[name] = std::make_unique< |
| callback_get_instance<PropertyType, CallbackTypeGet>>( |
| propertyPtr, std::move(getFunction)); |
| callbacksSet_[name] = |
| std::make_unique<callback_set_instance<PropertyType, CallbackType>>( |
| propertyPtr, std::move(setFunction)); |
| |
| vtable_.emplace_back(vtable::property(nameItr->c_str(), type.data(), |
| get_handler, set_handler, |
| vtable::property_::emits_change)); |
| |
| return true; |
| } |
| template <typename PropertyType> |
| bool set_property(const std::string &name, const PropertyType &value) |
| { |
| if (!initialized_) |
| { |
| return false; |
| } |
| auto func = callbacksSet_.find(name); |
| if (func != callbacksSet_.end()) |
| { |
| if (func->second->set(value) != 1) |
| { |
| return false; |
| } |
| signal_property(name); |
| return true; |
| } |
| return false; |
| } |
| |
| template <typename CallbackType> |
| std::enable_if_t<FirstArgIsMessage<CallbackType>::value, bool> |
| register_method(const std::string &name, CallbackType &&handler) |
| { |
| using CallbackSignature = typename utility::strip_first_arg< |
| boost::callable_traits::args_t<CallbackType>>::type; |
| using InputTupleType = |
| typename utility::decay_tuple<CallbackSignature>::type; |
| using ResultType = boost::callable_traits::return_type_t<CallbackType>; |
| |
| if (initialized_) |
| { |
| return false; |
| } |
| static const auto argType = utility::strip_ends( |
| utility::tuple_to_array(message::types::type_id<InputTupleType>())); |
| static const auto resultType = |
| utility::tuple_to_array(message::types::type_id<ResultType>()); |
| |
| auto nameItr = methodNames_.emplace(methodNames_.end(), name); |
| |
| callbacksMethod_[name] = |
| std::make_unique<callback_method_instance<CallbackType>>( |
| std::move(handler)); |
| |
| vtable_.emplace_back(vtable::method(nameItr->c_str(), argType.data(), |
| resultType.data(), method_handler)); |
| return true; |
| } |
| |
| template <typename CallbackType> |
| std::enable_if_t<!FirstArgIsMessage<CallbackType>::value, bool> |
| register_method(const std::string &name, CallbackType &&handler) |
| { |
| using CallbackSignature = boost::callable_traits::args_t<CallbackType>; |
| using InputTupleType = |
| typename utility::decay_tuple<CallbackSignature>::type; |
| using ResultType = boost::callable_traits::return_type_t<CallbackType>; |
| |
| if (initialized_) |
| { |
| return false; |
| } |
| static const auto argType = utility::strip_ends( |
| utility::tuple_to_array(message::types::type_id<InputTupleType>())); |
| static const auto resultType = |
| utility::tuple_to_array(message::types::type_id<ResultType>()); |
| |
| auto nameItr = methodNames_.emplace(methodNames_.end(), name); |
| |
| callbacksMethod_[name] = |
| std::make_unique<callback_method_instance<CallbackType>>( |
| std::move(handler)); |
| |
| vtable_.emplace_back(vtable::method(nameItr->c_str(), argType.data(), |
| resultType.data(), method_handler)); |
| return true; |
| } |
| |
| static int get_handler(sd_bus *bus, const char *path, const char *interface, |
| const char *property, sd_bus_message *reply, |
| void *userdata, sd_bus_error *error) |
| { |
| dbus_interface *data = static_cast<dbus_interface *>(userdata); |
| auto func = data->callbacksGet_.find(property); |
| auto mesg = message::message(reply); |
| if (func != data->callbacksGet_.end()) |
| { |
| #ifdef __EXCEPTIONS |
| try |
| { |
| #endif |
| return func->second->call(mesg); |
| #ifdef __EXCEPTIONS |
| } |
| |
| catch (sdbusplus::exception_t &e) |
| { |
| sd_bus_error_set_const(error, e.name(), e.description()); |
| return -EINVAL; |
| } |
| catch (...) |
| { |
| // hit default error below |
| } |
| #endif |
| } |
| sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS, NULL); |
| return -EINVAL; |
| } |
| |
| static int set_handler(sd_bus *bus, const char *path, const char *interface, |
| const char *property, sd_bus_message *value, |
| void *userdata, sd_bus_error *error) |
| { |
| dbus_interface *data = static_cast<dbus_interface *>(userdata); |
| auto func = data->callbacksSet_.find(property); |
| auto mesg = message::message(value); |
| if (func != data->callbacksSet_.end()) |
| { |
| #ifdef __EXCEPTIONS |
| try |
| { |
| #endif |
| int status = func->second->call(mesg); |
| if (status == 1) |
| { |
| data->signal_property(property); |
| return status; |
| } |
| #ifdef __EXCEPTIONS |
| } |
| |
| catch (sdbusplus::exception_t &e) |
| { |
| sd_bus_error_set_const(error, e.name(), e.description()); |
| return -EINVAL; |
| } |
| catch (...) |
| { |
| // hit default error below |
| } |
| #endif |
| } |
| sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS, NULL); |
| return -EINVAL; |
| } |
| |
| static int method_handler(sd_bus_message *m, void *userdata, |
| sd_bus_error *error) |
| { |
| dbus_interface *data = static_cast<dbus_interface *>(userdata); |
| auto mesg = message::message(m); |
| auto func = data->callbacksMethod_.find(mesg.get_member()); |
| if (func != data->callbacksMethod_.end()) |
| { |
| #ifdef __EXCEPTIONS |
| try |
| { |
| #endif |
| int status = func->second->call(mesg); |
| if (status == 1) |
| { |
| return status; |
| } |
| #ifdef __EXCEPTIONS |
| } |
| |
| catch (sdbusplus::exception_t &e) |
| { |
| sd_bus_error_set_const(error, e.name(), e.description()); |
| return -EINVAL; |
| } |
| catch (...) |
| { |
| // hit default error below |
| } |
| #endif |
| } |
| sd_bus_error_set_const(error, SD_BUS_ERROR_INVALID_ARGS, NULL); |
| return -EINVAL; |
| } |
| |
| bool initialize() |
| { |
| // can only register once |
| if (initialized_) |
| { |
| return false; |
| } |
| initialized_ = true; |
| vtable_.emplace_back(vtable::end()); |
| |
| interface_ = std::make_unique<sdbusplus::server::interface::interface>( |
| static_cast<sdbusplus::bus::bus &>(*conn_), path_.c_str(), |
| name_.c_str(), static_cast<const sd_bus_vtable *>(&vtable_[0]), |
| this); |
| conn_->emit_interfaces_added(path_.c_str(), |
| std::vector<std::string>{name_}); |
| for (const std::string &name : propertyNames_) |
| { |
| signal_property(name); |
| } |
| return true; |
| } |
| void signal_property(const std::string &name) |
| { |
| interface_->property_changed(name.c_str()); |
| } |
| |
| std::string get_object_path(void) |
| { |
| return path_; |
| } |
| |
| std::string get_interface_name(void) |
| { |
| return name_; |
| } |
| |
| private: |
| std::shared_ptr<sdbusplus::asio::connection> conn_; |
| std::string path_; |
| std::string name_; |
| std::list<std::string> propertyNames_; |
| std::list<std::string> methodNames_; |
| boost::container::flat_map<std::string, std::unique_ptr<callback>> |
| callbacksGet_; |
| boost::container::flat_map<std::string, std::unique_ptr<callback_set>> |
| callbacksSet_; |
| boost::container::flat_map<std::string, std::unique_ptr<callback>> |
| callbacksMethod_; |
| std::vector<sd_bus_vtable> vtable_; |
| std::unique_ptr<sdbusplus::server::interface::interface> interface_; |
| |
| bool initialized_ = false; |
| }; |
| |
| class object_server |
| { |
| public: |
| object_server(std::shared_ptr<sdbusplus::asio::connection> &conn) : |
| conn_(conn) |
| { |
| auto root = add_interface("/", ""); |
| root->initialize(); |
| add_manager("/"); |
| } |
| |
| std::shared_ptr<dbus_interface> add_interface(const std::string &path, |
| const std::string &name) |
| { |
| |
| auto dbusIface = std::make_shared<dbus_interface>(conn_, path, name); |
| interfaces_.emplace_back(dbusIface); |
| return dbusIface; |
| } |
| |
| void add_manager(const std::string &path) |
| { |
| managers_.emplace_back( |
| std::make_unique<server::manager::manager>(server::manager::manager( |
| static_cast<sdbusplus::bus::bus &>(*conn_), path.c_str()))); |
| } |
| |
| bool remove_interface(std::shared_ptr<dbus_interface> &iface) |
| { |
| auto findIface = |
| std::find(interfaces_.begin(), interfaces_.end(), iface); |
| if (findIface != interfaces_.end()) |
| { |
| interfaces_.erase(findIface); |
| return true; |
| } |
| return false; |
| } |
| |
| private: |
| std::shared_ptr<sdbusplus::asio::connection> conn_; |
| std::vector<std::shared_ptr<dbus_interface>> interfaces_; |
| std::vector<std::unique_ptr<server::manager::manager>> managers_; |
| }; |
| |
| } // namespace asio |
| } // namespace sdbusplus |