From 0f4556fc2343dc0ade0bb1e0d1fc6f85770d77af Mon Sep 17 00:00:00 2001 From: "Andrey V.Kosteltsev" Date: Fri, 15 Jul 2022 10:36:51 +0300 Subject: First commit: Sila SNMP Sub Agent and configuration manager --- agent/Makefile.am | 18 ++ agent/data/enums.hpp | 57 ++++++ agent/data/scalar.hpp | 150 +++++++++++++++ agent/data/table.hpp | 348 +++++++++++++++++++++++++++++++++++ agent/data/table/item.hpp | 157 ++++++++++++++++ agent/main.cpp | 180 ++++++++++++++++++ agent/sila-snmp-agent.service.in | 15 ++ agent/sila/inventory.cpp | 249 +++++++++++++++++++++++++ agent/sila/inventory.hpp | 32 ++++ agent/sila/powerstate.cpp | 140 ++++++++++++++ agent/sila/powerstate.hpp | 38 ++++ agent/sila/sensors.cpp | 385 +++++++++++++++++++++++++++++++++++++++ agent/sila/sensors.hpp | 33 ++++ agent/sila/sila_oid.hpp | 27 +++ agent/sila/software.cpp | 225 +++++++++++++++++++++++ agent/sila/software.hpp | 32 ++++ agent/snmp.cpp | 140 ++++++++++++++ agent/snmp.hpp | 26 +++ agent/snmp_oid.hpp | 83 +++++++++ agent/snmptrap.hpp | 164 +++++++++++++++++ agent/snmpvars.hpp | 121 ++++++++++++ agent/tracing.hpp | 30 +++ 22 files changed, 2650 insertions(+) create mode 100644 agent/Makefile.am create mode 100644 agent/data/enums.hpp create mode 100644 agent/data/scalar.hpp create mode 100644 agent/data/table.hpp create mode 100644 agent/data/table/item.hpp create mode 100644 agent/main.cpp create mode 100644 agent/sila-snmp-agent.service.in create mode 100644 agent/sila/inventory.cpp create mode 100644 agent/sila/inventory.hpp create mode 100644 agent/sila/powerstate.cpp create mode 100644 agent/sila/powerstate.hpp create mode 100644 agent/sila/sensors.cpp create mode 100644 agent/sila/sensors.hpp create mode 100644 agent/sila/sila_oid.hpp create mode 100644 agent/sila/software.cpp create mode 100644 agent/sila/software.hpp create mode 100644 agent/snmp.cpp create mode 100644 agent/snmp.hpp create mode 100644 agent/snmp_oid.hpp create mode 100644 agent/snmptrap.hpp create mode 100644 agent/snmpvars.hpp create mode 100644 agent/tracing.hpp (limited to 'agent') diff --git a/agent/Makefile.am b/agent/Makefile.am new file mode 100644 index 0000000..851c3a1 --- /dev/null +++ b/agent/Makefile.am @@ -0,0 +1,18 @@ +AM_CPPFLAGS = -iquote $(top_srcdir) + +bin_PROGRAMS = sila-snmp-agent + +sila_snmp_agent_SOURCES = \ + snmp.cpp \ + sila/powerstate.cpp \ + sila/sensors.cpp \ + sila/software.cpp \ + sila/inventory.cpp \ + main.cpp + +sila_snmp_agent_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(SDEVENTPLUS_CFLAGS) $(NETSNMP_CFLAGS) +sila_snmp_agent_LDADD = $(SDBUSPLUS_LIBS) $(SDEVENTPLUS_LIBS) $(NETSNMP_AGENT_LIBS) + +if HAVE_SYSTEMD +systemdsystemunit_DATA = sila-snmp-agent.service +endif 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 +#include + +namespace phosphor +{ +namespace snmp +{ +namespace data +{ + +template struct DBusEnum +{ + std::string base; + std::map 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 + +namespace phosphor +{ +namespace snmp +{ +namespace data +{ +template class Scalar +{ + public: + using value_t = std::variant; + + /* 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::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 data; + std::vector 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(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 +#include +#include + +namespace phosphor +{ +namespace snmp +{ +namespace data +{ + +/** + * @brief MIB Table implementation. + */ +template class Table +{ + public: + using interfaces_t = std::vector; + + /* 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::onInterfacesAdded, + this, std::placeholders::_1)); + _matches.emplace_back(sdbusplus::helper::helper::getBus(), + sdbusplus::bus::match::rules::interfacesRemoved(), + std::bind(&Table::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( + 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::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::get_first_data_point; + iinfo->get_next_data_point = Table::get_next_data_point; + iinfo->table_reginfo = table_info; + iinfo->myvoid = this; + + netsnmp_register_table_iterator(reg, iinfo); + } + + protected: + using ItemPtr = std::unique_ptr; + using Items = std::vector; + + /** + * @brief DBus signal `InterfacesAdded` handler. + */ + void onInterfacesAdded(sdbusplus::message::message& m) + { + using Data = std::map; + + 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 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(_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(0); + return Table::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(*loop_ctx); + auto& table = *reinterpret_cast*>(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(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( + 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 _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 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; + * and we should specify all possible types. + */ + using variant_t = std::variant; + + using values_t = std::tuple; + using fields_map_t = std::map; + + /* 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(args)...), + changedMatch(sdbusplus::helper::helper::getBus(), + sdbusplus::bus::match::rules::propertiesChanged( + folder + "/" + name), + std::bind(&Item::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 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 + void setField(const fields_map_t& fieldsMap, const char* propertyName) + { + using FieldType = typename std::tuple_element::type; + if (fieldsMap.find(propertyName) != fieldsMap.end() && + std::holds_alternative(fieldsMap.at(propertyName))) + { + std::get(data) = + std::get(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 +inline bool operator<(const std::unique_ptr& o, const std::string& s) +{ + return o->name < s; +} + +} // namespace table +} // namespace data +} // namespace snmp +} // namespace phosphor diff --git a/agent/main.cpp b/agent/main.cpp new file mode 100644 index 0000000..033696e --- /dev/null +++ b/agent/main.cpp @@ -0,0 +1,180 @@ +/** + * @brief SNMP Agent entry point + * + * 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. + * + */ + +#include "config.h" +#include "tracing.hpp" +#include "sdbusplus/helper.hpp" +#include "snmp.hpp" + +#include +#include + +#include +#include +#include + +#include + +#include "sila/powerstate.hpp" +#include "sila/sensors.hpp" +#include "sila/software.hpp" +#include "sila/inventory.hpp" + +void print_usage() +{ + fprintf(stderr, "Usage: %s [OPTIONS]\n\n", PACKAGE_NAME); + fprintf(stderr, " Version: %s\n\nOPTIONS:\n", PACKAGE_VERSION); + fprintf(stderr, " -h,--help\t\tdisplay this help message\n"); + fprintf(stderr, " -d\t\t\tdump sent and received SNMP packets\n"); + fprintf( + stderr, + " -D[TOKEN[,...]]\tturn on debugging output for the given TOKEN(s)\n" + "\t\t\t (try ALL for extremely verbose output)\n"); + fprintf(stderr, + " -L \t\ttoggle options controlling where to log to\n"); + snmp_log_options_usage("\t\t\t ", stderr); + fflush(stderr); +} + +// parse_args error codes +enum +{ + EC_SHOW_USAGE = 1, + EC_ERROR = -1, + EC_SUCCESS = 0, +}; + +int parse_args(int argc, char** argv) +{ + constexpr auto Opts = "dD:L:h"; + + optind = 1; + int arg; + int rc = EC_SUCCESS; + + while (EC_SUCCESS == rc && (arg = getopt(argc, argv, Opts)) != EOF) + { + DEBUGMSGTL(("parse_args", "handling (#%d): %c\n", optind, arg)); + switch (arg) + { + case 'D': + debug_register_tokens(optarg); + snmp_set_do_debugging(1); + break; + + case 'd': + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_DUMP_PACKET, 1); + break; + + case 'L': + rc = snmp_log_options(optarg, argc, argv); + break; + + case 'h': + rc = EC_SHOW_USAGE; + break; + + case '?': + default: + rc = EC_ERROR; + break; + } + } + DEBUGMSGTL(("parse_args", "finished: %d/%d\n", optind, argc)); + return rc; +} + +static void clean_exit(sdeventplus::source::Signal& source, + const struct signalfd_siginfo*) +{ + TRACE_INFO("Signal %d received, terminating...\n", source.get_signal()); + source.get_event().exit(EXIT_SUCCESS); +} + +int main(int argc, char* argv[]) +{ + int rc = parse_args(argc, argv); + if (rc < EC_SUCCESS) + { + return EXIT_FAILURE; + } + else if (rc > EC_SUCCESS) + { + print_usage(); + return EXIT_SUCCESS; + } + + auto evt = sdeventplus::Event::get_default(); + sdbusplus::helper::helper::getBus().attach_event(evt.get(), + SD_EVENT_PRIORITY_NORMAL); + + sigset_t ss; + if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 || + sigaddset(&ss, SIGINT) < 0) + { + TRACE_ERROR("Failed to setup signal hanlders.\n"); + return EXIT_FAILURE; + } + + /* Block SIGTERM first, so than the event loop can handle it */ + if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0) + { + TRACE_ERROR("Failed to block SIGTERM.\n"); + return EXIT_FAILURE; + } + + sdeventplus::source::Signal sigterm(evt, SIGTERM, clean_exit); + sdeventplus::source::Signal sigint(evt, SIGINT, clean_exit); + + snmpagent_init(evt); + + // Initialize DBus and MIB objects + + sila::host::power::state::init(); + sila::sensors::init(); + sila::software::init(); + sila::inventory::init(); + + // main loop + + TRACE_INFO("%s is up and running.\n", PACKAGE_STRING); + + rc = evt.loop(); + + TRACE_INFO("%s shuting down.\n", PACKAGE_STRING); + + // Release DBus and MIB objects resources + + sila::inventory::destroy(); + sila::software::destroy(); + sila::sensors::destroy(); + sila::host::power::state::destroy(); + + snmpagent_destroy(); + + if (rc < 0) + { + TRACE_ERROR("Event loop returned error %d, %s\n", rc, strerror(-rc)); + return -rc; + } + return EXIT_SUCCESS; +} diff --git a/agent/sila-snmp-agent.service.in b/agent/sila-snmp-agent.service.in new file mode 100644 index 0000000..9658558 --- /dev/null +++ b/agent/sila-snmp-agent.service.in @@ -0,0 +1,15 @@ +[Unit] +Description=Sila SNMP Sub Agent +PartOf=snmpd.service +After=snmpd.service +After=obmc-mapper.target + +[Service] +Restart=always +Environment=OPTIONS="-Ls0-6d" +EnvironmentFile=-@sysconfdir@/default/sila-snmp-agent +ExecStart=@bindir@/sila-snmp-agent $OPTIONS +SyslogIdentifier=sila-snmp + +[Install] +WantedBy=snmpd.service diff --git a/agent/sila/inventory.cpp b/agent/sila/inventory.cpp new file mode 100644 index 0000000..233b8f8 --- /dev/null +++ b/agent/sila/inventory.cpp @@ -0,0 +1,249 @@ +/** + * @brief SILA inventory table implementation. + * + * 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. + * + */ + +#include "tracing.hpp" +#include "data/table.hpp" +#include "data/table/item.hpp" +#include "sila/sila_oid.hpp" +#include "snmptrap.hpp" + +namespace sila +{ +namespace inventory +{ +using OID = phosphor::snmp::agent::OID; +static const OID NOTIFY_OID = SILA_OID(0, 7); + +struct InventoryItem : public phosphor::snmp::data::table::Item< + std::string, std::string, std::string, std::string, + std::string, std::string, std::string, bool, bool> +{ + // Indexes of fields in tuble + enum Fields + { + FIELD_INVENTORY_PRETTY_NAME = 0, + FIELD_INVENTORY_MANUFACTURER, + FIELD_INVENTORY_BUILD_DATE, + FIELD_INVENTORY_MODEL, + FIELD_INVENTORY_PART_NUMBER, + FIELD_INVENTORY_SERIAL_NUMBER, + FIELD_INVENTORY_VERSION, + FIELD_INVENTORY_PRESENT, + FIELD_INVENTORY_FUNCTIONAL, + }; + + enum Columns + { + COLUMN_SILAINVENTORY_PATH = 1, + COLUMN_SILAINVENTORY_NAME, + COLUMN_SILAINVENTORY_MANUFACTURER, + COLUMN_SILAINVENTORY_BUILD_DATE, + COLUMN_SILAINVENTORY_MODEL, + COLUMN_SILAINVENTORY_PART_NUMBER, + COLUMN_SILAINVENTORY_SERIAL_NUMBER, + COLUMN_SILAINVENTORY_VERSION, + COLUMN_SILAINVENTORY_PRESENT, + COLUMN_SILAINVENTORY_FUNCTIONAL, + }; + + InventoryItem(const std::string& folder, const std::string& name) : + phosphor::snmp::data::table::Item( + folder, name, + "", // Pretty Name + "", // Manufacturer + "", // Build date + "", // Model + "", // Part number + "", // Serial number + "", // Version + false, // Present + false) // Functional + { + phosphor::snmp::agent::make_oid( + _presentOid, ".1.3.6.1.4.1.49769.4.1.%lu.\"%s\"", + COLUMN_SILAINVENTORY_PRESENT, name.c_str()); + phosphor::snmp::agent::make_oid( + _functionalOid, ".1.3.6.1.4.1.49769.4.1.%lu.\"%s\"", + COLUMN_SILAINVENTORY_FUNCTIONAL, name.c_str()); + } + + void setFields(const fields_map_t& fields) override + { + bool isPresent = std::get(data); + bool isFunctional = std::get(data); + + setField(fields, "PrettyName"); + setField(fields, "Manufacturer"); + setField(fields, "BuildDate"); + setField(fields, "Model"); + setField(fields, "PartNumber"); + setField(fields, "SerialNumber"); + setField(fields, "Version"); + setField(fields, "Present"); + setField(fields, "Functional"); + + if (isPresent != std::get(data) || + isFunctional != std::get(data)) + { + DEBUGMSGTL(("sila:inventory", + "Inventory item '%s' at '%s': " + "present %d -> %d, function %d -> %d\n", + std::get(data).c_str(), + name.c_str(), isPresent, + std::get(data), isFunctional, + std::get(data))); + + phosphor::snmp::agent::Trap trap(NOTIFY_OID); + trap.add_field(_presentOid, + std::get(data)); + trap.add_field(_functionalOid, + std::get(data)); + trap.send(); + } + } + + void get_snmp_reply(netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* request) const override + { + using namespace phosphor::snmp::agent; + + netsnmp_table_request_info* tinfo = netsnmp_extract_table_info(request); + + switch (tinfo->colnum) + { + case COLUMN_SILAINVENTORY_PATH: + VariableList::set(request->requestvb, name); + break; + + case COLUMN_SILAINVENTORY_NAME: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILAINVENTORY_MANUFACTURER: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILAINVENTORY_BUILD_DATE: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILAINVENTORY_MODEL: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILAINVENTORY_PART_NUMBER: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILAINVENTORY_SERIAL_NUMBER: + VariableList::set( + request->requestvb, + std::get(data)); + break; + + case COLUMN_SILAINVENTORY_VERSION: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILAINVENTORY_PRESENT: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILAINVENTORY_FUNCTIONAL: + VariableList::set(request->requestvb, + std::get(data)); + break; + + default: + netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT); + break; + } + } + + void onCreate() override + { + DEBUGMSGTL( + ("sila:inventory", "Inventory item '%s' added.\n", name.c_str())); + } + + void onDestroy() override + { + DEBUGMSGTL(("sila:inventory", "Inventory item '%s' removed.\n", + name.c_str())); + if (std::get(data) || + std::get(data)) + { + phosphor::snmp::agent::Trap trap(NOTIFY_OID); + trap.add_field(_presentOid, false); + trap.add_field(_functionalOid, false); + trap.send(); + } + } + + OID _presentOid; + OID _functionalOid; +}; + +static phosphor::snmp::agent::OID inventoryTableOid = SILA_OID(4); + +static phosphor::snmp::data::Table + inventoryTable("/xyz/openbmc_project/inventory", + { + "xyz.openbmc_project.Inventory.Item", + "xyz.openbmc_project.Inventory.Decorator.Asset", + "xyz.openbmc_project.Inventory.Decorator.Revision", + "xyz.openbmc_project.State.Decorator.OperationalStatus", + }); + +/** + * @brief Initialize inventory table + */ +void init() +{ + DEBUGMSGTL(("sila:init", "Initialize silaInventoryTable\n")); + + inventoryTable.update(); + inventoryTable.init_mib("silaInventoryTable", inventoryTableOid.data(), + inventoryTableOid.size(), + InventoryItem::COLUMN_SILAINVENTORY_PATH, + InventoryItem::COLUMN_SILAINVENTORY_FUNCTIONAL); +} + +/** + * @brief Deinitialize inventory table + */ +void destroy() +{ + DEBUGMSGTL(("sila:shutdown", "Deinitialize silaInventoryTable\n")); + unregister_mib(inventoryTableOid.data(), inventoryTableOid.size()); +} + +} // namespace inventory +} // namespace sila diff --git a/agent/sila/inventory.hpp b/agent/sila/inventory.hpp new file mode 100644 index 0000000..ad8a3cc --- /dev/null +++ b/agent/sila/inventory.hpp @@ -0,0 +1,32 @@ +/** + * @brief SILA inventory table implementation. + * + * 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 + +namespace sila +{ +namespace inventory +{ + +void init(); +void destroy(); + +} // namespace inventory +} // namespace sila diff --git a/agent/sila/powerstate.cpp b/agent/sila/powerstate.cpp new file mode 100644 index 0000000..d82ad23 --- /dev/null +++ b/agent/sila/powerstate.cpp @@ -0,0 +1,140 @@ +/** + * @brief SILA host power state implementation. + * + * 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. + * + */ +#include "data/scalar.hpp" +#include "sila/sila_oid.hpp" +#include "tracing.hpp" + +#include +#include +#include + +#include "snmptrap.hpp" + +namespace sila +{ +namespace host +{ +namespace power +{ +namespace state +{ +using OID = phosphor::snmp::agent::OID; + +static const OID state_oid = SILA_OID(1, 1); +static const OID notify_oid = SILA_OID(0, 1); + +// Values specified in the MIB file. +constexpr int UNKNOWN = -1; +constexpr int OFF = 0; +constexpr int ON = 1; + +struct State : public phosphor::snmp::data::Scalar +{ + static constexpr auto IFACE = "xyz.openbmc_project.State.Host"; + static constexpr auto PATH = "/xyz/openbmc_project/state/host0"; + + State() : + phosphor::snmp::data::Scalar(PATH, IFACE, + "CurrentHostState", "") + { + } + + void setValue(value_t& var) + { + auto prev = getValue(); + phosphor::snmp::data::Scalar::setValue(var); + + auto curr = getValue(); + if (prev != curr) + { + DEBUGMSGTL(("sila:powerstate", + "Host power state changed: '%s' -> '%s'\n", + prev.c_str(), curr.c_str())); + + phosphor::snmp::agent::Trap trap(notify_oid); + trap.add_field(state_oid, toSNMPValue()); + trap.send(); + } + } + + int toSNMPValue() const + { + auto value = getValue(); + if (value == "xyz.openbmc_project.State.Host.HostState.Running") + { + return ON; + } + if (value == "xyz.openbmc_project.State.Host.HostState.Off") + { + return OFF; + } + + return UNKNOWN; + } +}; + +static State state; + +/** @brief Handler for snmp requests */ +static int State_snmp_handler(netsnmp_mib_handler* /*handler*/, + netsnmp_handler_registration* /*reginfo*/, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests) +{ + DEBUGMSGTL(("sila:handle", + "Processing request (%d) for silaHostPowerState\n", + reqinfo->mode)); + + switch (reqinfo->mode) + { + case MODE_GET: + for (netsnmp_request_info* request = requests; request; + request = request->next) + { + phosphor::snmp::agent::VariableList::set(request->requestvb, + state.toSNMPValue()); + } + break; + } + + return SNMP_ERR_NOERROR; +} + +void init() +{ + DEBUGMSGTL(("sila:init", "Initialize silaHostPowerState\n")); + + state.update(); + + netsnmp_register_read_only_instance(netsnmp_create_handler_registration( + "silaHostPowerState", State_snmp_handler, state_oid.data(), + state_oid.size(), HANDLER_CAN_RONLY)); +} +void destroy() +{ + DEBUGMSGTL(("sila:shutdown", "destroy silaHostPowerState\n")); + unregister_mib(const_cast(state_oid.data()), state_oid.size()); +} + +} // namespace state +} // namespace power +} // namespace host +} // namespace sila diff --git a/agent/sila/powerstate.hpp b/agent/sila/powerstate.hpp new file mode 100644 index 0000000..937786d --- /dev/null +++ b/agent/sila/powerstate.hpp @@ -0,0 +1,38 @@ +/** + * @brief SILA host power state implementation. + * + * 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 + +namespace sila +{ +namespace host +{ +namespace power +{ +namespace state +{ + +void init(); +void destroy(); + +} // namespace state +} // namespace power +} // namespace host +} // namespace sila diff --git a/agent/sila/sensors.cpp b/agent/sila/sensors.cpp new file mode 100644 index 0000000..5cb8392 --- /dev/null +++ b/agent/sila/sensors.cpp @@ -0,0 +1,385 @@ +/** + * @brief SILA sensors tables implementation. + * + * 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. + * + */ + +#include "tracing.hpp" +#include "data/table.hpp" +#include "data/table/item.hpp" +#include "sila/sila_oid.hpp" +#include "snmptrap.hpp" + +#include + +namespace sila +{ +namespace sensors +{ +/** + * @brief Sensor implementation. + */ +struct Sensor + : public phosphor::snmp::data::table::Item +{ + /** + * @brief State codes like in MIB file. + */ + enum state_t + { + E_DISABLED = 0, + E_NORMAL, + E_WARNING_LOW, + E_WARNING_HIGH, + E_CRITICAL_LOW, + E_CRITICAL_HIGH, + }; + + // SNMP table columns + enum Columns + { + COLUMN_SILASENSOR_NAME = 1, + COLUMN_SILASENSOR_VALUE, + COLUMN_SILASENSOR_WARNLOW, + COLUMN_SILASENSOR_WARNHIGH, + COLUMN_SILASENSOR_CRITLOW, + COLUMN_SILASENSOR_CRITHIGH, + COLUMN_SILASENSOR_STATE, + }; + + // Indexes of fields in tuple + enum Fields + { + FIELD_SENSOR_VALUE = 0, + FIELD_SENSOR_WARNLOW, + FIELD_SENSOR_WARNLOW_ALARM, + FIELD_SENSOR_WARNHI, + FIELD_SENSOR_WARNHI_ALARM, + FIELD_SENSOR_CRITLOW, + FIELD_SENSOR_CRITLOW_ALARM, + FIELD_SENSOR_CRITHI, + FIELD_SENSOR_CRITHI_ALARM, + }; + + // Sensor types (first letter in sensors folder name) + enum Types + { + ST_TEMPERATURE = 't', // temperature + ST_VOLTAGE = 'v', // voltage + ST_TACHOMETER = 'f', // fan_tach + ST_CURRENT = 'c', // current + ST_POWER = 'p', // power + }; + + /** + * @brief Object contructor. + */ + Sensor(const std::string& folder, const std::string& name) : + phosphor::snmp::data::table::Item( + folder, name, + .0, // Value + .0, false, // WarningLow + .0, false, // WarningHigh + .0, false, // CriticalLow + .0, false) // CriticalHigh + { + auto n = folder.rfind('/'); + if (n != std::string::npos) + { + // Prepare for send traps + switch (folder[n + 1]) + { + case ST_TEMPERATURE: + _notifyOid.assign(SILA_OID(0, 2)); + break; + + case ST_VOLTAGE: + _notifyOid.assign(SILA_OID(0, 3)); + break; + + case ST_TACHOMETER: + _notifyOid.assign(SILA_OID(0, 4)); + break; + + case ST_CURRENT: + _notifyOid.assign(SILA_OID(0, 5)); + break; + + case ST_POWER: + _notifyOid.assign(SILA_OID(0, 6)); + break; + } + + // Correct scale power + // Required for TEXTUAL-CONVENTION in SILA-MIB.txt + switch (folder[n + 1]) + { + case ST_TACHOMETER: + _power = 0; + break; + } + } + + if (!_notifyOid.empty()) + { + phosphor::snmp::agent::make_oid( + _stateOid, ".1.3.6.1.4.1.49769.1.%lu.1.7.\"%s\"", + _notifyOid.back(), name.c_str()); + } + } + + /** + * @brief Update fields with new values recieved from DBus. + */ + void setFields(const fields_map_t& fields) override + { + auto prevValue = getValue(); + auto prevState = getState(); + + setField(fields, "Value"); + setField(fields, "WarningLow"); + setField(fields, "WarningAlarmLow"); + setField(fields, "WarningHigh"); + setField(fields, "WarningAlarmHigh"); + setField(fields, "CriticalLow"); + setField(fields, "CriticalAlarmLow"); + setField(fields, "CriticalHigh"); + setField(fields, "CriticalAlarmHigh"); + + auto lastState = getState(); + + if (prevValue != getValue() || + prevState != lastState) + { + DEBUGMSGTL(("sila:sensors", + "Sensor '%s' changed: %d -> %d, state: %d -> %d\n", + name.c_str(), prevValue, getValue(), + prevState, lastState)); + } + + if (prevState != lastState) + { + send_notify(lastState); + } + } + + /** + * @brief Called when sensor added to table. + */ + void onCreate() override + { + DEBUGMSGTL(("sila:sensors", "Sensor '%s' added, state=%d\n", + name.c_str(), getState())); + send_notify(E_NORMAL); + } + + /** + * @brief Called when sensor removed from table. + */ + void onDestroy() override + { + DEBUGMSGTL(("sila:sensors", "Sensor '%s' removed, state=%d\n", + name.c_str(), getState())); + send_notify(E_DISABLED); + } + + /** + * @brief Send snmptrap about changed state. + * + * @param state - state value for sent with trap. + */ + void send_notify(state_t state) + { + if (!_notifyOid.empty() && !_stateOid.empty()) + { + phosphor::snmp::agent::Trap trap(_notifyOid); + trap.add_field(_stateOid.data(), _stateOid.size(), state); + trap.send(); + } + else + { + TRACE_ERROR("Notify is unsupported for sensor '%s'\n", + name.c_str()); + } + } + + /** + * @brief Get current state. + */ + state_t getState() const + { + if (std::get(data)) + { + return E_CRITICAL_HIGH; + } + else if (std::get(data)) + { + return E_WARNING_HIGH; + } + else if (std::get(data)) + { + return E_WARNING_LOW; + } + else if (std::get(data)) + { + return E_CRITICAL_LOW; + } + + return E_NORMAL; + } + + /** + * @brief snmp request handler. + */ + void get_snmp_reply(netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* request) const override + { + using namespace phosphor::snmp::agent; + + netsnmp_table_request_info* tinfo = netsnmp_extract_table_info(request); + + switch (tinfo->colnum) + { + case COLUMN_SILASENSOR_NAME: + VariableList::set(request->requestvb, name); + break; + + case COLUMN_SILASENSOR_VALUE: + VariableList::set(request->requestvb, + getValue()); + break; + + case COLUMN_SILASENSOR_WARNLOW: + VariableList::set(request->requestvb, + getValue()); + break; + + case COLUMN_SILASENSOR_WARNHIGH: + VariableList::set(request->requestvb, + getValue()); + break; + + case COLUMN_SILASENSOR_CRITLOW: + VariableList::set(request->requestvb, + getValue()); + break; + + case COLUMN_SILASENSOR_CRITHIGH: + VariableList::set(request->requestvb, + getValue()); + break; + + case COLUMN_SILASENSOR_STATE: + VariableList::set(request->requestvb, getState()); + break; + + default: + netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT); + break; + } + } + + /** + * @brief Scale and round sensors value. + */ + template int getValue() const + { + return static_cast( + std::round(std::get(data) * powf(10.f, _power))); + } + + std::vector _notifyOid; + std::vector _stateOid; + int _power = 3; +}; + +struct SensorsTable : public phosphor::snmp::data::Table +{ + using OID = std::vector; + + SensorsTable(const std::string& folder, const std::string& tableName, + const OID& tableOID) : + phosphor::snmp::data::Table( + "/xyz/openbmc_project/sensors/" + folder, + { + "xyz.openbmc_project.Sensor.Value", + "xyz.openbmc_project.Sensor.Threshold.Warning", + "xyz.openbmc_project.Sensor.Threshold.Critical", + "xyz.openbmc_project.Sensor.Threshold.Fatal", + }), + tableName(tableName), tableOID(tableOID) + + { + } + + std::string tableName; + OID tableOID; +}; + +static std::array sensors = { + SensorsTable{"temperature", "silaTempSensorsTable", SILA_OID(1, 2)}, + SensorsTable{"voltage", "silaVoltSensorsTable", SILA_OID(1, 3)}, + SensorsTable{"fan_tach", "silaTachSensorsTable", SILA_OID(1, 4)}, + SensorsTable{"current", "silaCurrSensorsTable", SILA_OID(1, 5)}, + SensorsTable{"power", "silaPowerSensorsTable", SILA_OID(1, 6)}, +}; + +/** + * @brief Update all sensors. + */ +void update() +{ + for (auto& s : sensors) + { + s.update(); + } +} + +/** + * @brief Initialize sensors. + */ +void init() +{ + DEBUGMSGTL(("sila:init", "Initialize silaSensors\n")); + + for (auto& s : sensors) + { + s.init_mib(s.tableName.c_str(), s.tableOID.data(), s.tableOID.size(), + Sensor::COLUMN_SILASENSOR_NAME, + Sensor::COLUMN_SILASENSOR_STATE); + s.update(); + } +} + +/** + * @brief Deinitialize sensors. + */ +void destroy() +{ + DEBUGMSGTL(("sila:shutdown", "Destroy silaSensors\n")); + + for (auto& s : sensors) + { + unregister_mib(s.tableOID.data(), s.tableOID.size()); + } +} + +} // namespace sensors +} // namespace sila diff --git a/agent/sila/sensors.hpp b/agent/sila/sensors.hpp new file mode 100644 index 0000000..6ce123c --- /dev/null +++ b/agent/sila/sensors.hpp @@ -0,0 +1,33 @@ +/** + * @brief SILA sensors tables implementation. + * + * 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 + +namespace sila +{ +namespace sensors +{ + +void init(); +void update(); +void destroy(); + +} // namespace sensors +} // namespace sila diff --git a/agent/sila/sila_oid.hpp b/agent/sila/sila_oid.hpp new file mode 100644 index 0000000..033a7e7 --- /dev/null +++ b/agent/sila/sila_oid.hpp @@ -0,0 +1,27 @@ +/** + * @brief SILA OID helper + * + * This file is part of yadro-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 + +#define SILA_OID(args...) \ + { \ + 1, 3, 6, 1, 4, 1, 49769, ##args \ + } diff --git a/agent/sila/software.cpp b/agent/sila/software.cpp new file mode 100644 index 0000000..bed1130 --- /dev/null +++ b/agent/sila/software.cpp @@ -0,0 +1,225 @@ +/** + * @brief SILA software table implementation. + * + * 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. + * + */ + +#include "tracing.hpp" +#include "data/table.hpp" +#include "data/table/item.hpp" +#include "data/enums.hpp" +#include "sila/sila_oid.hpp" +#include "snmpvars.hpp" + +#define INVALID_ENUM_VALUE 0xFF + +namespace sila +{ +namespace software +{ + +/** + * @brief DBus enum to byte converters. + */ +const phosphor::snmp::data::DBusEnum + purpose = {"xyz.openbmc_project.Software.Version.VersionPurpose", + { + {"Unknown", 0}, + {"Other", 1}, + {"System", 2}, + {"BMC", 3}, + {"Host", 4}, + }, + INVALID_ENUM_VALUE}, + + activation = {"xyz.openbmc_project.Software.Activation.Activations", + { + {"NotReady", 0}, + {"Invalid", 1}, + {"Ready", 2}, + {"Activating", 3}, + {"Active", 4}, + {"Failed", 5}, + }, + INVALID_ENUM_VALUE}; + +/** + * @brief Software item implementation. + */ +struct Software : public phosphor::snmp::data::table::Item +{ + // Indexes of fields in tuple + enum Fields + { + FIELD_SOFTWARE_VERSION = 0, + FIELD_SOFTWARE_PURPOSE, + FIELD_SOFTWARE_ACTIVATION, + FIELD_SOFTWARE_PRIORITY, + }; + + /** + * @brief Object constructor. + */ + Software(const std::string& folder, const std::string& name) : + phosphor::snmp::data::table::Item( + folder, name, + "", // Version + INVALID_ENUM_VALUE, // Purpose + INVALID_ENUM_VALUE, // Activation + INVALID_ENUM_VALUE) // Priority + { + } + + /** + * @brief Set field value with DBus enum converter. + * + * @tparam Idx - Index of field + * @param fields - DBus fields map + * @param field - Name of field in DBus + * @param enumcvt - Enum converter + */ + template + void setFieldEnum(const fields_map_t& fields, const char* field, + const phosphor::snmp::data::DBusEnum& enumcvt) + { + if (fields.find(field) != fields.end() && + std::holds_alternative(fields.at(field))) + { + std::get(data) = + enumcvt.get(std::get(fields.at(field))); + } + } + + /** + * @brief Update fields with new values recieved from DBus. + */ + void setFields(const fields_map_t& fields) override + { + uint8_t prevActivation = std::get(data), + prevPriority = std::get(data); + + setField(fields, "Version"); + setFieldEnum(fields, "Purpose", purpose); + setFieldEnum(fields, "Activation", + activation); + setField(fields, "Priority"); + + if (prevActivation != std::get(data) || + prevPriority != std::get(data)) + { + DEBUGMSGTL(("sila:software", + "Software '%s' version='%s', purpose=%d changed: " + "activation %d -> %d, priority %d -> %d\n", + name.c_str(), + std::get(data).c_str(), + std::get(data), prevActivation, + std::get(data), prevPriority, + std::get(data))); + } + } + + enum Columns + { + COLUMN_SILASOFTWARE_HASH = 1, + COLUMN_SILASOFTWARE_VERSION = 2, + COLUMN_SILASOFTWARE_PURPOSE = 3, + COLUMN_SILASOFTWARE_ACTIVATION = 4, + COLUMN_SILASOFTWARE_PRIORITY = 5, + }; + + /** + * @brief snmp request handler. + */ + void get_snmp_reply(netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* request) const override + { + using namespace phosphor::snmp::agent; + + netsnmp_table_request_info* tinfo = netsnmp_extract_table_info(request); + + switch (tinfo->colnum) + { + case COLUMN_SILASOFTWARE_HASH: + VariableList::set(request->requestvb, name); + break; + + case COLUMN_SILASOFTWARE_VERSION: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILASOFTWARE_PURPOSE: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILASOFTWARE_ACTIVATION: + VariableList::set(request->requestvb, + std::get(data)); + break; + + case COLUMN_SILASOFTWARE_PRIORITY: + VariableList::set(request->requestvb, + std::get(data)); + break; + + default: + netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT); + break; + } + } +}; + +constexpr oid softwareOid[] = SILA_OID(5); +constexpr auto SOFTWARE_FOLDER = "/xyz/openbmc_project/software"; + +static phosphor::snmp::data::Table + softwareTable("/xyz/openbmc_project/software", + { + "xyz.openbmc_project.Software.Version", + "xyz.openbmc_project.Software.Activation", + "xyz.openbmc_project.Software.RedundancyPriority", + }); + +/** + * @brief Initialize software table + */ +void init() +{ + DEBUGMSGTL(("sila:init", "Initialize silaSoftwareTable\n")); + + softwareTable.update(); + softwareTable.init_mib("silaSoftwareTable", softwareOid, + OID_LENGTH(softwareOid), + Software::COLUMN_SILASOFTWARE_HASH, + Software::COLUMN_SILASOFTWARE_PRIORITY); +} + +/** + * @brief Deinitialize software table + */ +void destroy() +{ + DEBUGMSGTL(("sila:shutdown", "Deinitialize silaSoftwareTable\n")); + unregister_mib(const_cast(softwareOid), OID_LENGTH(softwareOid)); +} + +} // namespace software +} // namespace sila diff --git a/agent/sila/software.hpp b/agent/sila/software.hpp new file mode 100644 index 0000000..3cd1d87 --- /dev/null +++ b/agent/sila/software.hpp @@ -0,0 +1,32 @@ +/** + * @brief SILA software tables implementation. + * + * 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 + +namespace sila +{ +namespace software +{ + +void init(); +void destroy(); + +} // namespace software +} // namespace sila diff --git a/agent/snmp.cpp b/agent/snmp.cpp new file mode 100644 index 0000000..b6a2881 --- /dev/null +++ b/agent/snmp.cpp @@ -0,0 +1,140 @@ +/** + * @brief SNMP Agent implementation. + * + * 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. + * + */ +#include "config.h" +#include "tracing.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +constexpr auto clockId = sdeventplus::ClockId::Monotonic; +using Clock = sdeventplus::Clock; +using Time = sdeventplus::source::Time; + +// list of snmp file descriptors attached to sd_event loop. +static std::map snmp_fds; + +/** @brief Called when snmp file descriptors have data for reading. */ +static void sdevent_snmp_read(sdeventplus::source::IO& /*source*/, int fd, + uint32_t /*revents*/) +{ + DEBUGMSGTL(("snmpagent:handle", "Handle fd=%d\n", fd)); + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + snmp_read(&fdset); +} + +/** @brief Refresh list of snmp file descriptors. */ +static void sdevent_snmp_update(const sdeventplus::Event& event) +{ + netsnmp_check_outstanding_agent_requests(); + + int maxfd = 0; + int is_blocked = 1; + fd_set fdset; + timeval timeout; + + FD_ZERO(&fdset); + snmp_select_info(&maxfd, &fdset, &timeout, &is_blocked); + + static Time snmpTimer(event, {}, std::chrono::microseconds{1}, + [](Time&, Time::TimePoint) { + DEBUGMSGTL(("snmpagent:handle", "Time out\n")); + snmp_timeout(); + run_alarms(); + }); + + if (timeout.tv_sec || timeout.tv_usec) + { + snmpTimer.set_time(Clock(event).now() + + std::chrono::seconds{timeout.tv_sec} + + std::chrono::microseconds{timeout.tv_usec}); + snmpTimer.set_enabled(sdeventplus::source::Enabled::OneShot); + } + + // We need to untrack any event whose FD is not in `fdset` anymore. + for (auto it = snmp_fds.begin(); it != snmp_fds.end();) + { + if (it->first >= maxfd || (!FD_ISSET(it->first, &fdset))) + { + DEBUGMSGTL( + ("snmpagent:handle", "Remove fd=%d from set.\n", it->first)); + it->second.set_enabled(sdeventplus::source::Enabled::Off); + it = snmp_fds.erase(it); + } + else + { + FD_CLR(it->first, &fdset); + ++it; + } + } + + // Invariant: FD in `fdset` are not in `snmp_fds` + for (int fd = 0; fd < maxfd; ++fd) + { + if (FD_ISSET(fd, &fdset)) + { + DEBUGMSGTL(("snmpagent:handle", "Add fd=%d to set.\n", fd)); + snmp_fds.emplace(fd, sdeventplus::source::IO(event, fd, EPOLLIN, + sdevent_snmp_read)); + } + } +} + +/** @brief Initialize snmp agent */ +void snmpagent_init(const sdeventplus::Event& event) +{ + // make us a agentx client + netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); + + // initialize tcpip, if necessary + SOCK_STARTUP; + + // initialize the agent library + init_agent(PACKAGE_NAME); + + // We will be used to read .conf files. + init_snmp(PACKAGE_NAME); + + // This is run before the event loop will sleep and wait for new events. + static sdeventplus::source::Post post( + event, [](sdeventplus::source::EventBase& source) { + sdevent_snmp_update(source.get_event()); + }); + + sdevent_snmp_update(event); +} + +/** @brief Deinitialize snmp agen */ +void snmpagent_destroy() +{ + snmp_shutdown(PACKAGE_NAME); + SOCK_CLEANUP; +} diff --git a/agent/snmp.hpp b/agent/snmp.hpp new file mode 100644 index 0000000..8332850 --- /dev/null +++ b/agent/snmp.hpp @@ -0,0 +1,26 @@ +/** + * @brief SNMP Agent implementation. + * + * 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 + +void snmpagent_init(const sdeventplus::Event& event); +void snmpagent_destroy(); diff --git a/agent/snmp_oid.hpp b/agent/snmp_oid.hpp new file mode 100644 index 0000000..5faf623 --- /dev/null +++ b/agent/snmp_oid.hpp @@ -0,0 +1,83 @@ +/** + * @brief SNMP OID manipulation helper. + * + * 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 +#include +#include + +namespace phosphor +{ +namespace snmp +{ +namespace agent +{ + +using OID = std::vector; + +/** + * @brief Parse OID present as string + * + * @param oid_value - Container for parsed oid + * @param fmt - Format string for oid presence + * @param ... - Additional args for oid string + */ +inline void make_oid(OID& oid_value, const char* fmt, ...) +{ + va_list ap; + std::vector oid_str; + + // Calculate required size of oid_strfer + va_start(ap, fmt); + int n = vsnprintf(nullptr, 0, fmt, ap); + va_end(ap); + + if (n > 0) + { + oid_str.resize(n + 1); // for terminating '\0' + + // Create string oid + va_start(ap, fmt); + n = vsnprintf(oid_str.data(), oid_str.size(), fmt, ap); + va_end(ap); + } + + if (n < 0) + { + oid_str.clear(); + } + + // Parse oid string + size_t oid_len = MAX_OID_LEN; + oid_value.resize(oid_len); + if (read_objid(oid_str.data(), oid_value.data(), &oid_len)) + { + oid_value.resize(oid_len); + } + else + { + oid_value.clear(); + } +} + +} // namespace agent +} // namespace snmp +} // namespace phosphor diff --git a/agent/snmptrap.hpp b/agent/snmptrap.hpp new file mode 100644 index 0000000..59ef74b --- /dev/null +++ b/agent/snmptrap.hpp @@ -0,0 +1,164 @@ +/** + * @brief SNMP Traps implementation + * + * 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 +#include +#include "snmp_oid.hpp" +#include "snmpvars.hpp" + +namespace phosphor +{ +namespace snmp +{ +namespace agent +{ + +namespace details +{ + +/** + * @brief unique_ptr functor to release an variable list reference. + */ +struct VariableListDeleter +{ + void operator()(netsnmp_variable_list* ptr) const + { + deleter(ptr); + } + + decltype(&snmp_free_varbind) deleter = snmp_free_varbind; +}; + +/** + * @brief Alias 'VariableList' to a unique_ptr type for auto-release. + */ +using VariableList = + std::unique_ptr; + +} // namespace details + +/* + * In the notification, we have to assign our notification OID to + * the snmpTrapOID.0 object. Here is it's defintion. + */ +constexpr std::array SNMPTRAP_OID = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; + +class Trap +{ + public: + /* 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. + */ + Trap() = delete; + Trap(const Trap&) = delete; + Trap& operator=(const Trap&) = delete; + Trap(Trap&&) = default; + Trap& operator=(Trap&&) = default; + ~Trap() = default; + + explicit Trap(const OID& trap_oid) + { + create_variable_list(trap_oid.data(), trap_oid.size()); + } + + Trap(const oid* trap_oid, size_t trap_oid_len) + { + create_variable_list(trap_oid, trap_oid_len); + } + + template void add_field(const OID& field_oid, T&& field_value) + { + add_field(field_oid.data(), field_oid.size(), + std::forward(field_value)); + } + + void add_field(const oid* field_oid, size_t field_oid_len, + const std::string field_value) + { + DEBUGMSGTL(("snmpagent:trap", " Field OID: ")); + DEBUGMSGOID(("snmpagent:trap", field_oid, field_oid_len)); + DEBUGMSG( + ("snmpagent:trap", ", Value: STRING(%s)\n", field_value.c_str())); + + VariableList::add(_vars.get(), field_oid, field_oid_len, field_value); + } + + void add_field(const oid* field_oid, size_t field_oid_len, bool field_value) + { + DEBUGMSGTL(("snmpagent:trap", " Field OID: ")); + DEBUGMSGOID(("snmpagent:trap", field_oid, field_oid_len)); + DEBUGMSG(("snmpagent:trap", ", Value: BOOLEAN(%s)\n", + field_value ? "True" : "False")); + + VariableList::add(_vars.get(), field_oid, field_oid_len, field_value); + } + + template + void add_field(const oid* field_oid, size_t field_oid_len, T&& field_value) + { + DEBUGMSGTL(("snmpagent:trap", " Field OID: ")); + DEBUGMSGOID(("snmpagent:trap", field_oid, field_oid_len)); + DEBUGMSG(("snmpagent:trap", ", Value: INTEGER(%d)\n", field_value)); + + VariableList::add(_vars.get(), field_oid, field_oid_len, + std::forward(field_value)); + } + + void send() const + { + DEBUGMSGTL(("snmpagent:trap", "send trap\n")); + send_v2trap(_vars.get()); + } + + protected: + void create_variable_list(const oid* trap_oid, size_t trap_oid_len) + { + DEBUGMSGTL(("snmpagent:trap", "Trap OID: ")); + DEBUGMSGOID(("snmpagent:trap", trap_oid, trap_oid_len)); + DEBUGMSG(("snmpagent:trap", "\n")); + + netsnmp_variable_list* vars = nullptr; + + // add in the trap definition object + snmp_varlist_add_variable(&vars, + /* the snmpTrapOID.0 variable */ + SNMPTRAP_OID.data(), SNMPTRAP_OID.size(), + /* value type is an OID */ + ASN_OBJECT_ID, + /* value contents is our notification OID */ + reinterpret_cast(trap_oid), + /* size of notification OID in bytes */ + trap_oid_len * sizeof(oid)); + _vars.reset(vars); + } + + details::VariableList _vars; +}; + +} // namespace agent +} // namespace snmp +} // namespace phosphor diff --git a/agent/snmpvars.hpp b/agent/snmpvars.hpp new file mode 100644 index 0000000..67cfa17 --- /dev/null +++ b/agent/snmpvars.hpp @@ -0,0 +1,121 @@ +/** + * @brief netsnmp_variable_list C++ wrapper. + * + * 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 + +namespace phosphor +{ +namespace snmp +{ +namespace agent +{ + +/** + * @brief SNMP representation of boolean type. + */ +enum class TruthValue : int +{ + True = 1, + False = 2, +}; + +#define SNMPBOOL(value) \ + static_cast(value ? TruthValue::True : TruthValue::False) + +struct VariableList +{ + /** + * @brief Fill snmp field with string value. + */ + static void set(netsnmp_variable_list* var, const std::string& value) + { + snmp_set_var_typed_value(var, ASN_OCTET_STR, value.c_str(), + value.length()); + } + + /** + * @brief Fill snmp field with boolean value. + */ + static void set(netsnmp_variable_list* var, bool value) + { + snmp_set_var_typed_integer(var, ASN_INTEGER, SNMPBOOL(value)); + } + + /** + * @brief Fill snmp field with integral value. + */ + template static void set(netsnmp_variable_list* var, T&& value) + { + snmp_set_var_typed_integer(var, ASN_INTEGER, value); + } + + /** + * @brief Add string as field into snmp variables list. + * + * @param vars - Pointer to snmp variables list + * @param field_oid - field OID + * @param field_oid_len - length of field OID + * @param field_value - field value + */ + static void add(netsnmp_variable_list* vars, const oid* field_oid, + size_t field_oid_len, const std::string& field_value) + { + snmp_varlist_add_variable( + &vars, field_oid, field_oid_len, ASN_OCTET_STR, + reinterpret_cast(field_value.data()), + field_value.length()); + } + + /** + * @brief Add boolean as field into snmp variables list. + * + * @param vars - Pointer to snmp variables list + * @param field_oid - field OID + * @param field_oid_len - length of field OID + * @param field_value - field value + */ + static void add(netsnmp_variable_list* vars, const oid* field_oid, + size_t field_oid_len, bool field_value) + { + auto v = SNMPBOOL(field_value); + snmp_varlist_add_variable(&vars, field_oid, field_oid_len, ASN_INTEGER, + &v, sizeof(v)); + } + /** + * @brief Add value of integral type as field into snmp variables list. + * + * @param vars - Pointer to snmp variables list + * @param field_oid - field OID + * @param field_oid_len - length of field OID + * @param field_value - field value + */ + template + static void add(netsnmp_variable_list* vars, const oid* field_oid, + size_t field_oid_len, T&& field_value) + { + snmp_varlist_add_variable(&vars, field_oid, field_oid_len, ASN_INTEGER, + reinterpret_cast(&field_value), + sizeof(field_value)); + } +}; + +} // namespace agent +} // namespace snmp +} // namespace phosphor diff --git a/agent/tracing.hpp b/agent/tracing.hpp new file mode 100644 index 0000000..cc9e1c8 --- /dev/null +++ b/agent/tracing.hpp @@ -0,0 +1,30 @@ +/** + * @brief TRACE_* macroses definitions. + * + * 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 +#include + +#define TRACE_ERROR(fmt, ...) snmp_log(LOG_ERR, fmt, ##__VA_ARGS__) +#define TRACE_WARNING(fmt, ...) snmp_log(LOG_WARNING, fmt, ##__VA_ARGS__) +#define TRACE_NOTICE(fmt, ...) snmp_log(LOG_NOTICE, fmt, ##__VA_ARGS__) +#define TRACE_INFO(fmt, ...) snmp_log(LOG_INFO, fmt, ##__VA_ARGS__) +#define TRACE_DEBUG(fmt, ...) snmp_log(LOG_DEBUG, fmt, ##__VA_ARGS__) -- cgit v1.2.3