diff options
Diffstat (limited to 'sdbusplus')
| -rw-r--r-- | sdbusplus/asio/object_server.hpp | 514 | ||||
| -rw-r--r-- | sdbusplus/message/types.hpp | 5 | ||||
| -rw-r--r-- | sdbusplus/utility/type_traits.hpp | 15 |
3 files changed, 534 insertions, 0 deletions
diff --git a/sdbusplus/asio/object_server.hpp b/sdbusplus/asio/object_server.hpp new file mode 100644 index 0000000..531b858 --- /dev/null +++ b/sdbusplus/asio/object_server.hpp @@ -0,0 +1,514 @@ +#pragma once + +#include <list> +#include <sdbusplus/exception.hpp> +#include <sdbusplus/server.hpp> +#include <sdbusplus/asio/connection.hpp> +#include <sdbusplus/message/types.hpp> +#include <sdbusplus/message/read.hpp> +#include <sdbusplus/utility/tuple_to_array.hpp> +#include <boost/any.hpp> +#include <boost/container/flat_map.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 CallbackType> +class callback_method_instance : public callback +{ + public: + callback_method_instance(CallbackType &&func) : func_(std::move(func)) + { + } + int call(message::message &m) override + { + 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; + }; + + 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); + } +}; + +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()); + } + + // 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> + 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); + + 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 diff --git a/sdbusplus/message/types.hpp b/sdbusplus/message/types.hpp index 5d3be09..af7be35 100644 --- a/sdbusplus/message/types.hpp +++ b/sdbusplus/message/types.hpp @@ -238,6 +238,11 @@ struct type_id<variant<Args...>> : tuple_type_id<SD_BUS_TYPE_VARIANT> { }; +template <> struct type_id<void> +{ + constexpr static auto value = std::make_tuple('\0'); +}; + template <typename T> constexpr auto type_id_single() { static_assert(!std::is_base_of<undefined_type_id, type_id<T>>::value, diff --git a/sdbusplus/utility/type_traits.hpp b/sdbusplus/utility/type_traits.hpp index 3629d09..a88fcfa 100644 --- a/sdbusplus/utility/type_traits.hpp +++ b/sdbusplus/utility/type_traits.hpp @@ -43,6 +43,21 @@ template <typename... Args> struct decay_tuple<std::tuple<Args...>> using type = std::tuple<typename std::decay<Args>::type...>; }; +// Small helper class for stripping off the first + last character of a char +// array +template <std::size_t N, std::size_t... Is> +constexpr std::array<char, N - 2> strip_ends(const std::array<char, N>& s, + std::index_sequence<Is...>) +{ + return {(s[1 + Is])..., static_cast<char>(0)}; +}; + +template <std::size_t N> +constexpr std::array<char, N - 2> strip_ends(const std::array<char, N>& s) +{ + return strip_ends(s, std::make_index_sequence<N - 3>{}); +}; + } // namespace utility } // namespace sdbusplus |

