summaryrefslogtreecommitdiff
path: root/agent/data/table.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'agent/data/table.hpp')
-rw-r--r--agent/data/table.hpp348
1 files changed, 348 insertions, 0 deletions
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