summaryrefslogtreecommitdiff
path: root/agent/data
diff options
context:
space:
mode:
Diffstat (limited to 'agent/data')
-rw-r--r--agent/data/enums.hpp57
-rw-r--r--agent/data/scalar.hpp150
-rw-r--r--agent/data/table.hpp348
-rw-r--r--agent/data/table/item.hpp157
4 files changed, 712 insertions, 0 deletions
diff --git a/agent/data/enums.hpp b/agent/data/enums.hpp
new file mode 100644
index 0000000..bd6eb94
--- /dev/null
+++ b/agent/data/enums.hpp
@@ -0,0 +1,57 @@
+/**
+ * @brief DBus enums to base type converter.
+ *
+ * This file is part of sila-snmp project.
+ *
+ * Copyright (c) 2022 SILA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#pragma once
+
+#include <string>
+#include <map>
+
+namespace phosphor
+{
+namespace snmp
+{
+namespace data
+{
+
+template <typename T> struct DBusEnum
+{
+ std::string base;
+ std::map<std::string, T> values;
+ T wrongValue;
+
+ T get(const std::string& str) const
+ {
+ const auto len = base.length();
+ if (0 == str.compare(0, len, base))
+ {
+ const auto& it = values.find(str.substr(len + 1));
+ if (it != values.end())
+ {
+ return it->second;
+ }
+ }
+
+ return wrongValue;
+ }
+};
+
+} // namespace data
+} // namespace snmp
+} // namespace phosphor
diff --git a/agent/data/scalar.hpp b/agent/data/scalar.hpp
new file mode 100644
index 0000000..2fc4a24
--- /dev/null
+++ b/agent/data/scalar.hpp
@@ -0,0 +1,150 @@
+/**
+ * @brief Export DBus property to scalar MIB value
+ *
+ * This file is part of sila-snmp project.
+ *
+ * Copyright (c) 2022 SILA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#pragma once
+
+#include "sdbusplus/helper.hpp"
+#include <memory>
+
+namespace phosphor
+{
+namespace snmp
+{
+namespace data
+{
+template <typename T> class Scalar
+{
+ public:
+ using value_t = std::variant<T>;
+
+ /* Define all of the basic class operations:
+ * Not allowed:
+ * - Default constructor to avoid nullptrs.
+ * - Copy operations due to internal unique_ptr.
+ * Allowed:
+ * - Move operations.
+ * - Destructor.
+ */
+ Scalar() = delete;
+ Scalar(const Scalar&) = delete;
+ Scalar& operator=(const Scalar&) = delete;
+ Scalar(Scalar&&) = default;
+ Scalar& operator=(Scalar&&) = default;
+ ~Scalar() = default;
+
+ /**
+ * @brief Object constructor
+ */
+ Scalar(const std::string& path, const std::string& iface,
+ const std::string& prop, const T& initValue) :
+ _value(initValue),
+ _path(path), _iface(iface), _prop(prop),
+ _onChangedMatch(sdbusplus::helper::helper::getBus(),
+ sdbusplus::bus::match::rules::propertiesChanged(
+ path.c_str(), iface.c_str()),
+ std::bind(&Scalar<T>::onPropertyChanged, this,
+ std::placeholders::_1))
+ {
+ }
+
+ /**
+ * @brief Sent request to DBus object and store new value of property
+ */
+ void update()
+ {
+ try
+ {
+ auto service = sdbusplus::helper::helper::getService(_path, _iface);
+ auto r = sdbusplus::helper::helper::callMethod(
+ service, _path, sdbusplus::helper::PROPERTIES_IFACE, "Get",
+ _iface, _prop);
+
+ value_t var;
+ r.read(var);
+ setValue(var);
+ }
+ catch (const sdbusplus::exception::SdBusError&)
+ {
+ // Corresponding service is not started yet.
+ // When service is started, we'll receive data with
+ // `propertiesChanged` signal.
+ // So, this catch block is just for silencing the exception.
+ }
+ }
+
+ const T& getValue() const
+ {
+ return _value;
+ }
+
+ const std::string& getPath() const
+ {
+ return _path;
+ }
+
+ const std::string& getInterface() const
+ {
+ return _iface;
+ }
+
+ const std::string& getProperty() const
+ {
+ return _prop;
+ }
+
+ protected:
+ /**
+ * @brief DBus signal `PropertiesChanged` handler
+ */
+ void onPropertyChanged(sdbusplus::message::message& m)
+ {
+ std::string iface;
+ std::map<std::string, value_t> data;
+ std::vector<std::string> v;
+
+ m.read(iface, data, v);
+
+ if (data.find(_prop) != data.end())
+ {
+ setValue(data[_prop]);
+ }
+ }
+
+ /**
+ * @brief Setter for actual value
+ */
+ virtual void setValue(value_t& var)
+ {
+ auto newValue = std::get<T>(var);
+ std::swap(_value, newValue);
+ }
+
+ private:
+ T _value;
+ std::string _path;
+ std::string _iface;
+ std::string _prop;
+
+ sdbusplus::bus::match::match _onChangedMatch;
+};
+
+} // namespace data
+} // namespace snmp
+} // namespace phosphor
diff --git a/agent/data/table.hpp b/agent/data/table.hpp
new file mode 100644
index 0000000..ab891c4
--- /dev/null
+++ b/agent/data/table.hpp
@@ -0,0 +1,348 @@
+/**
+ * @brief Export DBus objects tree to MIB table
+ *
+ * This file is part of sila-snmp project.
+ *
+ * Copyright (c) 2022 SILA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#pragma once
+
+#include "sdbusplus/helper.hpp"
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+namespace phosphor
+{
+namespace snmp
+{
+namespace data
+{
+
+/**
+ * @brief MIB Table implementation.
+ */
+template <typename ItemType> class Table
+{
+ public:
+ using interfaces_t = std::vector<std::string>;
+
+ /* Define all of the basic class operations:
+ * Not allowed:
+ * - Default constructor to avoid nullptrs.
+ * - Copy operations due to internal unique_ptr.
+ * Allowed:
+ * - Move operations.
+ * - Destructor.
+ */
+ Table() = delete;
+ Table(const Table&) = delete;
+ Table& operator=(const Table&) = delete;
+ Table(Table&&) = default;
+ Table& operator=(Table&&) = default;
+ ~Table() = default;
+
+ /**
+ * @brief Object constructor
+ *
+ * @param folder - DBus folder where required objects exist.
+ * @param interfaces - List of required DBus properties interfaces
+ */
+ Table(const std::string& folder, const interfaces_t interfaces = {}) :
+ _path(folder), _interfaces(interfaces)
+ {
+ _matches.emplace_back(sdbusplus::helper::helper::getBus(),
+ sdbusplus::bus::match::rules::interfacesAdded(),
+ std::bind(&Table<ItemType>::onInterfacesAdded,
+ this, std::placeholders::_1));
+ _matches.emplace_back(sdbusplus::helper::helper::getBus(),
+ sdbusplus::bus::match::rules::interfacesRemoved(),
+ std::bind(&Table<ItemType>::onInterfacesRemoved,
+ this, std::placeholders::_1));
+ }
+
+ /**
+ * @brief Force update table items
+ */
+ void update()
+ {
+ auto data = sdbusplus::helper::helper::getSubTree(_path, _interfaces);
+
+ // Drop sensors if it not present in answer
+ auto path = _path + "/";
+ for (auto it = _items.begin(); it != _items.end();)
+ {
+ if (data.find(path + (*it)->name) == data.end())
+ {
+ it = dropItem(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ // Update existing and create new items.
+ using fields_map_t = typename ItemType::fields_map_t;
+ for (const auto& pi : data)
+ {
+ for (const auto& bi : pi.second)
+ {
+ auto fields =
+ sdbusplus::helper::helper::callMethodAndRead<fields_map_t>(
+ bi.first, pi.first, sdbusplus::helper::PROPERTIES_IFACE,
+ "GetAll", "");
+ getItem(pi.first).setFields(fields);
+ }
+ }
+ }
+
+ /**
+ * @brief Register MIB handlers
+ *
+ * @param name - Name of table in MIB
+ * @param table_oid - OID of table
+ * @param table_oid_len - Table OID length
+ * @param min_column - Minimum columns number
+ * @param max_column - Maximum columns number
+ */
+ void init_mib(const char* name, const oid* table_oid, size_t table_oid_len,
+ size_t min_column, size_t max_column)
+ {
+ netsnmp_handler_registration* reg = netsnmp_create_handler_registration(
+ name, Table<ItemType>::snmp_handler, table_oid, table_oid_len,
+ HANDLER_CAN_RONLY);
+
+ netsnmp_table_registration_info* table_info =
+ SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
+ netsnmp_table_helper_add_indexes(table_info, ASN_OCTET_STR, 0);
+ table_info->min_column = min_column;
+ table_info->max_column = max_column;
+
+ netsnmp_iterator_info* iinfo =
+ SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info);
+ iinfo->get_first_data_point = Table<ItemType>::get_first_data_point;
+ iinfo->get_next_data_point = Table<ItemType>::get_next_data_point;
+ iinfo->table_reginfo = table_info;
+ iinfo->myvoid = this;
+
+ netsnmp_register_table_iterator(reg, iinfo);
+ }
+
+ protected:
+ using ItemPtr = std::unique_ptr<ItemType>;
+ using Items = std::vector<ItemPtr>;
+
+ /**
+ * @brief DBus signal `InterfacesAdded` handler.
+ */
+ void onInterfacesAdded(sdbusplus::message::message& m)
+ {
+ using Data = std::map<std::string, typename ItemType::fields_map_t>;
+
+ sdbusplus::message::object_path path;
+ Data data;
+ m.read(path, data);
+
+ if (0 == path.str.compare(0, _path.length(), _path))
+ {
+ // Skip unnecessary objects
+ bool isOwned = _interfaces.empty();
+ if (!isOwned)
+ {
+ auto it = std::find_first_of(
+ _interfaces.begin(), _interfaces.end(), data.begin(),
+ data.end(),
+ [](const std::string& iface,
+ const typename Data::value_type& item) {
+ return iface == item.first;
+ });
+ isOwned = (it != _interfaces.end());
+ }
+
+ if (isOwned)
+ {
+ auto& item = getItem(path.str);
+
+ for (const auto& [iface, fields] : data)
+ {
+ item.setFields(fields);
+ }
+ }
+ }
+ }
+
+ /**
+ * @brief DBus signal `InterfacesRemoved` handler.
+ */
+ void onInterfacesRemoved(sdbusplus::message::message& m)
+ {
+ sdbusplus::message::object_path path;
+ std::vector<std::string> data;
+ try
+ {
+ m.read(path, data);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ TRACE_ERROR("data/table: Failed to parse signal data. "
+ "ERROR='%s', REPLY_SIG='%s', PATH='%s', "
+ "IFACE='%s', MEMBER='%s', object path='%s'\n",
+ e.what(), m.get_signature(), m.get_path(),
+ m.get_interface(), m.get_member(), path.str.c_str());
+ }
+
+ if (0 == path.str.compare(0, _path.length(), _path))
+ {
+ bool isOwned = _interfaces.empty();
+ if (!isOwned)
+ {
+ auto it =
+ std::find_first_of(_interfaces.begin(), _interfaces.end(),
+ data.begin(), data.end());
+ isOwned = (it != _interfaces.end());
+ }
+
+ if (isOwned)
+ {
+ dropItem(path.str);
+ }
+ }
+ }
+
+ /**
+ * @brief Create new item if does not exist and return reference.
+ */
+ ItemType& getItem(const std::string& path)
+ {
+ auto name = path.substr(_path.length() + 1); // Skip following '/'
+ auto it = std::lower_bound(_items.begin(), _items.end(), name);
+ if (it != _items.end() && (*it)->name == name)
+ {
+ return *(*it);
+ }
+
+ it = _items.emplace(it, std::make_unique<ItemType>(_path, name));
+ (*it)->onCreate();
+ return *(*it);
+ }
+
+ /**
+ * @brief Drop item specified by iterator.
+ *
+ * @param it - Items iterator
+ *
+ * @return Iterator pointing to the new location of the item that followed
+ * the item erased.
+ */
+ typename Items::iterator dropItem(typename Items::iterator it)
+ {
+ if (it != _items.end())
+ {
+ (*it)->onDestroy();
+ it = _items.erase(it);
+ }
+ return it;
+ }
+
+ /**
+ * @brief Drop item.
+ */
+ void dropItem(const std::string& path)
+ {
+ auto name = path.substr(_path.length() + 1); // Skip following '/'
+ auto it = std::lower_bound(_items.begin(), _items.end(), name);
+ if (it != _items.end() && (*it)->name == name)
+ {
+ dropItem(it);
+ }
+ }
+
+ /**
+ * @brief Iterator hook routines
+ */
+ static netsnmp_variable_list*
+ get_first_data_point(void** loop_ctx, void** data_ctx,
+ netsnmp_variable_list* idx_data,
+ netsnmp_iterator_info* data)
+ {
+ *loop_ctx = reinterpret_cast<void*>(0);
+ return Table<ItemType>::get_next_data_point(loop_ctx, data_ctx,
+ idx_data, data);
+ }
+ static netsnmp_variable_list*
+ get_next_data_point(void** loop_ctx, void** data_ctx,
+ netsnmp_variable_list* idx_data,
+ netsnmp_iterator_info* data)
+ {
+ auto index = reinterpret_cast<size_t>(*loop_ctx);
+ auto& table = *reinterpret_cast<Table<ItemType>*>(data->myvoid);
+
+ if (index < table._items.size())
+ {
+ auto& item = table._items[index];
+
+ snmp_set_var_value(idx_data, item->name.c_str(),
+ item->name.length());
+
+ *data_ctx = item.get();
+ *loop_ctx = reinterpret_cast<void*>(index + 1);
+ return idx_data;
+ }
+
+ return nullptr;
+ }
+
+ /**
+ * @brief handles snmp requests for the table data.
+ */
+ static int snmp_handler(netsnmp_mib_handler* handler,
+ netsnmp_handler_registration* reginfo,
+ netsnmp_agent_request_info* reqinfo,
+ netsnmp_request_info* requests)
+ {
+ switch (reqinfo->mode)
+ {
+ case MODE_GET:
+ for (auto request = requests; request; request = request->next)
+ {
+ auto entry = reinterpret_cast<ItemType*>(
+ netsnmp_extract_iterator_context(request));
+
+ if (!entry)
+ {
+ netsnmp_set_request_error(reqinfo, request,
+ SNMP_NOSUCHINSTANCE);
+ continue;
+ }
+
+ entry->get_snmp_reply(reqinfo, request);
+ }
+ break;
+ }
+
+ return SNMP_ERR_NOERROR;
+ }
+
+ std::string _path;
+ interfaces_t _interfaces;
+ std::vector<sdbusplus::bus::match::match> _matches;
+ Items _items;
+};
+
+} // namespace data
+} // namespace snmp
+} // namespace phosphor
diff --git a/agent/data/table/item.hpp b/agent/data/table/item.hpp
new file mode 100644
index 0000000..7c4c0eb
--- /dev/null
+++ b/agent/data/table/item.hpp
@@ -0,0 +1,157 @@
+/**
+ * @brief MIB tables item definition.
+ *
+ * This file is part of sila-snmp project.
+ *
+ * Copyright (c) 2022 SILA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#pragma once
+
+#include "sdbusplus/helper.hpp"
+
+namespace phosphor
+{
+namespace snmp
+{
+namespace data
+{
+namespace table
+{
+
+/**
+ * @brief MIB Table row implementation.
+ */
+template <typename... T> struct Item
+{
+ /*
+ * The `std::variant` allows to keep duplicates of types,
+ * but `std::get<>()` and `std::holds_alternative<>()` is ill-formed
+ * in this case.
+ *
+ * So we can't use here:
+ * using variant_t = std::variant<T...>;
+ * and we should specify all possible types.
+ */
+ using variant_t = std::variant<int64_t, std::string, bool, uint8_t, double>;
+
+ using values_t = std::tuple<T...>;
+ using fields_map_t = std::map<std::string, variant_t>;
+
+ /* Define all of the basic class operations:
+ * Not allowed:
+ * - Default constructor to avoid nullptrs.
+ * - Copy operations due to internal unique_ptr.
+ * Allowed:
+ * - Move operations.
+ * - Destructor.
+ */
+ Item() = delete;
+ Item(const Item&) = delete;
+ Item& operator=(const Item&) = delete;
+ Item(Item&&) = default;
+ Item& operator=(Item&&) = default;
+ ~Item() = default;
+
+ /**
+ * @brief Object constructor
+ *
+ * @param folder - Base folder for DBus object path
+ * @param name - DBus object path relative by folder
+ * @param args - Default values for each fields
+ */
+ Item(const std::string& folder, const std::string& name, T&&... args) :
+ name(name), data(std::forward<T>(args)...),
+ changedMatch(sdbusplus::helper::helper::getBus(),
+ sdbusplus::bus::match::rules::propertiesChanged(
+ folder + "/" + name),
+ std::bind(&Item<T...>::onPropertiesChanged, this,
+ std::placeholders::_1))
+ {
+ }
+
+ /**
+ * @brief PropertiesChanged signal handler
+ */
+ virtual void onPropertiesChanged(sdbusplus::message::message& m)
+ {
+ std::string iface;
+ fields_map_t data;
+ std::vector<std::string> v;
+ m.read(iface, data, v);
+
+ setFields(data);
+ }
+
+ /**
+ * @brief Store fields values recieved from DBus
+ */
+ virtual void setFields(const fields_map_t& fields) = 0;
+
+ /**
+ * @brief Called after object has been created.
+ */
+ virtual void onCreate()
+ {
+ }
+
+ /**
+ * @brief Called before object will be destroyed.
+ */
+ virtual void onDestroy()
+ {
+ }
+
+ /**
+ * @brief String fields vlaues helper
+ */
+ template <size_t Index>
+ void setField(const fields_map_t& fieldsMap, const char* propertyName)
+ {
+ using FieldType = typename std::tuple_element<Index, values_t>::type;
+ if (fieldsMap.find(propertyName) != fieldsMap.end() &&
+ std::holds_alternative<FieldType>(fieldsMap.at(propertyName)))
+ {
+ std::get<Index>(data) =
+ std::get<FieldType>(fieldsMap.at(propertyName));
+ }
+ }
+
+ /**
+ * @brief Fill snmp reply with fields values
+ */
+ virtual void get_snmp_reply(netsnmp_agent_request_info* reqinfo,
+ netsnmp_request_info* request) const = 0;
+
+ std::string name;
+ values_t data;
+
+ private:
+ sdbusplus::bus::match::match changedMatch;
+};
+
+/**
+ * @brief Used for std::lower_bound throw vector of smartpointers.
+ */
+template <typename ItemType>
+inline bool operator<(const std::unique_ptr<ItemType>& o, const std::string& s)
+{
+ return o->name < s;
+}
+
+} // namespace table
+} // namespace data
+} // namespace snmp
+} // namespace phosphor