1b52664e2SAppaRao Puli /* 26be832e2SEd Tanous Copyright (c) 2020 Intel Corporation 36be832e2SEd Tanous 46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License"); 56be832e2SEd Tanous you may not use this file except in compliance with the License. 66be832e2SEd Tanous You may obtain a copy of the License at 76be832e2SEd Tanous 86be832e2SEd Tanous http://www.apache.org/licenses/LICENSE-2.0 96be832e2SEd Tanous 106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software 116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS, 126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136be832e2SEd Tanous See the License for the specific language governing permissions and 146be832e2SEd Tanous limitations under the License. 15b52664e2SAppaRao Puli */ 16b52664e2SAppaRao Puli #pragma once 173ccb3adbSEd Tanous #include "dbus_utility.hpp" 183ccb3adbSEd Tanous #include "error_messages.hpp" 19d3a48a14SEd Tanous #include "event_matches_filter.hpp" 203ccb3adbSEd Tanous #include "event_service_store.hpp" 21f80a87f2SEd Tanous #include "filter_expr_executor.hpp" 22539d8c6bSEd Tanous #include "generated/enums/event.hpp" 23539d8c6bSEd Tanous #include "generated/enums/log_entry.hpp" 243ccb3adbSEd Tanous #include "http_client.hpp" 25c0353249SWludzik, Jozef #include "metric_report.hpp" 262c6ffdb0SEd Tanous #include "ossl_random.hpp" 273ccb3adbSEd Tanous #include "persistent_data.hpp" 287f4eb588SAppaRao Puli #include "registries.hpp" 298dab0f58SEd Tanous #include "registries_selector.hpp" 3050ebd4afSEd Tanous #include "str_utility.hpp" 3177665bdaSNan Zhou #include "utility.hpp" 323ccb3adbSEd Tanous #include "utils/json_utils.hpp" 335b90429aSEd Tanous #include "utils/time_utils.hpp" 347f4eb588SAppaRao Puli 357f4eb588SAppaRao Puli #include <sys/inotify.h> 36b52664e2SAppaRao Puli 37fb4fd5d4SZhenfei Tai #include <boost/asio/io_context.hpp> 38f80a87f2SEd Tanous #include <boost/circular_buffer.hpp> 39b52664e2SAppaRao Puli #include <boost/container/flat_map.hpp> 40ef4c65b7SEd Tanous #include <boost/url/format.hpp> 414a7fbefdSEd Tanous #include <boost/url/url_view_base.hpp> 42b5b40605Snitroglycerine #include <sdbusplus/bus/match.hpp> 431214b7e7SGunnar Mills 445e44e3d8SAppaRao Puli #include <algorithm> 45b52664e2SAppaRao Puli #include <cstdlib> 46b52664e2SAppaRao Puli #include <ctime> 47a14c9113SEd Tanous #include <format> 481bf712bcSAyushi Smriti #include <fstream> 49b52664e2SAppaRao Puli #include <memory> 503544d2a7SEd Tanous #include <ranges> 5126702d01SEd Tanous #include <span> 52a14c9113SEd Tanous #include <string> 53b52664e2SAppaRao Puli 54b52664e2SAppaRao Puli namespace redfish 55b52664e2SAppaRao Puli { 56156d6b00SAppaRao Puli 57156d6b00SAppaRao Puli static constexpr const char* eventFormatType = "Event"; 58156d6b00SAppaRao Puli static constexpr const char* metricReportFormatType = "MetricReport"; 59156d6b00SAppaRao Puli 605e44e3d8SAppaRao Puli static constexpr const char* subscriptionTypeSSE = "SSE"; 611bf712bcSAyushi Smriti static constexpr const char* eventServiceFile = 621bf712bcSAyushi Smriti "/var/lib/bmcweb/eventservice_config.json"; 631bf712bcSAyushi Smriti 645e44e3d8SAppaRao Puli static constexpr const uint8_t maxNoOfSubscriptions = 20; 655e44e3d8SAppaRao Puli static constexpr const uint8_t maxNoOfSSESubscriptions = 10; 665e44e3d8SAppaRao Puli 67cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 684642bf8fSGeorge Liu static std::optional<boost::asio::posix::stream_descriptor> inotifyConn; 694642bf8fSGeorge Liu static constexpr const char* redfishEventLogDir = "/var/log"; 704642bf8fSGeorge Liu static constexpr const char* redfishEventLogFile = "/var/log/redfish"; 714642bf8fSGeorge Liu static constexpr const size_t iEventSize = sizeof(inotify_event); 72cf9e417dSEd Tanous 73cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 744642bf8fSGeorge Liu static int inotifyFd = -1; 75cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 764642bf8fSGeorge Liu static int dirWatchDesc = -1; 77cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 784642bf8fSGeorge Liu static int fileWatchDesc = -1; 79f80a87f2SEd Tanous struct EventLogObjectsType 80f80a87f2SEd Tanous { 81f80a87f2SEd Tanous std::string id; 82f80a87f2SEd Tanous std::string timestamp; 83f80a87f2SEd Tanous std::string messageId; 84f80a87f2SEd Tanous std::vector<std::string> messageArgs; 85f80a87f2SEd Tanous }; 864642bf8fSGeorge Liu 87fffb8c1fSEd Tanous namespace registries 884642bf8fSGeorge Liu { 897f4eb588SAppaRao Puli static const Message* 907f4eb588SAppaRao Puli getMsgFromRegistry(const std::string& messageKey, 9126702d01SEd Tanous const std::span<const MessageEntry>& registry) 927f4eb588SAppaRao Puli { 933544d2a7SEd Tanous std::span<const MessageEntry>::iterator messageIt = std::ranges::find_if( 943544d2a7SEd Tanous registry, [&messageKey](const MessageEntry& messageEntry) { 9555f79e6fSEd Tanous return messageKey == messageEntry.first; 967f4eb588SAppaRao Puli }); 9726702d01SEd Tanous if (messageIt != registry.end()) 987f4eb588SAppaRao Puli { 997f4eb588SAppaRao Puli return &messageIt->second; 1007f4eb588SAppaRao Puli } 1017f4eb588SAppaRao Puli 1027f4eb588SAppaRao Puli return nullptr; 1037f4eb588SAppaRao Puli } 1047f4eb588SAppaRao Puli 10526ccae32SEd Tanous static const Message* formatMessage(std::string_view messageID) 1067f4eb588SAppaRao Puli { 1077f4eb588SAppaRao Puli // Redfish MessageIds are in the form 1087f4eb588SAppaRao Puli // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 1097f4eb588SAppaRao Puli // the right Message 1107f4eb588SAppaRao Puli std::vector<std::string> fields; 1117f4eb588SAppaRao Puli fields.reserve(4); 11250ebd4afSEd Tanous 11350ebd4afSEd Tanous bmcweb::split(fields, messageID, '.'); 1147f4eb588SAppaRao Puli if (fields.size() != 4) 1157f4eb588SAppaRao Puli { 1167f4eb588SAppaRao Puli return nullptr; 1177f4eb588SAppaRao Puli } 11802cad96eSEd Tanous const std::string& registryName = fields[0]; 11902cad96eSEd Tanous const std::string& messageKey = fields[3]; 1207f4eb588SAppaRao Puli 1217f4eb588SAppaRao Puli // Find the right registry and check it for the MessageKey 122b304bd79SP Dheeraj Srujan Kumar return getMsgFromRegistry(messageKey, getRegistryFromPrefix(registryName)); 1237f4eb588SAppaRao Puli } 124fffb8c1fSEd Tanous } // namespace registries 1257f4eb588SAppaRao Puli 1267f4eb588SAppaRao Puli namespace event_log 1277f4eb588SAppaRao Puli { 1282558979cSP Dheeraj Srujan Kumar inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) 1297f4eb588SAppaRao Puli { 1307f4eb588SAppaRao Puli static time_t prevTs = 0; 1317f4eb588SAppaRao Puli static int index = 0; 1327f4eb588SAppaRao Puli 1337f4eb588SAppaRao Puli // Get the entry timestamp 1347f4eb588SAppaRao Puli std::time_t curTs = 0; 1357f4eb588SAppaRao Puli std::tm timeStruct = {}; 1367f4eb588SAppaRao Puli std::istringstream entryStream(logEntry); 1377f4eb588SAppaRao Puli if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 1387f4eb588SAppaRao Puli { 1397f4eb588SAppaRao Puli curTs = std::mktime(&timeStruct); 1407f4eb588SAppaRao Puli if (curTs == -1) 1417f4eb588SAppaRao Puli { 1427f4eb588SAppaRao Puli return false; 1437f4eb588SAppaRao Puli } 1447f4eb588SAppaRao Puli } 1457f4eb588SAppaRao Puli // If the timestamp isn't unique, increment the index 1467f4eb588SAppaRao Puli index = (curTs == prevTs) ? index + 1 : 0; 1477f4eb588SAppaRao Puli 1487f4eb588SAppaRao Puli // Save the timestamp 1497f4eb588SAppaRao Puli prevTs = curTs; 1507f4eb588SAppaRao Puli 1517f4eb588SAppaRao Puli entryID = std::to_string(curTs); 1527f4eb588SAppaRao Puli if (index > 0) 1537f4eb588SAppaRao Puli { 1547f4eb588SAppaRao Puli entryID += "_" + std::to_string(index); 1557f4eb588SAppaRao Puli } 1567f4eb588SAppaRao Puli return true; 1577f4eb588SAppaRao Puli } 1587f4eb588SAppaRao Puli 15923a21a1cSEd Tanous inline int getEventLogParams(const std::string& logEntry, 16023a21a1cSEd Tanous std::string& timestamp, std::string& messageID, 1615e715de6SAppaRao Puli std::vector<std::string>& messageArgs) 1627f4eb588SAppaRao Puli { 1637f4eb588SAppaRao Puli // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 1647f4eb588SAppaRao Puli // First get the Timestamp 165f23b7296SEd Tanous size_t space = logEntry.find_first_of(' '); 1667f4eb588SAppaRao Puli if (space == std::string::npos) 1677f4eb588SAppaRao Puli { 16803d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", 16903d4d37cSAlexander Hansen logEntry); 1707f4eb588SAppaRao Puli return -EINVAL; 1717f4eb588SAppaRao Puli } 1727f4eb588SAppaRao Puli timestamp = logEntry.substr(0, space); 1737f4eb588SAppaRao Puli // Then get the log contents 174f23b7296SEd Tanous size_t entryStart = logEntry.find_first_not_of(' ', space); 1757f4eb588SAppaRao Puli if (entryStart == std::string::npos) 1767f4eb588SAppaRao Puli { 17703d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", 17803d4d37cSAlexander Hansen logEntry); 1797f4eb588SAppaRao Puli return -EINVAL; 1807f4eb588SAppaRao Puli } 1817f4eb588SAppaRao Puli std::string_view entry(logEntry); 1827f4eb588SAppaRao Puli entry.remove_prefix(entryStart); 1837f4eb588SAppaRao Puli // Use split to separate the entry into its fields 1847f4eb588SAppaRao Puli std::vector<std::string> logEntryFields; 18550ebd4afSEd Tanous bmcweb::split(logEntryFields, entry, ','); 1867f4eb588SAppaRao Puli // We need at least a MessageId to be valid 18726f6976fSEd Tanous if (logEntryFields.empty()) 1887f4eb588SAppaRao Puli { 18903d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", 19003d4d37cSAlexander Hansen logEntry); 1917f4eb588SAppaRao Puli return -EINVAL; 1927f4eb588SAppaRao Puli } 1937f4eb588SAppaRao Puli messageID = logEntryFields[0]; 1947f4eb588SAppaRao Puli 1957f4eb588SAppaRao Puli // Get the MessageArgs from the log if there are any 1967f4eb588SAppaRao Puli if (logEntryFields.size() > 1) 1977f4eb588SAppaRao Puli { 19802cad96eSEd Tanous const std::string& messageArgsStart = logEntryFields[1]; 1997f4eb588SAppaRao Puli // If the first string is empty, assume there are no MessageArgs 2007f4eb588SAppaRao Puli if (!messageArgsStart.empty()) 2017f4eb588SAppaRao Puli { 2025e715de6SAppaRao Puli messageArgs.assign(logEntryFields.begin() + 1, 2035e715de6SAppaRao Puli logEntryFields.end()); 2047f4eb588SAppaRao Puli } 2057f4eb588SAppaRao Puli } 2067f4eb588SAppaRao Puli 2077f4eb588SAppaRao Puli return 0; 2087f4eb588SAppaRao Puli } 2097f4eb588SAppaRao Puli 210bd79bce8SPatrick Williams inline int formatEventLogEntry( 211bd79bce8SPatrick Williams const std::string& logEntryID, const std::string& messageID, 212bd79bce8SPatrick Williams const std::span<std::string_view> messageArgs, std::string timestamp, 213bd79bce8SPatrick Williams const std::string& customText, nlohmann::json::object_t& logEntryJson) 2147f4eb588SAppaRao Puli { 2157f4eb588SAppaRao Puli // Get the Message from the MessageRegistry 216fffb8c1fSEd Tanous const registries::Message* message = registries::formatMessage(messageID); 2177f4eb588SAppaRao Puli 21880f595e7SEd Tanous if (message == nullptr) 2197f4eb588SAppaRao Puli { 22080f595e7SEd Tanous return -1; 2217f4eb588SAppaRao Puli } 2227f4eb588SAppaRao Puli 223bd79bce8SPatrick Williams std::string msg = 224bd79bce8SPatrick Williams redfish::registries::fillMessageArgs(messageArgs, message->message); 22580f595e7SEd Tanous if (msg.empty()) 22680f595e7SEd Tanous { 22780f595e7SEd Tanous return -1; 22880f595e7SEd Tanous } 2297f4eb588SAppaRao Puli 2307f4eb588SAppaRao Puli // Get the Created time from the timestamp. The log timestamp is in 2317f4eb588SAppaRao Puli // RFC3339 format which matches the Redfish format except for the 2327f4eb588SAppaRao Puli // fractional seconds between the '.' and the '+', so just remove them. 233f23b7296SEd Tanous std::size_t dot = timestamp.find_first_of('.'); 234b2f7609bSEd Tanous std::size_t plus = timestamp.find_first_of('+', dot); 2357f4eb588SAppaRao Puli if (dot != std::string::npos && plus != std::string::npos) 2367f4eb588SAppaRao Puli { 2377f4eb588SAppaRao Puli timestamp.erase(dot, plus - dot); 2387f4eb588SAppaRao Puli } 2397f4eb588SAppaRao Puli 2407f4eb588SAppaRao Puli // Fill in the log entry with the gathered data 2411476687dSEd Tanous logEntryJson["EventId"] = logEntryID; 242539d8c6bSEd Tanous 24380f595e7SEd Tanous logEntryJson["Severity"] = message->messageSeverity; 2441476687dSEd Tanous logEntryJson["Message"] = std::move(msg); 2451476687dSEd Tanous logEntryJson["MessageId"] = messageID; 2461476687dSEd Tanous logEntryJson["MessageArgs"] = messageArgs; 2471476687dSEd Tanous logEntryJson["EventTimestamp"] = std::move(timestamp); 2481476687dSEd Tanous logEntryJson["Context"] = customText; 2497f4eb588SAppaRao Puli return 0; 2507f4eb588SAppaRao Puli } 2517f4eb588SAppaRao Puli 2527f4eb588SAppaRao Puli } // namespace event_log 2537f4eb588SAppaRao Puli 2544b712a29SEd Tanous class Subscription 255b52664e2SAppaRao Puli { 256b52664e2SAppaRao Puli public: 257b52664e2SAppaRao Puli Subscription(const Subscription&) = delete; 258b52664e2SAppaRao Puli Subscription& operator=(const Subscription&) = delete; 259b52664e2SAppaRao Puli Subscription(Subscription&&) = delete; 260b52664e2SAppaRao Puli Subscription& operator=(Subscription&&) = delete; 261b52664e2SAppaRao Puli 262*21a94d5cSMyung Bae Subscription(const persistent_data::UserSubscription& userSubIn, 263*21a94d5cSMyung Bae const boost::urls::url_view_base& url, 2644a7fbefdSEd Tanous boost::asio::io_context& ioc) : 265*21a94d5cSMyung Bae userSub(userSubIn), policy(std::make_shared<crow::ConnectionPolicy>()) 266b52664e2SAppaRao Puli { 2674b712a29SEd Tanous userSub.destinationUrl = url; 2685e44e3d8SAppaRao Puli client.emplace(ioc, policy); 2697adb85acSSunitha Harish // Subscription constructor 270d14a48ffSCarson Labrado policy->invalidResp = retryRespHandler; 271b52664e2SAppaRao Puli } 2724bbf237fSAppaRao Puli 2735e44e3d8SAppaRao Puli explicit Subscription(crow::sse_socket::Connection& connIn) : 2745e44e3d8SAppaRao Puli sseConn(&connIn) 2755e44e3d8SAppaRao Puli {} 2765e44e3d8SAppaRao Puli 2779f616dd1SEd Tanous ~Subscription() = default; 278b52664e2SAppaRao Puli 2796d799e14SEd Tanous bool sendEventToSubscriber(std::string&& msg) 280b52664e2SAppaRao Puli { 2816ba8c82eSsunharis_in persistent_data::EventServiceConfig eventServiceConfig = 2826ba8c82eSsunharis_in persistent_data::EventServiceStore::getInstance() 2836ba8c82eSsunharis_in .getEventServiceConfig(); 2846ba8c82eSsunharis_in if (!eventServiceConfig.enabled) 2856ba8c82eSsunharis_in { 2866ba8c82eSsunharis_in return false; 2876ba8c82eSsunharis_in } 2886ba8c82eSsunharis_in 2895e44e3d8SAppaRao Puli if (client) 2905e44e3d8SAppaRao Puli { 2914b712a29SEd Tanous client->sendData(std::move(msg), userSub.destinationUrl, 2924b712a29SEd Tanous static_cast<ensuressl::VerifyCertificate>( 2934b712a29SEd Tanous userSub.verifyCertificate), 2944b712a29SEd Tanous userSub.httpHeaders, 2954b712a29SEd Tanous boost::beast::http::verb::post); 2965e44e3d8SAppaRao Puli return true; 2975e44e3d8SAppaRao Puli } 2987adb85acSSunitha Harish 2994bbf237fSAppaRao Puli if (sseConn != nullptr) 3004bbf237fSAppaRao Puli { 3015e44e3d8SAppaRao Puli eventSeqNum++; 3026d799e14SEd Tanous sseConn->sendSseEvent(std::to_string(eventSeqNum), msg); 3034bbf237fSAppaRao Puli } 3046ba8c82eSsunharis_in return true; 3054bbf237fSAppaRao Puli } 3064bbf237fSAppaRao Puli 3076ba8c82eSsunharis_in bool sendTestEventLog() 3080b4bdd93SAppaRao Puli { 309f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 310f80a87f2SEd Tanous nlohmann::json& logEntryJson = logEntryArray.emplace_back(); 3110b4bdd93SAppaRao Puli 312613dabeaSEd Tanous logEntryJson["EventId"] = "TestID"; 313539d8c6bSEd Tanous logEntryJson["Severity"] = log_entry::EventSeverity::OK; 314613dabeaSEd Tanous logEntryJson["Message"] = "Generated test event"; 315613dabeaSEd Tanous logEntryJson["MessageId"] = "OpenBMC.0.2.TestEventLog"; 316d2cdd478SChandra Harkude // MemberId is 0 : since we are sending one event record. 317d2cdd478SChandra Harkude logEntryJson["MemberId"] = 0; 318613dabeaSEd Tanous logEntryJson["MessageArgs"] = nlohmann::json::array(); 319613dabeaSEd Tanous logEntryJson["EventTimestamp"] = 320613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 3214b712a29SEd Tanous logEntryJson["Context"] = userSub.customText; 3220b4bdd93SAppaRao Puli 3231476687dSEd Tanous nlohmann::json msg; 3241476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 3251476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 3261476687dSEd Tanous msg["Name"] = "Event Log"; 3271476687dSEd Tanous msg["Events"] = logEntryArray; 3280b4bdd93SAppaRao Puli 329bd79bce8SPatrick Williams std::string strMsg = 330bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 3316d799e14SEd Tanous return sendEventToSubscriber(std::move(strMsg)); 3320b4bdd93SAppaRao Puli } 3330b4bdd93SAppaRao Puli 3347f4eb588SAppaRao Puli void filterAndSendEventLogs( 3357f4eb588SAppaRao Puli const std::vector<EventLogObjectsType>& eventRecords) 3367f4eb588SAppaRao Puli { 337f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 3387f4eb588SAppaRao Puli for (const EventLogObjectsType& logEntry : eventRecords) 3397f4eb588SAppaRao Puli { 340f80a87f2SEd Tanous std::vector<std::string_view> messageArgsView( 341f80a87f2SEd Tanous logEntry.messageArgs.begin(), logEntry.messageArgs.end()); 3427f4eb588SAppaRao Puli 343f80a87f2SEd Tanous nlohmann::json::object_t bmcLogEntry; 344f80a87f2SEd Tanous if (event_log::formatEventLogEntry( 345f80a87f2SEd Tanous logEntry.id, logEntry.messageId, messageArgsView, 3464b712a29SEd Tanous logEntry.timestamp, userSub.customText, bmcLogEntry) != 0) 3477f4eb588SAppaRao Puli { 34862598e31SEd Tanous BMCWEB_LOG_DEBUG("Read eventLog entry failed"); 3497f4eb588SAppaRao Puli continue; 3507f4eb588SAppaRao Puli } 351f80a87f2SEd Tanous 352d3a48a14SEd Tanous if (!eventMatchesFilter(userSub, bmcLogEntry, "")) 353f80a87f2SEd Tanous { 35403d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Event {} did not match the filter", 35503d4d37cSAlexander Hansen nlohmann::json(bmcLogEntry).dump()); 356f80a87f2SEd Tanous continue; 357f80a87f2SEd Tanous } 358f80a87f2SEd Tanous 359d3a48a14SEd Tanous if (filter) 360d3a48a14SEd Tanous { 361d3a48a14SEd Tanous if (!memberMatches(bmcLogEntry, *filter)) 362d3a48a14SEd Tanous { 363d3a48a14SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 364d3a48a14SEd Tanous continue; 365d3a48a14SEd Tanous } 366d3a48a14SEd Tanous } 367d3a48a14SEd Tanous 368f80a87f2SEd Tanous logEntryArray.emplace_back(std::move(bmcLogEntry)); 3697f4eb588SAppaRao Puli } 3707f4eb588SAppaRao Puli 37126f6976fSEd Tanous if (logEntryArray.empty()) 3727f4eb588SAppaRao Puli { 37362598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 3747f4eb588SAppaRao Puli return; 3757f4eb588SAppaRao Puli } 3767f4eb588SAppaRao Puli 3771476687dSEd Tanous nlohmann::json msg; 3781476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 3791476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 3801476687dSEd Tanous msg["Name"] = "Event Log"; 381f80a87f2SEd Tanous msg["Events"] = std::move(logEntryArray); 382bd79bce8SPatrick Williams std::string strMsg = 383bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 3846d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 3855e44e3d8SAppaRao Puli eventSeqNum++; 3867f4eb588SAppaRao Puli } 3877f4eb588SAppaRao Puli 388248d0230SEd Tanous void filterAndSendReports(const std::string& reportId, 3891e1e598dSJonathan Doman const telemetry::TimestampReadings& var) 390156d6b00SAppaRao Puli { 391ef4c65b7SEd Tanous boost::urls::url mrdUri = boost::urls::format( 392ef4c65b7SEd Tanous "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", 393ef4c65b7SEd Tanous reportId); 394156d6b00SAppaRao Puli 395156d6b00SAppaRao Puli // Empty list means no filter. Send everything. 3964b712a29SEd Tanous if (!userSub.metricReportDefinitions.empty()) 397156d6b00SAppaRao Puli { 3984b712a29SEd Tanous if (std::ranges::find(userSub.metricReportDefinitions, 3994b712a29SEd Tanous mrdUri.buffer()) == 4004b712a29SEd Tanous userSub.metricReportDefinitions.end()) 401156d6b00SAppaRao Puli { 402156d6b00SAppaRao Puli return; 403156d6b00SAppaRao Puli } 404156d6b00SAppaRao Puli } 405156d6b00SAppaRao Puli 406c0353249SWludzik, Jozef nlohmann::json msg; 407248d0230SEd Tanous if (!telemetry::fillReport(msg, reportId, var)) 408156d6b00SAppaRao Puli { 40962598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to fill the MetricReport for DBus " 41062598e31SEd Tanous "Report with id {}", 41162598e31SEd Tanous reportId); 412c0353249SWludzik, Jozef return; 413156d6b00SAppaRao Puli } 414156d6b00SAppaRao Puli 41522daffd7SAppaRao Puli // Context is set by user during Event subscription and it must be 41622daffd7SAppaRao Puli // set for MetricReport response. 4174b712a29SEd Tanous if (!userSub.customText.empty()) 41822daffd7SAppaRao Puli { 4194b712a29SEd Tanous msg["Context"] = userSub.customText; 42022daffd7SAppaRao Puli } 42122daffd7SAppaRao Puli 422bd79bce8SPatrick Williams std::string strMsg = 423bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 4246d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 425156d6b00SAppaRao Puli } 426156d6b00SAppaRao Puli 427d14a48ffSCarson Labrado void updateRetryConfig(uint32_t retryAttempts, 428d14a48ffSCarson Labrado uint32_t retryTimeoutInterval) 429fe44eb0bSAyushi Smriti { 43093cf0ac2SEd Tanous if (policy == nullptr) 43193cf0ac2SEd Tanous { 43293cf0ac2SEd Tanous BMCWEB_LOG_DEBUG("Retry policy was nullptr, ignoring set"); 43393cf0ac2SEd Tanous return; 43493cf0ac2SEd Tanous } 435d14a48ffSCarson Labrado policy->maxRetryAttempts = retryAttempts; 436d14a48ffSCarson Labrado policy->retryIntervalSecs = std::chrono::seconds(retryTimeoutInterval); 43762de0c68SAppaRao Puli } 438fe44eb0bSAyushi Smriti 4399eb808c1SEd Tanous uint64_t getEventSeqNum() const 44096330b99SSunitha Harish { 44196330b99SSunitha Harish return eventSeqNum; 44296330b99SSunitha Harish } 44396330b99SSunitha Harish 4445e44e3d8SAppaRao Puli void setSubscriptionId(const std::string& id2) 4455e44e3d8SAppaRao Puli { 44662598e31SEd Tanous BMCWEB_LOG_DEBUG("Subscription ID: {}", id2); 4475e44e3d8SAppaRao Puli subId = id2; 4485e44e3d8SAppaRao Puli } 4495e44e3d8SAppaRao Puli 4505e44e3d8SAppaRao Puli std::string getSubscriptionId() 4515e44e3d8SAppaRao Puli { 4525e44e3d8SAppaRao Puli return subId; 4535e44e3d8SAppaRao Puli } 4545e44e3d8SAppaRao Puli 4555e44e3d8SAppaRao Puli bool matchSseId(const crow::sse_socket::Connection& thisConn) 4565e44e3d8SAppaRao Puli { 4575e44e3d8SAppaRao Puli return &thisConn == sseConn; 4585e44e3d8SAppaRao Puli } 4595e44e3d8SAppaRao Puli 460a7a80296SCarson Labrado // Check used to indicate what response codes are valid as part of our retry 461a7a80296SCarson Labrado // policy. 2XX is considered acceptable 462a7a80296SCarson Labrado static boost::system::error_code retryRespHandler(unsigned int respCode) 463a7a80296SCarson Labrado { 46462598e31SEd Tanous BMCWEB_LOG_DEBUG( 46562598e31SEd Tanous "Checking response code validity for SubscriptionEvent"); 466a7a80296SCarson Labrado if ((respCode < 200) || (respCode >= 300)) 467a7a80296SCarson Labrado { 468a7a80296SCarson Labrado return boost::system::errc::make_error_code( 469a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 470a7a80296SCarson Labrado } 471a7a80296SCarson Labrado 472a7a80296SCarson Labrado // Return 0 if the response code is valid 473a7a80296SCarson Labrado return boost::system::errc::make_error_code( 474a7a80296SCarson Labrado boost::system::errc::success); 4759fa6d147SNan Zhou } 476f80a87f2SEd Tanous 4774b712a29SEd Tanous persistent_data::UserSubscription userSub; 4784b712a29SEd Tanous 479f80a87f2SEd Tanous private: 480f80a87f2SEd Tanous std::string subId; 481f80a87f2SEd Tanous uint64_t eventSeqNum = 1; 482f80a87f2SEd Tanous boost::urls::url host; 483f80a87f2SEd Tanous std::shared_ptr<crow::ConnectionPolicy> policy; 484f80a87f2SEd Tanous crow::sse_socket::Connection* sseConn = nullptr; 485f80a87f2SEd Tanous 486f80a87f2SEd Tanous std::optional<crow::HttpClient> client; 487f80a87f2SEd Tanous 488f80a87f2SEd Tanous public: 489f80a87f2SEd Tanous std::optional<filter_ast::LogicalAnd> filter; 490b52664e2SAppaRao Puli }; 491b52664e2SAppaRao Puli 492b52664e2SAppaRao Puli class EventServiceManager 493b52664e2SAppaRao Puli { 494b52664e2SAppaRao Puli private: 495d3a9e084SEd Tanous bool serviceEnabled = false; 496d3a9e084SEd Tanous uint32_t retryAttempts = 0; 497d3a9e084SEd Tanous uint32_t retryTimeoutInterval = 0; 4987d1cc387SAppaRao Puli 4992558979cSP Dheeraj Srujan Kumar std::streampos redfishLogFilePosition{0}; 5009f616dd1SEd Tanous size_t noOfEventLogSubscribers{0}; 5019f616dd1SEd Tanous size_t noOfMetricReportSubscribers{0}; 50259d494eeSPatrick Williams std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor; 503b52664e2SAppaRao Puli boost::container::flat_map<std::string, std::shared_ptr<Subscription>> 504b52664e2SAppaRao Puli subscriptionsMap; 505b52664e2SAppaRao Puli 5069f616dd1SEd Tanous uint64_t eventId{1}; 50796330b99SSunitha Harish 508f80a87f2SEd Tanous struct Event 509f80a87f2SEd Tanous { 510f80a87f2SEd Tanous std::string id; 511f80a87f2SEd Tanous nlohmann::json message; 512f80a87f2SEd Tanous }; 513f80a87f2SEd Tanous 514f80a87f2SEd Tanous constexpr static size_t maxMessages = 200; 515f80a87f2SEd Tanous boost::circular_buffer<Event> messages{maxMessages}; 516f80a87f2SEd Tanous 517f8ca6d79SEd Tanous boost::asio::io_context& ioc; 518f8ca6d79SEd Tanous 519b52664e2SAppaRao Puli public: 5209f616dd1SEd Tanous EventServiceManager(const EventServiceManager&) = delete; 5219f616dd1SEd Tanous EventServiceManager& operator=(const EventServiceManager&) = delete; 5229f616dd1SEd Tanous EventServiceManager(EventServiceManager&&) = delete; 5239f616dd1SEd Tanous EventServiceManager& operator=(EventServiceManager&&) = delete; 524ecd6a3a2SEd Tanous ~EventServiceManager() = default; 5259f616dd1SEd Tanous 526f8ca6d79SEd Tanous explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn) 527b52664e2SAppaRao Puli { 528f8ca6d79SEd Tanous // Load config from persist store. 529f8ca6d79SEd Tanous initConfig(); 530f8ca6d79SEd Tanous } 531f8ca6d79SEd Tanous 532f8ca6d79SEd Tanous static EventServiceManager& 533f8ca6d79SEd Tanous getInstance(boost::asio::io_context* ioc = nullptr) 534f8ca6d79SEd Tanous { 535f8ca6d79SEd Tanous static EventServiceManager handler(*ioc); 536b52664e2SAppaRao Puli return handler; 537b52664e2SAppaRao Puli } 538b52664e2SAppaRao Puli 5391bf712bcSAyushi Smriti void initConfig() 5401bf712bcSAyushi Smriti { 54128afb49cSJunLin Chen loadOldBehavior(); 5421bf712bcSAyushi Smriti 54328afb49cSJunLin Chen persistent_data::EventServiceConfig eventServiceConfig = 54428afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 54528afb49cSJunLin Chen .getEventServiceConfig(); 5461bf712bcSAyushi Smriti 54728afb49cSJunLin Chen serviceEnabled = eventServiceConfig.enabled; 54828afb49cSJunLin Chen retryAttempts = eventServiceConfig.retryAttempts; 54928afb49cSJunLin Chen retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval; 5501bf712bcSAyushi Smriti 55128afb49cSJunLin Chen for (const auto& it : persistent_data::EventServiceStore::getInstance() 55228afb49cSJunLin Chen .subscriptionsConfigMap) 5531bf712bcSAyushi Smriti { 5544b712a29SEd Tanous const persistent_data::UserSubscription& newSub = it.second; 5554bbf237fSAppaRao Puli 5566fd29553SEd Tanous boost::system::result<boost::urls::url> url = 5574b712a29SEd Tanous boost::urls::parse_absolute_uri(newSub.destinationUrl); 5581bf712bcSAyushi Smriti 559a716aa74SEd Tanous if (!url) 5601bf712bcSAyushi Smriti { 56162598e31SEd Tanous BMCWEB_LOG_ERROR( 56262598e31SEd Tanous "Failed to validate and split destination url"); 5631bf712bcSAyushi Smriti continue; 5641bf712bcSAyushi Smriti } 5651bf712bcSAyushi Smriti std::shared_ptr<Subscription> subValue = 566*21a94d5cSMyung Bae std::make_shared<Subscription>(newSub, *url, ioc); 5671bf712bcSAyushi Smriti 5684b712a29SEd Tanous subscriptionsMap.insert(std::pair(subValue->userSub.id, subValue)); 56928afb49cSJunLin Chen 57028afb49cSJunLin Chen updateNoOfSubscribersCount(); 57128afb49cSJunLin Chen 57283328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 57383328316SEd Tanous { 5742558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 57583328316SEd Tanous } 5762558979cSP Dheeraj Srujan Kumar 57728afb49cSJunLin Chen // Update retry configuration. 57828afb49cSJunLin Chen subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 5791bf712bcSAyushi Smriti } 5801bf712bcSAyushi Smriti } 5811bf712bcSAyushi Smriti 58256d2396dSEd Tanous static void loadOldBehavior() 583b52664e2SAppaRao Puli { 58428afb49cSJunLin Chen std::ifstream eventConfigFile(eventServiceFile); 58528afb49cSJunLin Chen if (!eventConfigFile.good()) 5861bf712bcSAyushi Smriti { 58762598e31SEd Tanous BMCWEB_LOG_DEBUG("Old eventService config not exist"); 58828afb49cSJunLin Chen return; 58928afb49cSJunLin Chen } 59028afb49cSJunLin Chen auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false); 59128afb49cSJunLin Chen if (jsonData.is_discarded()) 5924bbf237fSAppaRao Puli { 59362598e31SEd Tanous BMCWEB_LOG_ERROR("Old eventService config parse error."); 59428afb49cSJunLin Chen return; 59528afb49cSJunLin Chen } 59628afb49cSJunLin Chen 5970bdda665SEd Tanous const nlohmann::json::object_t* obj = 5980bdda665SEd Tanous jsonData.get_ptr<const nlohmann::json::object_t*>(); 5990bdda665SEd Tanous for (const auto& item : *obj) 60028afb49cSJunLin Chen { 6010bdda665SEd Tanous if (item.first == "Configuration") 60228afb49cSJunLin Chen { 60328afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 60428afb49cSJunLin Chen .getEventServiceConfig() 6050bdda665SEd Tanous .fromJson(item.second); 60628afb49cSJunLin Chen } 6070bdda665SEd Tanous else if (item.first == "Subscriptions") 60828afb49cSJunLin Chen { 6090bdda665SEd Tanous for (const auto& elem : item.second) 61028afb49cSJunLin Chen { 6114b712a29SEd Tanous std::optional<persistent_data::UserSubscription> 61228afb49cSJunLin Chen newSubscription = 61328afb49cSJunLin Chen persistent_data::UserSubscription::fromJson(elem, 61428afb49cSJunLin Chen true); 6154b712a29SEd Tanous if (!newSubscription) 61628afb49cSJunLin Chen { 61762598e31SEd Tanous BMCWEB_LOG_ERROR("Problem reading subscription " 61862598e31SEd Tanous "from old persistent store"); 6194bbf237fSAppaRao Puli continue; 6204bbf237fSAppaRao Puli } 6214b712a29SEd Tanous persistent_data::UserSubscription& newSub = 6224b712a29SEd Tanous *newSubscription; 6231bf712bcSAyushi Smriti 62428afb49cSJunLin Chen std::uniform_int_distribution<uint32_t> dist(0); 62528afb49cSJunLin Chen bmcweb::OpenSSLGenerator gen; 6261bf712bcSAyushi Smriti 62728afb49cSJunLin Chen std::string id; 6281bf712bcSAyushi Smriti 62928afb49cSJunLin Chen int retry = 3; 630e662eae8SEd Tanous while (retry != 0) 6311bf712bcSAyushi Smriti { 63228afb49cSJunLin Chen id = std::to_string(dist(gen)); 63328afb49cSJunLin Chen if (gen.error()) 6347d1cc387SAppaRao Puli { 63528afb49cSJunLin Chen retry = 0; 63628afb49cSJunLin Chen break; 63728afb49cSJunLin Chen } 6384b712a29SEd Tanous newSub.id = id; 63928afb49cSJunLin Chen auto inserted = 64028afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 64128afb49cSJunLin Chen .subscriptionsConfigMap.insert( 6424b712a29SEd Tanous std::pair(id, newSub)); 64328afb49cSJunLin Chen if (inserted.second) 64428afb49cSJunLin Chen { 64528afb49cSJunLin Chen break; 64628afb49cSJunLin Chen } 64728afb49cSJunLin Chen --retry; 6487d1cc387SAppaRao Puli } 6497d1cc387SAppaRao Puli 65028afb49cSJunLin Chen if (retry <= 0) 65128afb49cSJunLin Chen { 65262598e31SEd Tanous BMCWEB_LOG_ERROR( 65362598e31SEd Tanous "Failed to generate random number from old " 65462598e31SEd Tanous "persistent store"); 65528afb49cSJunLin Chen continue; 65628afb49cSJunLin Chen } 65728afb49cSJunLin Chen } 65828afb49cSJunLin Chen } 65928afb49cSJunLin Chen 66028afb49cSJunLin Chen persistent_data::getConfig().writeData(); 6614c521c3cSEd Tanous std::error_code ec; 6624c521c3cSEd Tanous std::filesystem::remove(eventServiceFile, ec); 6634c521c3cSEd Tanous if (ec) 6644c521c3cSEd Tanous { 6654c521c3cSEd Tanous BMCWEB_LOG_DEBUG( 6664c521c3cSEd Tanous "Failed to remove old event service file. Ignoring"); 6674c521c3cSEd Tanous } 6684c521c3cSEd Tanous else 6694c521c3cSEd Tanous { 67062598e31SEd Tanous BMCWEB_LOG_DEBUG("Remove old eventservice config"); 67128afb49cSJunLin Chen } 67228afb49cSJunLin Chen } 6734c521c3cSEd Tanous } 67428afb49cSJunLin Chen 6759eb808c1SEd Tanous void updateSubscriptionData() const 67628afb49cSJunLin Chen { 67728afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 67828afb49cSJunLin Chen .eventServiceConfig.enabled = serviceEnabled; 67928afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 68028afb49cSJunLin Chen .eventServiceConfig.retryAttempts = retryAttempts; 68128afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 68228afb49cSJunLin Chen .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval; 68328afb49cSJunLin Chen 68428afb49cSJunLin Chen persistent_data::getConfig().writeData(); 68528afb49cSJunLin Chen } 68628afb49cSJunLin Chen 68728afb49cSJunLin Chen void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg) 6887d1cc387SAppaRao Puli { 6897d1cc387SAppaRao Puli bool updateConfig = false; 690fe44eb0bSAyushi Smriti bool updateRetryCfg = false; 6917d1cc387SAppaRao Puli 69228afb49cSJunLin Chen if (serviceEnabled != cfg.enabled) 6937d1cc387SAppaRao Puli { 69428afb49cSJunLin Chen serviceEnabled = cfg.enabled; 695e662eae8SEd Tanous if (serviceEnabled && noOfMetricReportSubscribers != 0U) 6967d1cc387SAppaRao Puli { 6977d1cc387SAppaRao Puli registerMetricReportSignal(); 6987d1cc387SAppaRao Puli } 6997d1cc387SAppaRao Puli else 7007d1cc387SAppaRao Puli { 7017d1cc387SAppaRao Puli unregisterMetricReportSignal(); 7027d1cc387SAppaRao Puli } 7037d1cc387SAppaRao Puli updateConfig = true; 7047d1cc387SAppaRao Puli } 7057d1cc387SAppaRao Puli 70628afb49cSJunLin Chen if (retryAttempts != cfg.retryAttempts) 7077d1cc387SAppaRao Puli { 70828afb49cSJunLin Chen retryAttempts = cfg.retryAttempts; 7097d1cc387SAppaRao Puli updateConfig = true; 710fe44eb0bSAyushi Smriti updateRetryCfg = true; 7117d1cc387SAppaRao Puli } 7127d1cc387SAppaRao Puli 71328afb49cSJunLin Chen if (retryTimeoutInterval != cfg.retryTimeoutInterval) 7147d1cc387SAppaRao Puli { 71528afb49cSJunLin Chen retryTimeoutInterval = cfg.retryTimeoutInterval; 7167d1cc387SAppaRao Puli updateConfig = true; 717fe44eb0bSAyushi Smriti updateRetryCfg = true; 7187d1cc387SAppaRao Puli } 7197d1cc387SAppaRao Puli 7207d1cc387SAppaRao Puli if (updateConfig) 7217d1cc387SAppaRao Puli { 7227d1cc387SAppaRao Puli updateSubscriptionData(); 7237d1cc387SAppaRao Puli } 724fe44eb0bSAyushi Smriti 725fe44eb0bSAyushi Smriti if (updateRetryCfg) 726fe44eb0bSAyushi Smriti { 727fe44eb0bSAyushi Smriti // Update the changed retry config to all subscriptions 728fe44eb0bSAyushi Smriti for (const auto& it : 729fe44eb0bSAyushi Smriti EventServiceManager::getInstance().subscriptionsMap) 730fe44eb0bSAyushi Smriti { 7315e44e3d8SAppaRao Puli Subscription& entry = *it.second; 7325e44e3d8SAppaRao Puli entry.updateRetryConfig(retryAttempts, retryTimeoutInterval); 733fe44eb0bSAyushi Smriti } 734fe44eb0bSAyushi Smriti } 7357d1cc387SAppaRao Puli } 7367d1cc387SAppaRao Puli 7377d1cc387SAppaRao Puli void updateNoOfSubscribersCount() 7387d1cc387SAppaRao Puli { 7397d1cc387SAppaRao Puli size_t eventLogSubCount = 0; 7407d1cc387SAppaRao Puli size_t metricReportSubCount = 0; 7417d1cc387SAppaRao Puli for (const auto& it : subscriptionsMap) 7427d1cc387SAppaRao Puli { 7437d1cc387SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 7444b712a29SEd Tanous if (entry->userSub.eventFormatType == eventFormatType) 7457d1cc387SAppaRao Puli { 7467d1cc387SAppaRao Puli eventLogSubCount++; 7477d1cc387SAppaRao Puli } 7484b712a29SEd Tanous else if (entry->userSub.eventFormatType == metricReportFormatType) 7497d1cc387SAppaRao Puli { 7507d1cc387SAppaRao Puli metricReportSubCount++; 7517d1cc387SAppaRao Puli } 7527d1cc387SAppaRao Puli } 7537d1cc387SAppaRao Puli 7547d1cc387SAppaRao Puli noOfEventLogSubscribers = eventLogSubCount; 7557d1cc387SAppaRao Puli if (noOfMetricReportSubscribers != metricReportSubCount) 7567d1cc387SAppaRao Puli { 7577d1cc387SAppaRao Puli noOfMetricReportSubscribers = metricReportSubCount; 758e662eae8SEd Tanous if (noOfMetricReportSubscribers != 0U) 7597d1cc387SAppaRao Puli { 7607d1cc387SAppaRao Puli registerMetricReportSignal(); 7617d1cc387SAppaRao Puli } 7627d1cc387SAppaRao Puli else 7637d1cc387SAppaRao Puli { 7647d1cc387SAppaRao Puli unregisterMetricReportSignal(); 7657d1cc387SAppaRao Puli } 7667d1cc387SAppaRao Puli } 7677d1cc387SAppaRao Puli } 7687d1cc387SAppaRao Puli 769b52664e2SAppaRao Puli std::shared_ptr<Subscription> getSubscription(const std::string& id) 770b52664e2SAppaRao Puli { 771b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 772b52664e2SAppaRao Puli if (obj == subscriptionsMap.end()) 773b52664e2SAppaRao Puli { 77462598e31SEd Tanous BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id); 775b52664e2SAppaRao Puli return nullptr; 776b52664e2SAppaRao Puli } 777b52664e2SAppaRao Puli std::shared_ptr<Subscription> subValue = obj->second; 778b52664e2SAppaRao Puli return subValue; 779b52664e2SAppaRao Puli } 780b52664e2SAppaRao Puli 781f80a87f2SEd Tanous std::string 782f80a87f2SEd Tanous addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue) 783b52664e2SAppaRao Puli { 784fc76b8acSEd Tanous std::uniform_int_distribution<uint32_t> dist(0); 785fc76b8acSEd Tanous bmcweb::OpenSSLGenerator gen; 786fc76b8acSEd Tanous 787b52664e2SAppaRao Puli std::string id; 788b52664e2SAppaRao Puli 789b52664e2SAppaRao Puli int retry = 3; 790e662eae8SEd Tanous while (retry != 0) 791b52664e2SAppaRao Puli { 792fc76b8acSEd Tanous id = std::to_string(dist(gen)); 793fc76b8acSEd Tanous if (gen.error()) 794fc76b8acSEd Tanous { 795fc76b8acSEd Tanous retry = 0; 796fc76b8acSEd Tanous break; 797fc76b8acSEd Tanous } 798b52664e2SAppaRao Puli auto inserted = subscriptionsMap.insert(std::pair(id, subValue)); 799b52664e2SAppaRao Puli if (inserted.second) 800b52664e2SAppaRao Puli { 801b52664e2SAppaRao Puli break; 802b52664e2SAppaRao Puli } 803b52664e2SAppaRao Puli --retry; 80423a21a1cSEd Tanous } 805b52664e2SAppaRao Puli 806b52664e2SAppaRao Puli if (retry <= 0) 807b52664e2SAppaRao Puli { 80862598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to generate random number"); 809abb93cddSEd Tanous return ""; 810b52664e2SAppaRao Puli } 811b52664e2SAppaRao Puli 8124b712a29SEd Tanous persistent_data::UserSubscription newSub(subValue->userSub); 813a14c9113SEd Tanous 81428afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 8154b712a29SEd Tanous .subscriptionsConfigMap.emplace(newSub.id, newSub); 81628afb49cSJunLin Chen 8177d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 8181bf712bcSAyushi Smriti 81983328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 82083328316SEd Tanous { 8212558979cSP Dheeraj Srujan Kumar if (redfishLogFilePosition != 0) 8227f4eb588SAppaRao Puli { 8232558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 8247f4eb588SAppaRao Puli } 82583328316SEd Tanous } 826fe44eb0bSAyushi Smriti // Update retry configuration. 827fe44eb0bSAyushi Smriti subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 828fe44eb0bSAyushi Smriti 8295e44e3d8SAppaRao Puli // Set Subscription ID for back trace 8305e44e3d8SAppaRao Puli subValue->setSubscriptionId(id); 831f80a87f2SEd Tanous 832f80a87f2SEd Tanous return id; 833f80a87f2SEd Tanous } 834f80a87f2SEd Tanous 835f80a87f2SEd Tanous std::string 836f80a87f2SEd Tanous addSSESubscription(const std::shared_ptr<Subscription>& subValue, 837f80a87f2SEd Tanous std::string_view lastEventId) 838f80a87f2SEd Tanous { 839f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 840f80a87f2SEd Tanous 841f80a87f2SEd Tanous if (!lastEventId.empty()) 842f80a87f2SEd Tanous { 843f80a87f2SEd Tanous BMCWEB_LOG_INFO("Attempting to find message for last id {}", 844f80a87f2SEd Tanous lastEventId); 845f80a87f2SEd Tanous boost::circular_buffer<Event>::iterator lastEvent = 846f80a87f2SEd Tanous std::find_if(messages.begin(), messages.end(), 847f80a87f2SEd Tanous [&lastEventId](const Event& event) { 848f80a87f2SEd Tanous return event.id == lastEventId; 849f80a87f2SEd Tanous }); 850f80a87f2SEd Tanous // Can't find a matching ID 851f80a87f2SEd Tanous if (lastEvent == messages.end()) 852f80a87f2SEd Tanous { 853f80a87f2SEd Tanous nlohmann::json msg = messages::eventBufferExceeded(); 854f80a87f2SEd Tanous // If the buffer overloaded, send all messages. 8556d799e14SEd Tanous subValue->sendEventToSubscriber(msg); 856f80a87f2SEd Tanous lastEvent = messages.begin(); 857f80a87f2SEd Tanous } 858f80a87f2SEd Tanous else 859f80a87f2SEd Tanous { 860f80a87f2SEd Tanous // Skip the last event the user already has 861f80a87f2SEd Tanous lastEvent++; 862f80a87f2SEd Tanous } 863f80a87f2SEd Tanous 864f80a87f2SEd Tanous for (boost::circular_buffer<Event>::const_iterator event = 865f80a87f2SEd Tanous lastEvent; 866f80a87f2SEd Tanous lastEvent != messages.end(); lastEvent++) 867f80a87f2SEd Tanous { 8686d799e14SEd Tanous subValue->sendEventToSubscriber(event->message); 869f80a87f2SEd Tanous } 870f80a87f2SEd Tanous } 871f80a87f2SEd Tanous return id; 872f80a87f2SEd Tanous } 873f80a87f2SEd Tanous 874f80a87f2SEd Tanous std::string 875f80a87f2SEd Tanous addPushSubscription(const std::shared_ptr<Subscription>& subValue) 876f80a87f2SEd Tanous { 877f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 878f80a87f2SEd Tanous 879f80a87f2SEd Tanous updateSubscriptionData(); 880b52664e2SAppaRao Puli return id; 881b52664e2SAppaRao Puli } 882b52664e2SAppaRao Puli 883b52664e2SAppaRao Puli bool isSubscriptionExist(const std::string& id) 884b52664e2SAppaRao Puli { 885b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 88655f79e6fSEd Tanous return obj != subscriptionsMap.end(); 887b52664e2SAppaRao Puli } 888b52664e2SAppaRao Puli 8894b712a29SEd Tanous bool deleteSubscription(const std::string& id) 890b52664e2SAppaRao Puli { 891b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 8924b712a29SEd Tanous if (obj == subscriptionsMap.end()) 893b52664e2SAppaRao Puli { 8944b712a29SEd Tanous BMCWEB_LOG_WARNING("Could not find subscription with id {}", id); 8954b712a29SEd Tanous return false; 8964b712a29SEd Tanous } 897b52664e2SAppaRao Puli subscriptionsMap.erase(obj); 8984b712a29SEd Tanous auto& event = persistent_data::EventServiceStore::getInstance(); 8994b712a29SEd Tanous auto persistentObj = event.subscriptionsConfigMap.find(id); 9004b712a29SEd Tanous if (persistentObj == event.subscriptionsConfigMap.end()) 9014b712a29SEd Tanous { 9024b712a29SEd Tanous BMCWEB_LOG_ERROR("Subscription wasn't in persistent data"); 9034b712a29SEd Tanous return true; 9044b712a29SEd Tanous } 90528afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 9064b712a29SEd Tanous .subscriptionsConfigMap.erase(persistentObj); 9077d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 908b52664e2SAppaRao Puli updateSubscriptionData(); 9094b712a29SEd Tanous 9104b712a29SEd Tanous return true; 911b52664e2SAppaRao Puli } 912b52664e2SAppaRao Puli 9135e44e3d8SAppaRao Puli void deleteSseSubscription(const crow::sse_socket::Connection& thisConn) 9145e44e3d8SAppaRao Puli { 915bdbfae2aSEd Tanous for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();) 9165e44e3d8SAppaRao Puli { 917bdbfae2aSEd Tanous std::shared_ptr<Subscription> entry = it->second; 9185e44e3d8SAppaRao Puli bool entryIsThisConn = entry->matchSseId(thisConn); 9195e44e3d8SAppaRao Puli if (entryIsThisConn) 9205e44e3d8SAppaRao Puli { 9215e44e3d8SAppaRao Puli persistent_data::EventServiceStore::getInstance() 9225e44e3d8SAppaRao Puli .subscriptionsConfigMap.erase( 923bdbfae2aSEd Tanous it->second->getSubscriptionId()); 924bdbfae2aSEd Tanous it = subscriptionsMap.erase(it); 9255e44e3d8SAppaRao Puli return; 9265e44e3d8SAppaRao Puli } 927bdbfae2aSEd Tanous it++; 9285e44e3d8SAppaRao Puli } 9295e44e3d8SAppaRao Puli } 9305e44e3d8SAppaRao Puli 9315e44e3d8SAppaRao Puli size_t getNumberOfSubscriptions() const 932b52664e2SAppaRao Puli { 933b52664e2SAppaRao Puli return subscriptionsMap.size(); 934b52664e2SAppaRao Puli } 935b52664e2SAppaRao Puli 9365e44e3d8SAppaRao Puli size_t getNumberOfSSESubscriptions() const 9375e44e3d8SAppaRao Puli { 9383544d2a7SEd Tanous auto size = std::ranges::count_if( 9393544d2a7SEd Tanous subscriptionsMap, 9405e44e3d8SAppaRao Puli [](const std::pair<std::string, std::shared_ptr<Subscription>>& 9415e44e3d8SAppaRao Puli entry) { 9424b712a29SEd Tanous return (entry.second->userSub.subscriptionType == 9434b712a29SEd Tanous subscriptionTypeSSE); 9445e44e3d8SAppaRao Puli }); 9455e44e3d8SAppaRao Puli return static_cast<size_t>(size); 9465e44e3d8SAppaRao Puli } 9475e44e3d8SAppaRao Puli 948b52664e2SAppaRao Puli std::vector<std::string> getAllIDs() 949b52664e2SAppaRao Puli { 950b52664e2SAppaRao Puli std::vector<std::string> idList; 951b52664e2SAppaRao Puli for (const auto& it : subscriptionsMap) 952b52664e2SAppaRao Puli { 953b52664e2SAppaRao Puli idList.emplace_back(it.first); 954b52664e2SAppaRao Puli } 955b52664e2SAppaRao Puli return idList; 956b52664e2SAppaRao Puli } 957b52664e2SAppaRao Puli 9586ba8c82eSsunharis_in bool sendTestEventLog() 9590b4bdd93SAppaRao Puli { 9605e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 9610b4bdd93SAppaRao Puli { 9620b4bdd93SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 9636ba8c82eSsunharis_in if (!entry->sendTestEventLog()) 9646ba8c82eSsunharis_in { 9656ba8c82eSsunharis_in return false; 9660b4bdd93SAppaRao Puli } 9670b4bdd93SAppaRao Puli } 9686ba8c82eSsunharis_in return true; 9696ba8c82eSsunharis_in } 970e9a14131SAppaRao Puli 971f80a87f2SEd Tanous void sendEvent(nlohmann::json::object_t eventMessage, 972f80a87f2SEd Tanous std::string_view origin, std::string_view resourceType) 97396330b99SSunitha Harish { 974613dabeaSEd Tanous eventMessage["EventId"] = eventId; 975f80a87f2SEd Tanous 976613dabeaSEd Tanous eventMessage["EventTimestamp"] = 977613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 978613dabeaSEd Tanous eventMessage["OriginOfCondition"] = origin; 979613dabeaSEd Tanous 980f80a87f2SEd Tanous // MemberId is 0 : since we are sending one event record. 981f80a87f2SEd Tanous eventMessage["MemberId"] = 0; 98296330b99SSunitha Harish 983f80a87f2SEd Tanous messages.push_back(Event(std::to_string(eventId), eventMessage)); 984f80a87f2SEd Tanous 985f80a87f2SEd Tanous for (auto& it : subscriptionsMap) 98696330b99SSunitha Harish { 987f80a87f2SEd Tanous std::shared_ptr<Subscription>& entry = it.second; 988d3a48a14SEd Tanous if (!eventMatchesFilter(entry->userSub, eventMessage, resourceType)) 98996330b99SSunitha Harish { 990f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 991f80a87f2SEd Tanous continue; 99296330b99SSunitha Harish } 993f80a87f2SEd Tanous 994f80a87f2SEd Tanous nlohmann::json::array_t eventRecord; 995f80a87f2SEd Tanous eventRecord.emplace_back(eventMessage); 996f80a87f2SEd Tanous 997613dabeaSEd Tanous nlohmann::json msgJson; 998613dabeaSEd Tanous 999613dabeaSEd Tanous msgJson["@odata.type"] = "#Event.v1_4_0.Event"; 1000613dabeaSEd Tanous msgJson["Name"] = "Event Log"; 1001613dabeaSEd Tanous msgJson["Id"] = eventId; 1002f80a87f2SEd Tanous msgJson["Events"] = std::move(eventRecord); 1003f52c03c1SCarson Labrado 1004f52c03c1SCarson Labrado std::string strMsg = msgJson.dump( 1005f52c03c1SCarson Labrado 2, ' ', true, nlohmann::json::error_handler_t::replace); 10066d799e14SEd Tanous entry->sendEventToSubscriber(std::move(strMsg)); 10078ece0e45SEd Tanous eventId++; // increment the eventId 100896330b99SSunitha Harish } 100996330b99SSunitha Harish } 101096330b99SSunitha Harish 10112558979cSP Dheeraj Srujan Kumar void resetRedfishFilePosition() 10127f4eb588SAppaRao Puli { 10132558979cSP Dheeraj Srujan Kumar // Control would be here when Redfish file is created. 10142558979cSP Dheeraj Srujan Kumar // Reset File Position as new file is created 10152558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = 0; 10162558979cSP Dheeraj Srujan Kumar } 10172558979cSP Dheeraj Srujan Kumar 10182558979cSP Dheeraj Srujan Kumar void cacheRedfishLogFile() 10192558979cSP Dheeraj Srujan Kumar { 10202558979cSP Dheeraj Srujan Kumar // Open the redfish file and read till the last record. 10212558979cSP Dheeraj Srujan Kumar 10227f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 10237f4eb588SAppaRao Puli if (!logStream.good()) 10247f4eb588SAppaRao Puli { 102562598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed "); 10267f4eb588SAppaRao Puli return; 10277f4eb588SAppaRao Puli } 10287f4eb588SAppaRao Puli std::string logEntry; 10297f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 10307f4eb588SAppaRao Puli { 10312558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 10327f4eb588SAppaRao Puli } 10337f4eb588SAppaRao Puli } 10347f4eb588SAppaRao Puli 10357f4eb588SAppaRao Puli void readEventLogsFromFile() 10367f4eb588SAppaRao Puli { 10377f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 10387f4eb588SAppaRao Puli if (!logStream.good()) 10397f4eb588SAppaRao Puli { 104062598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed"); 10417f4eb588SAppaRao Puli return; 10427f4eb588SAppaRao Puli } 10437f4eb588SAppaRao Puli 10447f4eb588SAppaRao Puli std::vector<EventLogObjectsType> eventRecords; 10457f4eb588SAppaRao Puli 10467f4eb588SAppaRao Puli std::string logEntry; 10472558979cSP Dheeraj Srujan Kumar 104803d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: seek to {}", 104903d4d37cSAlexander Hansen static_cast<int>(redfishLogFilePosition)); 105003d4d37cSAlexander Hansen 10512558979cSP Dheeraj Srujan Kumar // Get the read pointer to the next log to be read. 10522558979cSP Dheeraj Srujan Kumar logStream.seekg(redfishLogFilePosition); 10532558979cSP Dheeraj Srujan Kumar 10547f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 10557f4eb588SAppaRao Puli { 105603d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry"); 10572558979cSP Dheeraj Srujan Kumar // Update Pointer position 10582558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 10592558979cSP Dheeraj Srujan Kumar 10602558979cSP Dheeraj Srujan Kumar std::string idStr; 10612558979cSP Dheeraj Srujan Kumar if (!event_log::getUniqueEntryID(logEntry, idStr)) 10627f4eb588SAppaRao Puli { 106303d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 106403d4d37cSAlexander Hansen "Redfish log file: could not get unique entry id for {}", 106503d4d37cSAlexander Hansen logEntry); 10667f4eb588SAppaRao Puli continue; 10677f4eb588SAppaRao Puli } 10687f4eb588SAppaRao Puli 1069e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 10707f4eb588SAppaRao Puli { 10712558979cSP Dheeraj Srujan Kumar // If Service is not enabled, no need to compute 10722558979cSP Dheeraj Srujan Kumar // the remaining items below. 10732558979cSP Dheeraj Srujan Kumar // But, Loop must continue to keep track of Timestamp 107403d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 107503d4d37cSAlexander Hansen "Redfish log file: no subscribers / event service not enabled"); 10767f4eb588SAppaRao Puli continue; 10777f4eb588SAppaRao Puli } 10787f4eb588SAppaRao Puli 10797f4eb588SAppaRao Puli std::string timestamp; 10807f4eb588SAppaRao Puli std::string messageID; 10815e715de6SAppaRao Puli std::vector<std::string> messageArgs; 10827f4eb588SAppaRao Puli if (event_log::getEventLogParams(logEntry, timestamp, messageID, 10837f4eb588SAppaRao Puli messageArgs) != 0) 10847f4eb588SAppaRao Puli { 108503d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}", 108603d4d37cSAlexander Hansen logEntry); 10877f4eb588SAppaRao Puli continue; 10887f4eb588SAppaRao Puli } 10897f4eb588SAppaRao Puli 1090f80a87f2SEd Tanous eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs); 10917f4eb588SAppaRao Puli } 10927f4eb588SAppaRao Puli 1093e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 10942558979cSP Dheeraj Srujan Kumar { 109562598e31SEd Tanous BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions."); 10962558979cSP Dheeraj Srujan Kumar return; 10972558979cSP Dheeraj Srujan Kumar } 10982558979cSP Dheeraj Srujan Kumar 10992558979cSP Dheeraj Srujan Kumar if (eventRecords.empty()) 11002558979cSP Dheeraj Srujan Kumar { 11012558979cSP Dheeraj Srujan Kumar // No Records to send 110262598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 11032558979cSP Dheeraj Srujan Kumar return; 11042558979cSP Dheeraj Srujan Kumar } 11052558979cSP Dheeraj Srujan Kumar 11065e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 11077f4eb588SAppaRao Puli { 11087f4eb588SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 11094b712a29SEd Tanous if (entry->userSub.eventFormatType == "Event") 11107f4eb588SAppaRao Puli { 11117f4eb588SAppaRao Puli entry->filterAndSendEventLogs(eventRecords); 11127f4eb588SAppaRao Puli } 11137f4eb588SAppaRao Puli } 11147f4eb588SAppaRao Puli } 11157f4eb588SAppaRao Puli 11167f4eb588SAppaRao Puli static void watchRedfishEventLogFile() 11177f4eb588SAppaRao Puli { 11186a9f85f9SAppaRao Puli if (!inotifyConn) 11197f4eb588SAppaRao Puli { 112003d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("inotify Connection is not present"); 11217f4eb588SAppaRao Puli return; 11227f4eb588SAppaRao Puli } 11237f4eb588SAppaRao Puli 11247f4eb588SAppaRao Puli static std::array<char, 1024> readBuffer; 11257f4eb588SAppaRao Puli 1126bd79bce8SPatrick Williams inotifyConn->async_read_some( 1127bd79bce8SPatrick Williams boost::asio::buffer(readBuffer), 11287f4eb588SAppaRao Puli [&](const boost::system::error_code& ec, 11297f4eb588SAppaRao Puli const std::size_t& bytesTransferred) { 11309ed3f90aSEd Tanous if (ec == boost::asio::error::operation_aborted) 11319ed3f90aSEd Tanous { 11329ed3f90aSEd Tanous BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)"); 11339ed3f90aSEd Tanous return; 11349ed3f90aSEd Tanous } 11357f4eb588SAppaRao Puli if (ec) 11367f4eb588SAppaRao Puli { 113762598e31SEd Tanous BMCWEB_LOG_ERROR("Callback Error: {}", ec.message()); 11387f4eb588SAppaRao Puli return; 11397f4eb588SAppaRao Puli } 114003d4d37cSAlexander Hansen 114103d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred); 114203d4d37cSAlexander Hansen 11437f4eb588SAppaRao Puli std::size_t index = 0; 1144b792cc56SAppaRao Puli while ((index + iEventSize) <= bytesTransferred) 11457f4eb588SAppaRao Puli { 1146d3a9e084SEd Tanous struct inotify_event event 1147d3a9e084SEd Tanous {}; 1148b792cc56SAppaRao Puli std::memcpy(&event, &readBuffer[index], iEventSize); 1149b792cc56SAppaRao Puli if (event.wd == dirWatchDesc) 1150b792cc56SAppaRao Puli { 1151b792cc56SAppaRao Puli if ((event.len == 0) || 1152b792cc56SAppaRao Puli (index + iEventSize + event.len > bytesTransferred)) 1153b792cc56SAppaRao Puli { 1154b792cc56SAppaRao Puli index += (iEventSize + event.len); 1155b792cc56SAppaRao Puli continue; 1156b792cc56SAppaRao Puli } 1157b792cc56SAppaRao Puli 11584f568f74SJiaqing Zhao std::string fileName(&readBuffer[index + iEventSize]); 11594f568f74SJiaqing Zhao if (fileName != "redfish") 1160b792cc56SAppaRao Puli { 1161b792cc56SAppaRao Puli index += (iEventSize + event.len); 1162b792cc56SAppaRao Puli continue; 1163b792cc56SAppaRao Puli } 1164b792cc56SAppaRao Puli 116562598e31SEd Tanous BMCWEB_LOG_DEBUG( 116662598e31SEd Tanous "Redfish log file created/deleted. event.name: {}", 116762598e31SEd Tanous fileName); 1168b792cc56SAppaRao Puli if (event.mask == IN_CREATE) 1169b792cc56SAppaRao Puli { 1170b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1171b792cc56SAppaRao Puli { 117262598e31SEd Tanous BMCWEB_LOG_DEBUG( 117362598e31SEd Tanous "Remove and Add inotify watcher on " 117462598e31SEd Tanous "redfish event log file"); 1175016761afSAppaRao Puli // Remove existing inotify watcher and add 1176016761afSAppaRao Puli // with new redfish event log file. 1177016761afSAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1178016761afSAppaRao Puli fileWatchDesc = -1; 1179b792cc56SAppaRao Puli } 1180b792cc56SAppaRao Puli 1181b792cc56SAppaRao Puli fileWatchDesc = inotify_add_watch( 1182b792cc56SAppaRao Puli inotifyFd, redfishEventLogFile, IN_MODIFY); 1183b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1184b792cc56SAppaRao Puli { 118562598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for " 118662598e31SEd Tanous "redfish log file."); 1187b792cc56SAppaRao Puli return; 1188b792cc56SAppaRao Puli } 1189b792cc56SAppaRao Puli 1190b792cc56SAppaRao Puli EventServiceManager::getInstance() 11912558979cSP Dheeraj Srujan Kumar .resetRedfishFilePosition(); 1192b792cc56SAppaRao Puli EventServiceManager::getInstance() 1193b792cc56SAppaRao Puli .readEventLogsFromFile(); 1194b792cc56SAppaRao Puli } 1195b792cc56SAppaRao Puli else if ((event.mask == IN_DELETE) || 1196b792cc56SAppaRao Puli (event.mask == IN_MOVED_TO)) 1197b792cc56SAppaRao Puli { 1198b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1199b792cc56SAppaRao Puli { 1200b792cc56SAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1201b792cc56SAppaRao Puli fileWatchDesc = -1; 1202b792cc56SAppaRao Puli } 1203b792cc56SAppaRao Puli } 1204b792cc56SAppaRao Puli } 1205b792cc56SAppaRao Puli else if (event.wd == fileWatchDesc) 1206b792cc56SAppaRao Puli { 1207b792cc56SAppaRao Puli if (event.mask == IN_MODIFY) 12087f4eb588SAppaRao Puli { 12097f4eb588SAppaRao Puli EventServiceManager::getInstance() 12107f4eb588SAppaRao Puli .readEventLogsFromFile(); 12117f4eb588SAppaRao Puli } 1212b792cc56SAppaRao Puli } 1213b792cc56SAppaRao Puli index += (iEventSize + event.len); 12147f4eb588SAppaRao Puli } 12157f4eb588SAppaRao Puli 12167f4eb588SAppaRao Puli watchRedfishEventLogFile(); 12177f4eb588SAppaRao Puli }); 12187f4eb588SAppaRao Puli } 12197f4eb588SAppaRao Puli 12207f4eb588SAppaRao Puli static int startEventLogMonitor(boost::asio::io_context& ioc) 12217f4eb588SAppaRao Puli { 122203d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 122303d4d37cSAlexander Hansen 122423a21a1cSEd Tanous inotifyConn.emplace(ioc); 1225b792cc56SAppaRao Puli inotifyFd = inotify_init1(IN_NONBLOCK); 1226b792cc56SAppaRao Puli if (inotifyFd == -1) 12277f4eb588SAppaRao Puli { 122862598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_init1 failed."); 12297f4eb588SAppaRao Puli return -1; 12307f4eb588SAppaRao Puli } 1231b792cc56SAppaRao Puli 1232b792cc56SAppaRao Puli // Add watch on directory to handle redfish event log file 1233b792cc56SAppaRao Puli // create/delete. 1234b792cc56SAppaRao Puli dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 1235b792cc56SAppaRao Puli IN_CREATE | IN_MOVED_TO | IN_DELETE); 1236b792cc56SAppaRao Puli if (dirWatchDesc == -1) 12377f4eb588SAppaRao Puli { 123862598e31SEd Tanous BMCWEB_LOG_ERROR( 123962598e31SEd Tanous "inotify_add_watch failed for event log directory."); 12407f4eb588SAppaRao Puli return -1; 12417f4eb588SAppaRao Puli } 12427f4eb588SAppaRao Puli 1243b792cc56SAppaRao Puli // Watch redfish event log file for modifications. 1244bd79bce8SPatrick Williams fileWatchDesc = 1245bd79bce8SPatrick Williams inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 1246b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1247b792cc56SAppaRao Puli { 124862598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 1249b792cc56SAppaRao Puli // Don't return error if file not exist. 1250b792cc56SAppaRao Puli // Watch on directory will handle create/delete of file. 1251b792cc56SAppaRao Puli } 1252b792cc56SAppaRao Puli 12537f4eb588SAppaRao Puli // monitor redfish event log file 1254b792cc56SAppaRao Puli inotifyConn->assign(inotifyFd); 12557f4eb588SAppaRao Puli watchRedfishEventLogFile(); 12567f4eb588SAppaRao Puli 12577f4eb588SAppaRao Puli return 0; 12587f4eb588SAppaRao Puli } 12597f4eb588SAppaRao Puli 12609ed3f90aSEd Tanous static void stopEventLogMonitor() 12619ed3f90aSEd Tanous { 12629ed3f90aSEd Tanous inotifyConn.reset(); 12639ed3f90aSEd Tanous } 12649ed3f90aSEd Tanous 126559d494eeSPatrick Williams static void getReadingsForReport(sdbusplus::message_t& msg) 1266156d6b00SAppaRao Puli { 126756d2396dSEd Tanous if (msg.is_method_error()) 126856d2396dSEd Tanous { 126962598e31SEd Tanous BMCWEB_LOG_ERROR("TelemetryMonitor Signal error"); 127056d2396dSEd Tanous return; 127156d2396dSEd Tanous } 127256d2396dSEd Tanous 1273c0353249SWludzik, Jozef sdbusplus::message::object_path path(msg.get_path()); 1274c0353249SWludzik, Jozef std::string id = path.filename(); 1275c0353249SWludzik, Jozef if (id.empty()) 1276156d6b00SAppaRao Puli { 127762598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to get Id from path"); 1278156d6b00SAppaRao Puli return; 1279156d6b00SAppaRao Puli } 1280156d6b00SAppaRao Puli 1281c0353249SWludzik, Jozef std::string interface; 1282b9d36b47SEd Tanous dbus::utility::DBusPropertiesMap props; 1283c0353249SWludzik, Jozef std::vector<std::string> invalidProps; 1284c0353249SWludzik, Jozef msg.read(interface, props, invalidProps); 1285c0353249SWludzik, Jozef 1286bd79bce8SPatrick Williams auto found = std::ranges::find_if(props, [](const auto& x) { 1287bd79bce8SPatrick Williams return x.first == "Readings"; 1288bd79bce8SPatrick Williams }); 1289c0353249SWludzik, Jozef if (found == props.end()) 1290156d6b00SAppaRao Puli { 129162598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 1292156d6b00SAppaRao Puli return; 1293156d6b00SAppaRao Puli } 1294156d6b00SAppaRao Puli 12951e1e598dSJonathan Doman const telemetry::TimestampReadings* readings = 12961e1e598dSJonathan Doman std::get_if<telemetry::TimestampReadings>(&found->second); 1297e662eae8SEd Tanous if (readings == nullptr) 12981e1e598dSJonathan Doman { 129962598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 13001e1e598dSJonathan Doman return; 13011e1e598dSJonathan Doman } 13021e1e598dSJonathan Doman 1303156d6b00SAppaRao Puli for (const auto& it : 1304156d6b00SAppaRao Puli EventServiceManager::getInstance().subscriptionsMap) 1305156d6b00SAppaRao Puli { 1306e05aec50SEd Tanous Subscription& entry = *it.second; 13074b712a29SEd Tanous if (entry.userSub.eventFormatType == metricReportFormatType) 1308156d6b00SAppaRao Puli { 13091e1e598dSJonathan Doman entry.filterAndSendReports(id, *readings); 1310156d6b00SAppaRao Puli } 1311156d6b00SAppaRao Puli } 1312156d6b00SAppaRao Puli } 1313156d6b00SAppaRao Puli 1314156d6b00SAppaRao Puli void unregisterMetricReportSignal() 1315156d6b00SAppaRao Puli { 13167d1cc387SAppaRao Puli if (matchTelemetryMonitor) 13177d1cc387SAppaRao Puli { 131862598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Unregister"); 1319156d6b00SAppaRao Puli matchTelemetryMonitor.reset(); 1320156d6b00SAppaRao Puli matchTelemetryMonitor = nullptr; 1321156d6b00SAppaRao Puli } 13227d1cc387SAppaRao Puli } 1323156d6b00SAppaRao Puli 1324156d6b00SAppaRao Puli void registerMetricReportSignal() 1325156d6b00SAppaRao Puli { 13267d1cc387SAppaRao Puli if (!serviceEnabled || matchTelemetryMonitor) 1327156d6b00SAppaRao Puli { 132862598e31SEd Tanous BMCWEB_LOG_DEBUG("Not registering metric report signal."); 1329156d6b00SAppaRao Puli return; 1330156d6b00SAppaRao Puli } 1331156d6b00SAppaRao Puli 133262598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Register"); 1333c0353249SWludzik, Jozef std::string matchStr = "type='signal',member='PropertiesChanged'," 1334c0353249SWludzik, Jozef "interface='org.freedesktop.DBus.Properties'," 1335c0353249SWludzik, Jozef "arg0=xyz.openbmc_project.Telemetry.Report"; 1336156d6b00SAppaRao Puli 133759d494eeSPatrick Williams matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>( 133856d2396dSEd Tanous *crow::connections::systemBus, matchStr, getReadingsForReport); 1339156d6b00SAppaRao Puli } 134023a21a1cSEd Tanous }; 1341b52664e2SAppaRao Puli 1342b52664e2SAppaRao Puli } // namespace redfish 1343