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" 21c0353249SWludzik, Jozef #include "metric_report.hpp" 222c6ffdb0SEd Tanous #include "ossl_random.hpp" 233ccb3adbSEd Tanous #include "persistent_data.hpp" 247f4eb588SAppaRao Puli #include "registries.hpp" 258dab0f58SEd Tanous #include "registries_selector.hpp" 2650ebd4afSEd Tanous #include "str_utility.hpp" 27*02c1e29fSAlexander Hansen #include "subscription.hpp" 285b90429aSEd Tanous #include "utils/time_utils.hpp" 297f4eb588SAppaRao Puli 307f4eb588SAppaRao Puli #include <sys/inotify.h> 31b52664e2SAppaRao Puli 32fb4fd5d4SZhenfei Tai #include <boost/asio/io_context.hpp> 33f80a87f2SEd Tanous #include <boost/circular_buffer.hpp> 34b52664e2SAppaRao Puli #include <boost/container/flat_map.hpp> 35ef4c65b7SEd Tanous #include <boost/url/format.hpp> 364a7fbefdSEd Tanous #include <boost/url/url_view_base.hpp> 37b5b40605Snitroglycerine #include <sdbusplus/bus/match.hpp> 381214b7e7SGunnar Mills 395e44e3d8SAppaRao Puli #include <algorithm> 40b52664e2SAppaRao Puli #include <cstdlib> 41b52664e2SAppaRao Puli #include <ctime> 42a14c9113SEd Tanous #include <format> 431bf712bcSAyushi Smriti #include <fstream> 44b52664e2SAppaRao Puli #include <memory> 4526702d01SEd Tanous #include <span> 46a14c9113SEd Tanous #include <string> 4756ba386dSMyung Bae #include <string_view> 485fe4ef35SMyung Bae #include <utility> 49b52664e2SAppaRao Puli 50b52664e2SAppaRao Puli namespace redfish 51b52664e2SAppaRao Puli { 52156d6b00SAppaRao Puli 53156d6b00SAppaRao Puli static constexpr const char* eventFormatType = "Event"; 54156d6b00SAppaRao Puli static constexpr const char* metricReportFormatType = "MetricReport"; 55156d6b00SAppaRao Puli 561bf712bcSAyushi Smriti static constexpr const char* eventServiceFile = 571bf712bcSAyushi Smriti "/var/lib/bmcweb/eventservice_config.json"; 581bf712bcSAyushi Smriti 59cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 604642bf8fSGeorge Liu static std::optional<boost::asio::posix::stream_descriptor> inotifyConn; 614642bf8fSGeorge Liu static constexpr const char* redfishEventLogDir = "/var/log"; 624642bf8fSGeorge Liu static constexpr const char* redfishEventLogFile = "/var/log/redfish"; 634642bf8fSGeorge Liu static constexpr const size_t iEventSize = sizeof(inotify_event); 64cf9e417dSEd Tanous 65cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 664642bf8fSGeorge Liu static int inotifyFd = -1; 67cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 684642bf8fSGeorge Liu static int dirWatchDesc = -1; 69cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 704642bf8fSGeorge Liu static int fileWatchDesc = -1; 714642bf8fSGeorge Liu 72fffb8c1fSEd Tanous namespace registries 734642bf8fSGeorge Liu { 747f4eb588SAppaRao Puli static const Message* 757f4eb588SAppaRao Puli getMsgFromRegistry(const std::string& messageKey, 7626702d01SEd Tanous const std::span<const MessageEntry>& registry) 777f4eb588SAppaRao Puli { 783544d2a7SEd Tanous std::span<const MessageEntry>::iterator messageIt = std::ranges::find_if( 793544d2a7SEd Tanous registry, [&messageKey](const MessageEntry& messageEntry) { 8055f79e6fSEd Tanous return messageKey == messageEntry.first; 817f4eb588SAppaRao Puli }); 8226702d01SEd Tanous if (messageIt != registry.end()) 837f4eb588SAppaRao Puli { 847f4eb588SAppaRao Puli return &messageIt->second; 857f4eb588SAppaRao Puli } 867f4eb588SAppaRao Puli 877f4eb588SAppaRao Puli return nullptr; 887f4eb588SAppaRao Puli } 897f4eb588SAppaRao Puli 9026ccae32SEd Tanous static const Message* formatMessage(std::string_view messageID) 917f4eb588SAppaRao Puli { 927f4eb588SAppaRao Puli // Redfish MessageIds are in the form 937f4eb588SAppaRao Puli // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 947f4eb588SAppaRao Puli // the right Message 957f4eb588SAppaRao Puli std::vector<std::string> fields; 967f4eb588SAppaRao Puli fields.reserve(4); 9750ebd4afSEd Tanous 9850ebd4afSEd Tanous bmcweb::split(fields, messageID, '.'); 997f4eb588SAppaRao Puli if (fields.size() != 4) 1007f4eb588SAppaRao Puli { 1017f4eb588SAppaRao Puli return nullptr; 1027f4eb588SAppaRao Puli } 10302cad96eSEd Tanous const std::string& registryName = fields[0]; 10402cad96eSEd Tanous const std::string& messageKey = fields[3]; 1057f4eb588SAppaRao Puli 1067f4eb588SAppaRao Puli // Find the right registry and check it for the MessageKey 107b304bd79SP Dheeraj Srujan Kumar return getMsgFromRegistry(messageKey, getRegistryFromPrefix(registryName)); 1087f4eb588SAppaRao Puli } 109fffb8c1fSEd Tanous } // namespace registries 1107f4eb588SAppaRao Puli 1117f4eb588SAppaRao Puli namespace event_log 1127f4eb588SAppaRao Puli { 1132558979cSP Dheeraj Srujan Kumar inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) 1147f4eb588SAppaRao Puli { 1157f4eb588SAppaRao Puli static time_t prevTs = 0; 1167f4eb588SAppaRao Puli static int index = 0; 1177f4eb588SAppaRao Puli 1187f4eb588SAppaRao Puli // Get the entry timestamp 1197f4eb588SAppaRao Puli std::time_t curTs = 0; 1207f4eb588SAppaRao Puli std::tm timeStruct = {}; 1217f4eb588SAppaRao Puli std::istringstream entryStream(logEntry); 1227f4eb588SAppaRao Puli if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 1237f4eb588SAppaRao Puli { 1247f4eb588SAppaRao Puli curTs = std::mktime(&timeStruct); 1257f4eb588SAppaRao Puli if (curTs == -1) 1267f4eb588SAppaRao Puli { 1277f4eb588SAppaRao Puli return false; 1287f4eb588SAppaRao Puli } 1297f4eb588SAppaRao Puli } 1307f4eb588SAppaRao Puli // If the timestamp isn't unique, increment the index 1317f4eb588SAppaRao Puli index = (curTs == prevTs) ? index + 1 : 0; 1327f4eb588SAppaRao Puli 1337f4eb588SAppaRao Puli // Save the timestamp 1347f4eb588SAppaRao Puli prevTs = curTs; 1357f4eb588SAppaRao Puli 1367f4eb588SAppaRao Puli entryID = std::to_string(curTs); 1377f4eb588SAppaRao Puli if (index > 0) 1387f4eb588SAppaRao Puli { 1397f4eb588SAppaRao Puli entryID += "_" + std::to_string(index); 1407f4eb588SAppaRao Puli } 1417f4eb588SAppaRao Puli return true; 1427f4eb588SAppaRao Puli } 1437f4eb588SAppaRao Puli 14423a21a1cSEd Tanous inline int getEventLogParams(const std::string& logEntry, 14523a21a1cSEd Tanous std::string& timestamp, std::string& messageID, 1465e715de6SAppaRao Puli std::vector<std::string>& messageArgs) 1477f4eb588SAppaRao Puli { 1487f4eb588SAppaRao Puli // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 1497f4eb588SAppaRao Puli // First get the Timestamp 150f23b7296SEd Tanous size_t space = logEntry.find_first_of(' '); 1517f4eb588SAppaRao Puli if (space == std::string::npos) 1527f4eb588SAppaRao Puli { 15303d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", 15403d4d37cSAlexander Hansen logEntry); 1557f4eb588SAppaRao Puli return -EINVAL; 1567f4eb588SAppaRao Puli } 1577f4eb588SAppaRao Puli timestamp = logEntry.substr(0, space); 1587f4eb588SAppaRao Puli // Then get the log contents 159f23b7296SEd Tanous size_t entryStart = logEntry.find_first_not_of(' ', space); 1607f4eb588SAppaRao Puli if (entryStart == std::string::npos) 1617f4eb588SAppaRao Puli { 16203d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", 16303d4d37cSAlexander Hansen logEntry); 1647f4eb588SAppaRao Puli return -EINVAL; 1657f4eb588SAppaRao Puli } 1667f4eb588SAppaRao Puli std::string_view entry(logEntry); 1677f4eb588SAppaRao Puli entry.remove_prefix(entryStart); 1687f4eb588SAppaRao Puli // Use split to separate the entry into its fields 1697f4eb588SAppaRao Puli std::vector<std::string> logEntryFields; 17050ebd4afSEd Tanous bmcweb::split(logEntryFields, entry, ','); 1717f4eb588SAppaRao Puli // We need at least a MessageId to be valid 17226f6976fSEd Tanous if (logEntryFields.empty()) 1737f4eb588SAppaRao Puli { 17403d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", 17503d4d37cSAlexander Hansen logEntry); 1767f4eb588SAppaRao Puli return -EINVAL; 1777f4eb588SAppaRao Puli } 1787f4eb588SAppaRao Puli messageID = logEntryFields[0]; 1797f4eb588SAppaRao Puli 1807f4eb588SAppaRao Puli // Get the MessageArgs from the log if there are any 1817f4eb588SAppaRao Puli if (logEntryFields.size() > 1) 1827f4eb588SAppaRao Puli { 18302cad96eSEd Tanous const std::string& messageArgsStart = logEntryFields[1]; 1847f4eb588SAppaRao Puli // If the first string is empty, assume there are no MessageArgs 1857f4eb588SAppaRao Puli if (!messageArgsStart.empty()) 1867f4eb588SAppaRao Puli { 1875e715de6SAppaRao Puli messageArgs.assign(logEntryFields.begin() + 1, 1885e715de6SAppaRao Puli logEntryFields.end()); 1897f4eb588SAppaRao Puli } 1907f4eb588SAppaRao Puli } 1917f4eb588SAppaRao Puli 1927f4eb588SAppaRao Puli return 0; 1937f4eb588SAppaRao Puli } 1947f4eb588SAppaRao Puli 195bd79bce8SPatrick Williams inline int formatEventLogEntry( 196bd79bce8SPatrick Williams const std::string& logEntryID, const std::string& messageID, 197bd79bce8SPatrick Williams const std::span<std::string_view> messageArgs, std::string timestamp, 198bd79bce8SPatrick Williams const std::string& customText, nlohmann::json::object_t& logEntryJson) 1997f4eb588SAppaRao Puli { 2007f4eb588SAppaRao Puli // Get the Message from the MessageRegistry 201fffb8c1fSEd Tanous const registries::Message* message = registries::formatMessage(messageID); 2027f4eb588SAppaRao Puli 20380f595e7SEd Tanous if (message == nullptr) 2047f4eb588SAppaRao Puli { 20580f595e7SEd Tanous return -1; 2067f4eb588SAppaRao Puli } 2077f4eb588SAppaRao Puli 208bd79bce8SPatrick Williams std::string msg = 209bd79bce8SPatrick Williams redfish::registries::fillMessageArgs(messageArgs, message->message); 21080f595e7SEd Tanous if (msg.empty()) 21180f595e7SEd Tanous { 21280f595e7SEd Tanous return -1; 21380f595e7SEd Tanous } 2147f4eb588SAppaRao Puli 2157f4eb588SAppaRao Puli // Get the Created time from the timestamp. The log timestamp is in 2167f4eb588SAppaRao Puli // RFC3339 format which matches the Redfish format except for the 2177f4eb588SAppaRao Puli // fractional seconds between the '.' and the '+', so just remove them. 218f23b7296SEd Tanous std::size_t dot = timestamp.find_first_of('.'); 219b2f7609bSEd Tanous std::size_t plus = timestamp.find_first_of('+', dot); 2207f4eb588SAppaRao Puli if (dot != std::string::npos && plus != std::string::npos) 2217f4eb588SAppaRao Puli { 2227f4eb588SAppaRao Puli timestamp.erase(dot, plus - dot); 2237f4eb588SAppaRao Puli } 2247f4eb588SAppaRao Puli 2257f4eb588SAppaRao Puli // Fill in the log entry with the gathered data 2261476687dSEd Tanous logEntryJson["EventId"] = logEntryID; 227539d8c6bSEd Tanous 22880f595e7SEd Tanous logEntryJson["Severity"] = message->messageSeverity; 2291476687dSEd Tanous logEntryJson["Message"] = std::move(msg); 2301476687dSEd Tanous logEntryJson["MessageId"] = messageID; 2311476687dSEd Tanous logEntryJson["MessageArgs"] = messageArgs; 2321476687dSEd Tanous logEntryJson["EventTimestamp"] = std::move(timestamp); 2331476687dSEd Tanous logEntryJson["Context"] = customText; 2347f4eb588SAppaRao Puli return 0; 2357f4eb588SAppaRao Puli } 2367f4eb588SAppaRao Puli 2377f4eb588SAppaRao Puli } // namespace event_log 2387f4eb588SAppaRao Puli 239b52664e2SAppaRao Puli class EventServiceManager 240b52664e2SAppaRao Puli { 241b52664e2SAppaRao Puli private: 242d3a9e084SEd Tanous bool serviceEnabled = false; 243d3a9e084SEd Tanous uint32_t retryAttempts = 0; 244d3a9e084SEd Tanous uint32_t retryTimeoutInterval = 0; 2457d1cc387SAppaRao Puli 2462558979cSP Dheeraj Srujan Kumar std::streampos redfishLogFilePosition{0}; 2479f616dd1SEd Tanous size_t noOfEventLogSubscribers{0}; 2489f616dd1SEd Tanous size_t noOfMetricReportSubscribers{0}; 24959d494eeSPatrick Williams std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor; 250b52664e2SAppaRao Puli boost::container::flat_map<std::string, std::shared_ptr<Subscription>> 251b52664e2SAppaRao Puli subscriptionsMap; 252b52664e2SAppaRao Puli 2539f616dd1SEd Tanous uint64_t eventId{1}; 25496330b99SSunitha Harish 255f80a87f2SEd Tanous struct Event 256f80a87f2SEd Tanous { 257f80a87f2SEd Tanous std::string id; 258f80a87f2SEd Tanous nlohmann::json message; 259f80a87f2SEd Tanous }; 260f80a87f2SEd Tanous 261f80a87f2SEd Tanous constexpr static size_t maxMessages = 200; 262f80a87f2SEd Tanous boost::circular_buffer<Event> messages{maxMessages}; 263f80a87f2SEd Tanous 264f8ca6d79SEd Tanous boost::asio::io_context& ioc; 265f8ca6d79SEd Tanous 266b52664e2SAppaRao Puli public: 2679f616dd1SEd Tanous EventServiceManager(const EventServiceManager&) = delete; 2689f616dd1SEd Tanous EventServiceManager& operator=(const EventServiceManager&) = delete; 2699f616dd1SEd Tanous EventServiceManager(EventServiceManager&&) = delete; 2709f616dd1SEd Tanous EventServiceManager& operator=(EventServiceManager&&) = delete; 271ecd6a3a2SEd Tanous ~EventServiceManager() = default; 2729f616dd1SEd Tanous 273f8ca6d79SEd Tanous explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn) 274b52664e2SAppaRao Puli { 275f8ca6d79SEd Tanous // Load config from persist store. 276f8ca6d79SEd Tanous initConfig(); 277f8ca6d79SEd Tanous } 278f8ca6d79SEd Tanous 279f8ca6d79SEd Tanous static EventServiceManager& 280f8ca6d79SEd Tanous getInstance(boost::asio::io_context* ioc = nullptr) 281f8ca6d79SEd Tanous { 282f8ca6d79SEd Tanous static EventServiceManager handler(*ioc); 283b52664e2SAppaRao Puli return handler; 284b52664e2SAppaRao Puli } 285b52664e2SAppaRao Puli 2861bf712bcSAyushi Smriti void initConfig() 2871bf712bcSAyushi Smriti { 28828afb49cSJunLin Chen loadOldBehavior(); 2891bf712bcSAyushi Smriti 29028afb49cSJunLin Chen persistent_data::EventServiceConfig eventServiceConfig = 29128afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 29228afb49cSJunLin Chen .getEventServiceConfig(); 2931bf712bcSAyushi Smriti 29428afb49cSJunLin Chen serviceEnabled = eventServiceConfig.enabled; 29528afb49cSJunLin Chen retryAttempts = eventServiceConfig.retryAttempts; 29628afb49cSJunLin Chen retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval; 2971bf712bcSAyushi Smriti 29828afb49cSJunLin Chen for (const auto& it : persistent_data::EventServiceStore::getInstance() 29928afb49cSJunLin Chen .subscriptionsConfigMap) 3001bf712bcSAyushi Smriti { 3015fe4ef35SMyung Bae std::shared_ptr<persistent_data::UserSubscription> newSub = 3025fe4ef35SMyung Bae it.second; 3034bbf237fSAppaRao Puli 3046fd29553SEd Tanous boost::system::result<boost::urls::url> url = 3055fe4ef35SMyung Bae boost::urls::parse_absolute_uri(newSub->destinationUrl); 3061bf712bcSAyushi Smriti 307a716aa74SEd Tanous if (!url) 3081bf712bcSAyushi Smriti { 30962598e31SEd Tanous BMCWEB_LOG_ERROR( 31062598e31SEd Tanous "Failed to validate and split destination url"); 3111bf712bcSAyushi Smriti continue; 3121bf712bcSAyushi Smriti } 3131bf712bcSAyushi Smriti std::shared_ptr<Subscription> subValue = 31421a94d5cSMyung Bae std::make_shared<Subscription>(newSub, *url, ioc); 3155fe4ef35SMyung Bae std::string id = subValue->userSub->id; 316a0969c70SMyung Bae subValue->deleter = [id]() { 317a0969c70SMyung Bae EventServiceManager::getInstance().deleteSubscription(id); 318a0969c70SMyung Bae }; 3191bf712bcSAyushi Smriti 320a0969c70SMyung Bae subscriptionsMap.emplace(id, subValue); 32128afb49cSJunLin Chen 32228afb49cSJunLin Chen updateNoOfSubscribersCount(); 32328afb49cSJunLin Chen 32483328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 32583328316SEd Tanous { 3262558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 32783328316SEd Tanous } 3282558979cSP Dheeraj Srujan Kumar 32928afb49cSJunLin Chen // Update retry configuration. 33028afb49cSJunLin Chen subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 3311bf712bcSAyushi Smriti } 3321bf712bcSAyushi Smriti } 3331bf712bcSAyushi Smriti 33456d2396dSEd Tanous static void loadOldBehavior() 335b52664e2SAppaRao Puli { 33628afb49cSJunLin Chen std::ifstream eventConfigFile(eventServiceFile); 33728afb49cSJunLin Chen if (!eventConfigFile.good()) 3381bf712bcSAyushi Smriti { 33962598e31SEd Tanous BMCWEB_LOG_DEBUG("Old eventService config not exist"); 34028afb49cSJunLin Chen return; 34128afb49cSJunLin Chen } 34228afb49cSJunLin Chen auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false); 34328afb49cSJunLin Chen if (jsonData.is_discarded()) 3444bbf237fSAppaRao Puli { 34562598e31SEd Tanous BMCWEB_LOG_ERROR("Old eventService config parse error."); 34628afb49cSJunLin Chen return; 34728afb49cSJunLin Chen } 34828afb49cSJunLin Chen 3490bdda665SEd Tanous const nlohmann::json::object_t* obj = 3500bdda665SEd Tanous jsonData.get_ptr<const nlohmann::json::object_t*>(); 3510bdda665SEd Tanous for (const auto& item : *obj) 35228afb49cSJunLin Chen { 3530bdda665SEd Tanous if (item.first == "Configuration") 35428afb49cSJunLin Chen { 35528afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 35628afb49cSJunLin Chen .getEventServiceConfig() 3570bdda665SEd Tanous .fromJson(item.second); 35828afb49cSJunLin Chen } 3590bdda665SEd Tanous else if (item.first == "Subscriptions") 36028afb49cSJunLin Chen { 3610bdda665SEd Tanous for (const auto& elem : item.second) 36228afb49cSJunLin Chen { 3634b712a29SEd Tanous std::optional<persistent_data::UserSubscription> 36428afb49cSJunLin Chen newSubscription = 36528afb49cSJunLin Chen persistent_data::UserSubscription::fromJson(elem, 36628afb49cSJunLin Chen true); 3674b712a29SEd Tanous if (!newSubscription) 36828afb49cSJunLin Chen { 36962598e31SEd Tanous BMCWEB_LOG_ERROR("Problem reading subscription " 37062598e31SEd Tanous "from old persistent store"); 3714bbf237fSAppaRao Puli continue; 3724bbf237fSAppaRao Puli } 3734b712a29SEd Tanous persistent_data::UserSubscription& newSub = 3744b712a29SEd Tanous *newSubscription; 3751bf712bcSAyushi Smriti 37628afb49cSJunLin Chen std::uniform_int_distribution<uint32_t> dist(0); 37728afb49cSJunLin Chen bmcweb::OpenSSLGenerator gen; 3781bf712bcSAyushi Smriti 37928afb49cSJunLin Chen std::string id; 3801bf712bcSAyushi Smriti 38128afb49cSJunLin Chen int retry = 3; 382e662eae8SEd Tanous while (retry != 0) 3831bf712bcSAyushi Smriti { 38428afb49cSJunLin Chen id = std::to_string(dist(gen)); 38528afb49cSJunLin Chen if (gen.error()) 3867d1cc387SAppaRao Puli { 38728afb49cSJunLin Chen retry = 0; 38828afb49cSJunLin Chen break; 38928afb49cSJunLin Chen } 3904b712a29SEd Tanous newSub.id = id; 39128afb49cSJunLin Chen auto inserted = 39228afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 3935fe4ef35SMyung Bae .subscriptionsConfigMap.insert(std::pair( 3945fe4ef35SMyung Bae id, std::make_shared< 3955fe4ef35SMyung Bae persistent_data::UserSubscription>( 3965fe4ef35SMyung Bae newSub))); 39728afb49cSJunLin Chen if (inserted.second) 39828afb49cSJunLin Chen { 39928afb49cSJunLin Chen break; 40028afb49cSJunLin Chen } 40128afb49cSJunLin Chen --retry; 4027d1cc387SAppaRao Puli } 4037d1cc387SAppaRao Puli 40428afb49cSJunLin Chen if (retry <= 0) 40528afb49cSJunLin Chen { 40662598e31SEd Tanous BMCWEB_LOG_ERROR( 40762598e31SEd Tanous "Failed to generate random number from old " 40862598e31SEd Tanous "persistent store"); 40928afb49cSJunLin Chen continue; 41028afb49cSJunLin Chen } 41128afb49cSJunLin Chen } 41228afb49cSJunLin Chen } 41328afb49cSJunLin Chen 41428afb49cSJunLin Chen persistent_data::getConfig().writeData(); 4154c521c3cSEd Tanous std::error_code ec; 4164c521c3cSEd Tanous std::filesystem::remove(eventServiceFile, ec); 4174c521c3cSEd Tanous if (ec) 4184c521c3cSEd Tanous { 4194c521c3cSEd Tanous BMCWEB_LOG_DEBUG( 4204c521c3cSEd Tanous "Failed to remove old event service file. Ignoring"); 4214c521c3cSEd Tanous } 4224c521c3cSEd Tanous else 4234c521c3cSEd Tanous { 42462598e31SEd Tanous BMCWEB_LOG_DEBUG("Remove old eventservice config"); 42528afb49cSJunLin Chen } 42628afb49cSJunLin Chen } 4274c521c3cSEd Tanous } 42828afb49cSJunLin Chen 4299eb808c1SEd Tanous void updateSubscriptionData() const 43028afb49cSJunLin Chen { 43128afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 43228afb49cSJunLin Chen .eventServiceConfig.enabled = serviceEnabled; 43328afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 43428afb49cSJunLin Chen .eventServiceConfig.retryAttempts = retryAttempts; 43528afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 43628afb49cSJunLin Chen .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval; 43728afb49cSJunLin Chen 43828afb49cSJunLin Chen persistent_data::getConfig().writeData(); 43928afb49cSJunLin Chen } 44028afb49cSJunLin Chen 44128afb49cSJunLin Chen void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg) 4427d1cc387SAppaRao Puli { 4437d1cc387SAppaRao Puli bool updateConfig = false; 444fe44eb0bSAyushi Smriti bool updateRetryCfg = false; 4457d1cc387SAppaRao Puli 44628afb49cSJunLin Chen if (serviceEnabled != cfg.enabled) 4477d1cc387SAppaRao Puli { 44828afb49cSJunLin Chen serviceEnabled = cfg.enabled; 449e662eae8SEd Tanous if (serviceEnabled && noOfMetricReportSubscribers != 0U) 4507d1cc387SAppaRao Puli { 4517d1cc387SAppaRao Puli registerMetricReportSignal(); 4527d1cc387SAppaRao Puli } 4537d1cc387SAppaRao Puli else 4547d1cc387SAppaRao Puli { 4557d1cc387SAppaRao Puli unregisterMetricReportSignal(); 4567d1cc387SAppaRao Puli } 4577d1cc387SAppaRao Puli updateConfig = true; 4587d1cc387SAppaRao Puli } 4597d1cc387SAppaRao Puli 46028afb49cSJunLin Chen if (retryAttempts != cfg.retryAttempts) 4617d1cc387SAppaRao Puli { 46228afb49cSJunLin Chen retryAttempts = cfg.retryAttempts; 4637d1cc387SAppaRao Puli updateConfig = true; 464fe44eb0bSAyushi Smriti updateRetryCfg = true; 4657d1cc387SAppaRao Puli } 4667d1cc387SAppaRao Puli 46728afb49cSJunLin Chen if (retryTimeoutInterval != cfg.retryTimeoutInterval) 4687d1cc387SAppaRao Puli { 46928afb49cSJunLin Chen retryTimeoutInterval = cfg.retryTimeoutInterval; 4707d1cc387SAppaRao Puli updateConfig = true; 471fe44eb0bSAyushi Smriti updateRetryCfg = true; 4727d1cc387SAppaRao Puli } 4737d1cc387SAppaRao Puli 4747d1cc387SAppaRao Puli if (updateConfig) 4757d1cc387SAppaRao Puli { 4767d1cc387SAppaRao Puli updateSubscriptionData(); 4777d1cc387SAppaRao Puli } 478fe44eb0bSAyushi Smriti 479fe44eb0bSAyushi Smriti if (updateRetryCfg) 480fe44eb0bSAyushi Smriti { 481fe44eb0bSAyushi Smriti // Update the changed retry config to all subscriptions 482fe44eb0bSAyushi Smriti for (const auto& it : 483fe44eb0bSAyushi Smriti EventServiceManager::getInstance().subscriptionsMap) 484fe44eb0bSAyushi Smriti { 4855e44e3d8SAppaRao Puli Subscription& entry = *it.second; 4865e44e3d8SAppaRao Puli entry.updateRetryConfig(retryAttempts, retryTimeoutInterval); 487fe44eb0bSAyushi Smriti } 488fe44eb0bSAyushi Smriti } 4897d1cc387SAppaRao Puli } 4907d1cc387SAppaRao Puli 4917d1cc387SAppaRao Puli void updateNoOfSubscribersCount() 4927d1cc387SAppaRao Puli { 4937d1cc387SAppaRao Puli size_t eventLogSubCount = 0; 4947d1cc387SAppaRao Puli size_t metricReportSubCount = 0; 4957d1cc387SAppaRao Puli for (const auto& it : subscriptionsMap) 4967d1cc387SAppaRao Puli { 4977d1cc387SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 4985fe4ef35SMyung Bae if (entry->userSub->eventFormatType == eventFormatType) 4997d1cc387SAppaRao Puli { 5007d1cc387SAppaRao Puli eventLogSubCount++; 5017d1cc387SAppaRao Puli } 5025fe4ef35SMyung Bae else if (entry->userSub->eventFormatType == metricReportFormatType) 5037d1cc387SAppaRao Puli { 5047d1cc387SAppaRao Puli metricReportSubCount++; 5057d1cc387SAppaRao Puli } 5067d1cc387SAppaRao Puli } 5077d1cc387SAppaRao Puli 5087d1cc387SAppaRao Puli noOfEventLogSubscribers = eventLogSubCount; 5097d1cc387SAppaRao Puli if (noOfMetricReportSubscribers != metricReportSubCount) 5107d1cc387SAppaRao Puli { 5117d1cc387SAppaRao Puli noOfMetricReportSubscribers = metricReportSubCount; 512e662eae8SEd Tanous if (noOfMetricReportSubscribers != 0U) 5137d1cc387SAppaRao Puli { 5147d1cc387SAppaRao Puli registerMetricReportSignal(); 5157d1cc387SAppaRao Puli } 5167d1cc387SAppaRao Puli else 5177d1cc387SAppaRao Puli { 5187d1cc387SAppaRao Puli unregisterMetricReportSignal(); 5197d1cc387SAppaRao Puli } 5207d1cc387SAppaRao Puli } 5217d1cc387SAppaRao Puli } 5227d1cc387SAppaRao Puli 523b52664e2SAppaRao Puli std::shared_ptr<Subscription> getSubscription(const std::string& id) 524b52664e2SAppaRao Puli { 525b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 526b52664e2SAppaRao Puli if (obj == subscriptionsMap.end()) 527b52664e2SAppaRao Puli { 52862598e31SEd Tanous BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id); 529b52664e2SAppaRao Puli return nullptr; 530b52664e2SAppaRao Puli } 531b52664e2SAppaRao Puli std::shared_ptr<Subscription> subValue = obj->second; 532b52664e2SAppaRao Puli return subValue; 533b52664e2SAppaRao Puli } 534b52664e2SAppaRao Puli 535f80a87f2SEd Tanous std::string 536f80a87f2SEd Tanous addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue) 537b52664e2SAppaRao Puli { 538fc76b8acSEd Tanous std::uniform_int_distribution<uint32_t> dist(0); 539fc76b8acSEd Tanous bmcweb::OpenSSLGenerator gen; 540fc76b8acSEd Tanous 541b52664e2SAppaRao Puli std::string id; 542b52664e2SAppaRao Puli 543b52664e2SAppaRao Puli int retry = 3; 544e662eae8SEd Tanous while (retry != 0) 545b52664e2SAppaRao Puli { 546fc76b8acSEd Tanous id = std::to_string(dist(gen)); 547fc76b8acSEd Tanous if (gen.error()) 548fc76b8acSEd Tanous { 549fc76b8acSEd Tanous retry = 0; 550fc76b8acSEd Tanous break; 551fc76b8acSEd Tanous } 552b52664e2SAppaRao Puli auto inserted = subscriptionsMap.insert(std::pair(id, subValue)); 553b52664e2SAppaRao Puli if (inserted.second) 554b52664e2SAppaRao Puli { 555b52664e2SAppaRao Puli break; 556b52664e2SAppaRao Puli } 557b52664e2SAppaRao Puli --retry; 55823a21a1cSEd Tanous } 559b52664e2SAppaRao Puli 560b52664e2SAppaRao Puli if (retry <= 0) 561b52664e2SAppaRao Puli { 56262598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to generate random number"); 563abb93cddSEd Tanous return ""; 564b52664e2SAppaRao Puli } 565b52664e2SAppaRao Puli 56656ba386dSMyung Bae // Set Subscription ID for back trace 5675fe4ef35SMyung Bae subValue->userSub->id = id; 568a14c9113SEd Tanous 56928afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 5705fe4ef35SMyung Bae .subscriptionsConfigMap.emplace(id, subValue->userSub); 57128afb49cSJunLin Chen 5727d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 5731bf712bcSAyushi Smriti 57483328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 57583328316SEd Tanous { 5762558979cSP Dheeraj Srujan Kumar if (redfishLogFilePosition != 0) 5777f4eb588SAppaRao Puli { 5782558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 5797f4eb588SAppaRao Puli } 58083328316SEd Tanous } 581fe44eb0bSAyushi Smriti // Update retry configuration. 582fe44eb0bSAyushi Smriti subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 583fe44eb0bSAyushi Smriti 584f80a87f2SEd Tanous return id; 585f80a87f2SEd Tanous } 586f80a87f2SEd Tanous 587f80a87f2SEd Tanous std::string 588f80a87f2SEd Tanous addSSESubscription(const std::shared_ptr<Subscription>& subValue, 589f80a87f2SEd Tanous std::string_view lastEventId) 590f80a87f2SEd Tanous { 591f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 592f80a87f2SEd Tanous 593f80a87f2SEd Tanous if (!lastEventId.empty()) 594f80a87f2SEd Tanous { 595f80a87f2SEd Tanous BMCWEB_LOG_INFO("Attempting to find message for last id {}", 596f80a87f2SEd Tanous lastEventId); 597f80a87f2SEd Tanous boost::circular_buffer<Event>::iterator lastEvent = 598f80a87f2SEd Tanous std::find_if(messages.begin(), messages.end(), 599f80a87f2SEd Tanous [&lastEventId](const Event& event) { 600f80a87f2SEd Tanous return event.id == lastEventId; 601f80a87f2SEd Tanous }); 602f80a87f2SEd Tanous // Can't find a matching ID 603f80a87f2SEd Tanous if (lastEvent == messages.end()) 604f80a87f2SEd Tanous { 605f80a87f2SEd Tanous nlohmann::json msg = messages::eventBufferExceeded(); 606f80a87f2SEd Tanous // If the buffer overloaded, send all messages. 6076d799e14SEd Tanous subValue->sendEventToSubscriber(msg); 608f80a87f2SEd Tanous lastEvent = messages.begin(); 609f80a87f2SEd Tanous } 610f80a87f2SEd Tanous else 611f80a87f2SEd Tanous { 612f80a87f2SEd Tanous // Skip the last event the user already has 613f80a87f2SEd Tanous lastEvent++; 614f80a87f2SEd Tanous } 615f80a87f2SEd Tanous 616f80a87f2SEd Tanous for (boost::circular_buffer<Event>::const_iterator event = 617f80a87f2SEd Tanous lastEvent; 618f80a87f2SEd Tanous lastEvent != messages.end(); lastEvent++) 619f80a87f2SEd Tanous { 6206d799e14SEd Tanous subValue->sendEventToSubscriber(event->message); 621f80a87f2SEd Tanous } 622f80a87f2SEd Tanous } 623f80a87f2SEd Tanous return id; 624f80a87f2SEd Tanous } 625f80a87f2SEd Tanous 626f80a87f2SEd Tanous std::string 627f80a87f2SEd Tanous addPushSubscription(const std::shared_ptr<Subscription>& subValue) 628f80a87f2SEd Tanous { 629f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 630a0969c70SMyung Bae subValue->deleter = [id]() { 631a0969c70SMyung Bae EventServiceManager::getInstance().deleteSubscription(id); 632a0969c70SMyung Bae }; 633f80a87f2SEd Tanous updateSubscriptionData(); 634b52664e2SAppaRao Puli return id; 635b52664e2SAppaRao Puli } 636b52664e2SAppaRao Puli 637b52664e2SAppaRao Puli bool isSubscriptionExist(const std::string& id) 638b52664e2SAppaRao Puli { 639b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 64055f79e6fSEd Tanous return obj != subscriptionsMap.end(); 641b52664e2SAppaRao Puli } 642b52664e2SAppaRao Puli 6434b712a29SEd Tanous bool deleteSubscription(const std::string& id) 644b52664e2SAppaRao Puli { 645b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 6464b712a29SEd Tanous if (obj == subscriptionsMap.end()) 647b52664e2SAppaRao Puli { 6484b712a29SEd Tanous BMCWEB_LOG_WARNING("Could not find subscription with id {}", id); 6494b712a29SEd Tanous return false; 6504b712a29SEd Tanous } 651b52664e2SAppaRao Puli subscriptionsMap.erase(obj); 6524b712a29SEd Tanous auto& event = persistent_data::EventServiceStore::getInstance(); 6534b712a29SEd Tanous auto persistentObj = event.subscriptionsConfigMap.find(id); 6544b712a29SEd Tanous if (persistentObj == event.subscriptionsConfigMap.end()) 6554b712a29SEd Tanous { 6564b712a29SEd Tanous BMCWEB_LOG_ERROR("Subscription wasn't in persistent data"); 6574b712a29SEd Tanous return true; 6584b712a29SEd Tanous } 65928afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 6604b712a29SEd Tanous .subscriptionsConfigMap.erase(persistentObj); 6617d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 662b52664e2SAppaRao Puli updateSubscriptionData(); 6634b712a29SEd Tanous 6644b712a29SEd Tanous return true; 665b52664e2SAppaRao Puli } 666b52664e2SAppaRao Puli 6675e44e3d8SAppaRao Puli void deleteSseSubscription(const crow::sse_socket::Connection& thisConn) 6685e44e3d8SAppaRao Puli { 669bdbfae2aSEd Tanous for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();) 6705e44e3d8SAppaRao Puli { 671bdbfae2aSEd Tanous std::shared_ptr<Subscription> entry = it->second; 6725e44e3d8SAppaRao Puli bool entryIsThisConn = entry->matchSseId(thisConn); 6735e44e3d8SAppaRao Puli if (entryIsThisConn) 6745e44e3d8SAppaRao Puli { 6755e44e3d8SAppaRao Puli persistent_data::EventServiceStore::getInstance() 6765fe4ef35SMyung Bae .subscriptionsConfigMap.erase(entry->userSub->id); 677bdbfae2aSEd Tanous it = subscriptionsMap.erase(it); 6785e44e3d8SAppaRao Puli return; 6795e44e3d8SAppaRao Puli } 680bdbfae2aSEd Tanous it++; 6815e44e3d8SAppaRao Puli } 6825e44e3d8SAppaRao Puli } 6835e44e3d8SAppaRao Puli 6845e44e3d8SAppaRao Puli size_t getNumberOfSubscriptions() const 685b52664e2SAppaRao Puli { 686b52664e2SAppaRao Puli return subscriptionsMap.size(); 687b52664e2SAppaRao Puli } 688b52664e2SAppaRao Puli 6895e44e3d8SAppaRao Puli size_t getNumberOfSSESubscriptions() const 6905e44e3d8SAppaRao Puli { 6913544d2a7SEd Tanous auto size = std::ranges::count_if( 6923544d2a7SEd Tanous subscriptionsMap, 6935e44e3d8SAppaRao Puli [](const std::pair<std::string, std::shared_ptr<Subscription>>& 6945e44e3d8SAppaRao Puli entry) { 6955fe4ef35SMyung Bae return (entry.second->userSub->subscriptionType == 6964b712a29SEd Tanous subscriptionTypeSSE); 6975e44e3d8SAppaRao Puli }); 6985e44e3d8SAppaRao Puli return static_cast<size_t>(size); 6995e44e3d8SAppaRao Puli } 7005e44e3d8SAppaRao Puli 701b52664e2SAppaRao Puli std::vector<std::string> getAllIDs() 702b52664e2SAppaRao Puli { 703b52664e2SAppaRao Puli std::vector<std::string> idList; 704b52664e2SAppaRao Puli for (const auto& it : subscriptionsMap) 705b52664e2SAppaRao Puli { 706b52664e2SAppaRao Puli idList.emplace_back(it.first); 707b52664e2SAppaRao Puli } 708b52664e2SAppaRao Puli return idList; 709b52664e2SAppaRao Puli } 710b52664e2SAppaRao Puli 7116ba8c82eSsunharis_in bool sendTestEventLog() 7120b4bdd93SAppaRao Puli { 7135e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 7140b4bdd93SAppaRao Puli { 7150b4bdd93SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 7166ba8c82eSsunharis_in if (!entry->sendTestEventLog()) 7176ba8c82eSsunharis_in { 7186ba8c82eSsunharis_in return false; 7190b4bdd93SAppaRao Puli } 7200b4bdd93SAppaRao Puli } 7216ba8c82eSsunharis_in return true; 7226ba8c82eSsunharis_in } 723e9a14131SAppaRao Puli 724f80a87f2SEd Tanous void sendEvent(nlohmann::json::object_t eventMessage, 725f80a87f2SEd Tanous std::string_view origin, std::string_view resourceType) 72696330b99SSunitha Harish { 727613dabeaSEd Tanous eventMessage["EventId"] = eventId; 728f80a87f2SEd Tanous 729613dabeaSEd Tanous eventMessage["EventTimestamp"] = 730613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 731613dabeaSEd Tanous eventMessage["OriginOfCondition"] = origin; 732613dabeaSEd Tanous 733f80a87f2SEd Tanous // MemberId is 0 : since we are sending one event record. 734788b091bSIgor Kanyuka eventMessage["MemberId"] = "0"; 73596330b99SSunitha Harish 736f80a87f2SEd Tanous messages.push_back(Event(std::to_string(eventId), eventMessage)); 737f80a87f2SEd Tanous 738f80a87f2SEd Tanous for (auto& it : subscriptionsMap) 73996330b99SSunitha Harish { 740f80a87f2SEd Tanous std::shared_ptr<Subscription>& entry = it.second; 7415fe4ef35SMyung Bae if (!eventMatchesFilter(*entry->userSub, eventMessage, 7425fe4ef35SMyung Bae resourceType)) 74396330b99SSunitha Harish { 744f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 745f80a87f2SEd Tanous continue; 74696330b99SSunitha Harish } 747f80a87f2SEd Tanous 748f80a87f2SEd Tanous nlohmann::json::array_t eventRecord; 749f80a87f2SEd Tanous eventRecord.emplace_back(eventMessage); 750f80a87f2SEd Tanous 751613dabeaSEd Tanous nlohmann::json msgJson; 752613dabeaSEd Tanous 753613dabeaSEd Tanous msgJson["@odata.type"] = "#Event.v1_4_0.Event"; 754613dabeaSEd Tanous msgJson["Name"] = "Event Log"; 755613dabeaSEd Tanous msgJson["Id"] = eventId; 756f80a87f2SEd Tanous msgJson["Events"] = std::move(eventRecord); 757f52c03c1SCarson Labrado 758f52c03c1SCarson Labrado std::string strMsg = msgJson.dump( 759f52c03c1SCarson Labrado 2, ' ', true, nlohmann::json::error_handler_t::replace); 7606d799e14SEd Tanous entry->sendEventToSubscriber(std::move(strMsg)); 7618ece0e45SEd Tanous eventId++; // increment the eventId 76296330b99SSunitha Harish } 76396330b99SSunitha Harish } 76496330b99SSunitha Harish 7652558979cSP Dheeraj Srujan Kumar void resetRedfishFilePosition() 7667f4eb588SAppaRao Puli { 7672558979cSP Dheeraj Srujan Kumar // Control would be here when Redfish file is created. 7682558979cSP Dheeraj Srujan Kumar // Reset File Position as new file is created 7692558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = 0; 7702558979cSP Dheeraj Srujan Kumar } 7712558979cSP Dheeraj Srujan Kumar 7722558979cSP Dheeraj Srujan Kumar void cacheRedfishLogFile() 7732558979cSP Dheeraj Srujan Kumar { 7742558979cSP Dheeraj Srujan Kumar // Open the redfish file and read till the last record. 7752558979cSP Dheeraj Srujan Kumar 7767f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 7777f4eb588SAppaRao Puli if (!logStream.good()) 7787f4eb588SAppaRao Puli { 77962598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed "); 7807f4eb588SAppaRao Puli return; 7817f4eb588SAppaRao Puli } 7827f4eb588SAppaRao Puli std::string logEntry; 7837f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 7847f4eb588SAppaRao Puli { 7852558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 7867f4eb588SAppaRao Puli } 7877f4eb588SAppaRao Puli } 7887f4eb588SAppaRao Puli 7897f4eb588SAppaRao Puli void readEventLogsFromFile() 7907f4eb588SAppaRao Puli { 7917f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 7927f4eb588SAppaRao Puli if (!logStream.good()) 7937f4eb588SAppaRao Puli { 79462598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed"); 7957f4eb588SAppaRao Puli return; 7967f4eb588SAppaRao Puli } 7977f4eb588SAppaRao Puli 7987f4eb588SAppaRao Puli std::vector<EventLogObjectsType> eventRecords; 7997f4eb588SAppaRao Puli 8007f4eb588SAppaRao Puli std::string logEntry; 8012558979cSP Dheeraj Srujan Kumar 80203d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: seek to {}", 80303d4d37cSAlexander Hansen static_cast<int>(redfishLogFilePosition)); 80403d4d37cSAlexander Hansen 8052558979cSP Dheeraj Srujan Kumar // Get the read pointer to the next log to be read. 8062558979cSP Dheeraj Srujan Kumar logStream.seekg(redfishLogFilePosition); 8072558979cSP Dheeraj Srujan Kumar 8087f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 8097f4eb588SAppaRao Puli { 81003d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry"); 8112558979cSP Dheeraj Srujan Kumar // Update Pointer position 8122558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 8132558979cSP Dheeraj Srujan Kumar 8142558979cSP Dheeraj Srujan Kumar std::string idStr; 8152558979cSP Dheeraj Srujan Kumar if (!event_log::getUniqueEntryID(logEntry, idStr)) 8167f4eb588SAppaRao Puli { 81703d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 81803d4d37cSAlexander Hansen "Redfish log file: could not get unique entry id for {}", 81903d4d37cSAlexander Hansen logEntry); 8207f4eb588SAppaRao Puli continue; 8217f4eb588SAppaRao Puli } 8227f4eb588SAppaRao Puli 823e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 8247f4eb588SAppaRao Puli { 8252558979cSP Dheeraj Srujan Kumar // If Service is not enabled, no need to compute 8262558979cSP Dheeraj Srujan Kumar // the remaining items below. 8272558979cSP Dheeraj Srujan Kumar // But, Loop must continue to keep track of Timestamp 82803d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 82903d4d37cSAlexander Hansen "Redfish log file: no subscribers / event service not enabled"); 8307f4eb588SAppaRao Puli continue; 8317f4eb588SAppaRao Puli } 8327f4eb588SAppaRao Puli 8337f4eb588SAppaRao Puli std::string timestamp; 8347f4eb588SAppaRao Puli std::string messageID; 8355e715de6SAppaRao Puli std::vector<std::string> messageArgs; 8367f4eb588SAppaRao Puli if (event_log::getEventLogParams(logEntry, timestamp, messageID, 8377f4eb588SAppaRao Puli messageArgs) != 0) 8387f4eb588SAppaRao Puli { 83903d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}", 84003d4d37cSAlexander Hansen logEntry); 8417f4eb588SAppaRao Puli continue; 8427f4eb588SAppaRao Puli } 8437f4eb588SAppaRao Puli 844f80a87f2SEd Tanous eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs); 8457f4eb588SAppaRao Puli } 8467f4eb588SAppaRao Puli 847e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 8482558979cSP Dheeraj Srujan Kumar { 84962598e31SEd Tanous BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions."); 8502558979cSP Dheeraj Srujan Kumar return; 8512558979cSP Dheeraj Srujan Kumar } 8522558979cSP Dheeraj Srujan Kumar 8532558979cSP Dheeraj Srujan Kumar if (eventRecords.empty()) 8542558979cSP Dheeraj Srujan Kumar { 8552558979cSP Dheeraj Srujan Kumar // No Records to send 85662598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 8572558979cSP Dheeraj Srujan Kumar return; 8582558979cSP Dheeraj Srujan Kumar } 8592558979cSP Dheeraj Srujan Kumar 8605e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 8617f4eb588SAppaRao Puli { 8627f4eb588SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 8635fe4ef35SMyung Bae if (entry->userSub->eventFormatType == "Event") 8647f4eb588SAppaRao Puli { 8657f4eb588SAppaRao Puli entry->filterAndSendEventLogs(eventRecords); 8667f4eb588SAppaRao Puli } 8677f4eb588SAppaRao Puli } 8687f4eb588SAppaRao Puli } 8697f4eb588SAppaRao Puli 8707f4eb588SAppaRao Puli static void watchRedfishEventLogFile() 8717f4eb588SAppaRao Puli { 8726a9f85f9SAppaRao Puli if (!inotifyConn) 8737f4eb588SAppaRao Puli { 87403d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("inotify Connection is not present"); 8757f4eb588SAppaRao Puli return; 8767f4eb588SAppaRao Puli } 8777f4eb588SAppaRao Puli 8787f4eb588SAppaRao Puli static std::array<char, 1024> readBuffer; 8797f4eb588SAppaRao Puli 880bd79bce8SPatrick Williams inotifyConn->async_read_some( 881bd79bce8SPatrick Williams boost::asio::buffer(readBuffer), 8827f4eb588SAppaRao Puli [&](const boost::system::error_code& ec, 8837f4eb588SAppaRao Puli const std::size_t& bytesTransferred) { 8849ed3f90aSEd Tanous if (ec == boost::asio::error::operation_aborted) 8859ed3f90aSEd Tanous { 8869ed3f90aSEd Tanous BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)"); 8879ed3f90aSEd Tanous return; 8889ed3f90aSEd Tanous } 8897f4eb588SAppaRao Puli if (ec) 8907f4eb588SAppaRao Puli { 89162598e31SEd Tanous BMCWEB_LOG_ERROR("Callback Error: {}", ec.message()); 8927f4eb588SAppaRao Puli return; 8937f4eb588SAppaRao Puli } 89403d4d37cSAlexander Hansen 89503d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred); 89603d4d37cSAlexander Hansen 8977f4eb588SAppaRao Puli std::size_t index = 0; 898b792cc56SAppaRao Puli while ((index + iEventSize) <= bytesTransferred) 8997f4eb588SAppaRao Puli { 900d3a9e084SEd Tanous struct inotify_event event 901d3a9e084SEd Tanous {}; 902b792cc56SAppaRao Puli std::memcpy(&event, &readBuffer[index], iEventSize); 903b792cc56SAppaRao Puli if (event.wd == dirWatchDesc) 904b792cc56SAppaRao Puli { 905b792cc56SAppaRao Puli if ((event.len == 0) || 906b792cc56SAppaRao Puli (index + iEventSize + event.len > bytesTransferred)) 907b792cc56SAppaRao Puli { 908b792cc56SAppaRao Puli index += (iEventSize + event.len); 909b792cc56SAppaRao Puli continue; 910b792cc56SAppaRao Puli } 911b792cc56SAppaRao Puli 9124f568f74SJiaqing Zhao std::string fileName(&readBuffer[index + iEventSize]); 9134f568f74SJiaqing Zhao if (fileName != "redfish") 914b792cc56SAppaRao Puli { 915b792cc56SAppaRao Puli index += (iEventSize + event.len); 916b792cc56SAppaRao Puli continue; 917b792cc56SAppaRao Puli } 918b792cc56SAppaRao Puli 91962598e31SEd Tanous BMCWEB_LOG_DEBUG( 92062598e31SEd Tanous "Redfish log file created/deleted. event.name: {}", 92162598e31SEd Tanous fileName); 922b792cc56SAppaRao Puli if (event.mask == IN_CREATE) 923b792cc56SAppaRao Puli { 924b792cc56SAppaRao Puli if (fileWatchDesc != -1) 925b792cc56SAppaRao Puli { 92662598e31SEd Tanous BMCWEB_LOG_DEBUG( 92762598e31SEd Tanous "Remove and Add inotify watcher on " 92862598e31SEd Tanous "redfish event log file"); 929016761afSAppaRao Puli // Remove existing inotify watcher and add 930016761afSAppaRao Puli // with new redfish event log file. 931016761afSAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 932016761afSAppaRao Puli fileWatchDesc = -1; 933b792cc56SAppaRao Puli } 934b792cc56SAppaRao Puli 935b792cc56SAppaRao Puli fileWatchDesc = inotify_add_watch( 936b792cc56SAppaRao Puli inotifyFd, redfishEventLogFile, IN_MODIFY); 937b792cc56SAppaRao Puli if (fileWatchDesc == -1) 938b792cc56SAppaRao Puli { 93962598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for " 94062598e31SEd Tanous "redfish log file."); 941b792cc56SAppaRao Puli return; 942b792cc56SAppaRao Puli } 943b792cc56SAppaRao Puli 944b792cc56SAppaRao Puli EventServiceManager::getInstance() 9452558979cSP Dheeraj Srujan Kumar .resetRedfishFilePosition(); 946b792cc56SAppaRao Puli EventServiceManager::getInstance() 947b792cc56SAppaRao Puli .readEventLogsFromFile(); 948b792cc56SAppaRao Puli } 949b792cc56SAppaRao Puli else if ((event.mask == IN_DELETE) || 950b792cc56SAppaRao Puli (event.mask == IN_MOVED_TO)) 951b792cc56SAppaRao Puli { 952b792cc56SAppaRao Puli if (fileWatchDesc != -1) 953b792cc56SAppaRao Puli { 954b792cc56SAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 955b792cc56SAppaRao Puli fileWatchDesc = -1; 956b792cc56SAppaRao Puli } 957b792cc56SAppaRao Puli } 958b792cc56SAppaRao Puli } 959b792cc56SAppaRao Puli else if (event.wd == fileWatchDesc) 960b792cc56SAppaRao Puli { 961b792cc56SAppaRao Puli if (event.mask == IN_MODIFY) 9627f4eb588SAppaRao Puli { 9637f4eb588SAppaRao Puli EventServiceManager::getInstance() 9647f4eb588SAppaRao Puli .readEventLogsFromFile(); 9657f4eb588SAppaRao Puli } 966b792cc56SAppaRao Puli } 967b792cc56SAppaRao Puli index += (iEventSize + event.len); 9687f4eb588SAppaRao Puli } 9697f4eb588SAppaRao Puli 9707f4eb588SAppaRao Puli watchRedfishEventLogFile(); 9717f4eb588SAppaRao Puli }); 9727f4eb588SAppaRao Puli } 9737f4eb588SAppaRao Puli 9747f4eb588SAppaRao Puli static int startEventLogMonitor(boost::asio::io_context& ioc) 9757f4eb588SAppaRao Puli { 97603d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 97703d4d37cSAlexander Hansen 97823a21a1cSEd Tanous inotifyConn.emplace(ioc); 979b792cc56SAppaRao Puli inotifyFd = inotify_init1(IN_NONBLOCK); 980b792cc56SAppaRao Puli if (inotifyFd == -1) 9817f4eb588SAppaRao Puli { 98262598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_init1 failed."); 9837f4eb588SAppaRao Puli return -1; 9847f4eb588SAppaRao Puli } 985b792cc56SAppaRao Puli 986b792cc56SAppaRao Puli // Add watch on directory to handle redfish event log file 987b792cc56SAppaRao Puli // create/delete. 988b792cc56SAppaRao Puli dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 989b792cc56SAppaRao Puli IN_CREATE | IN_MOVED_TO | IN_DELETE); 990b792cc56SAppaRao Puli if (dirWatchDesc == -1) 9917f4eb588SAppaRao Puli { 99262598e31SEd Tanous BMCWEB_LOG_ERROR( 99362598e31SEd Tanous "inotify_add_watch failed for event log directory."); 9947f4eb588SAppaRao Puli return -1; 9957f4eb588SAppaRao Puli } 9967f4eb588SAppaRao Puli 997b792cc56SAppaRao Puli // Watch redfish event log file for modifications. 998bd79bce8SPatrick Williams fileWatchDesc = 999bd79bce8SPatrick Williams inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 1000b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1001b792cc56SAppaRao Puli { 100262598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 1003b792cc56SAppaRao Puli // Don't return error if file not exist. 1004b792cc56SAppaRao Puli // Watch on directory will handle create/delete of file. 1005b792cc56SAppaRao Puli } 1006b792cc56SAppaRao Puli 10077f4eb588SAppaRao Puli // monitor redfish event log file 1008b792cc56SAppaRao Puli inotifyConn->assign(inotifyFd); 10097f4eb588SAppaRao Puli watchRedfishEventLogFile(); 10107f4eb588SAppaRao Puli 10117f4eb588SAppaRao Puli return 0; 10127f4eb588SAppaRao Puli } 10137f4eb588SAppaRao Puli 10149ed3f90aSEd Tanous static void stopEventLogMonitor() 10159ed3f90aSEd Tanous { 10169ed3f90aSEd Tanous inotifyConn.reset(); 10179ed3f90aSEd Tanous } 10189ed3f90aSEd Tanous 101959d494eeSPatrick Williams static void getReadingsForReport(sdbusplus::message_t& msg) 1020156d6b00SAppaRao Puli { 102156d2396dSEd Tanous if (msg.is_method_error()) 102256d2396dSEd Tanous { 102362598e31SEd Tanous BMCWEB_LOG_ERROR("TelemetryMonitor Signal error"); 102456d2396dSEd Tanous return; 102556d2396dSEd Tanous } 102656d2396dSEd Tanous 1027c0353249SWludzik, Jozef sdbusplus::message::object_path path(msg.get_path()); 1028c0353249SWludzik, Jozef std::string id = path.filename(); 1029c0353249SWludzik, Jozef if (id.empty()) 1030156d6b00SAppaRao Puli { 103162598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to get Id from path"); 1032156d6b00SAppaRao Puli return; 1033156d6b00SAppaRao Puli } 1034156d6b00SAppaRao Puli 1035c0353249SWludzik, Jozef std::string interface; 1036b9d36b47SEd Tanous dbus::utility::DBusPropertiesMap props; 1037c0353249SWludzik, Jozef std::vector<std::string> invalidProps; 1038c0353249SWludzik, Jozef msg.read(interface, props, invalidProps); 1039c0353249SWludzik, Jozef 1040bd79bce8SPatrick Williams auto found = std::ranges::find_if(props, [](const auto& x) { 1041bd79bce8SPatrick Williams return x.first == "Readings"; 1042bd79bce8SPatrick Williams }); 1043c0353249SWludzik, Jozef if (found == props.end()) 1044156d6b00SAppaRao Puli { 104562598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 1046156d6b00SAppaRao Puli return; 1047156d6b00SAppaRao Puli } 1048156d6b00SAppaRao Puli 10491e1e598dSJonathan Doman const telemetry::TimestampReadings* readings = 10501e1e598dSJonathan Doman std::get_if<telemetry::TimestampReadings>(&found->second); 1051e662eae8SEd Tanous if (readings == nullptr) 10521e1e598dSJonathan Doman { 105362598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 10541e1e598dSJonathan Doman return; 10551e1e598dSJonathan Doman } 10561e1e598dSJonathan Doman 1057156d6b00SAppaRao Puli for (const auto& it : 1058156d6b00SAppaRao Puli EventServiceManager::getInstance().subscriptionsMap) 1059156d6b00SAppaRao Puli { 1060e05aec50SEd Tanous Subscription& entry = *it.second; 10615fe4ef35SMyung Bae if (entry.userSub->eventFormatType == metricReportFormatType) 1062156d6b00SAppaRao Puli { 10631e1e598dSJonathan Doman entry.filterAndSendReports(id, *readings); 1064156d6b00SAppaRao Puli } 1065156d6b00SAppaRao Puli } 1066156d6b00SAppaRao Puli } 1067156d6b00SAppaRao Puli 1068156d6b00SAppaRao Puli void unregisterMetricReportSignal() 1069156d6b00SAppaRao Puli { 10707d1cc387SAppaRao Puli if (matchTelemetryMonitor) 10717d1cc387SAppaRao Puli { 107262598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Unregister"); 1073156d6b00SAppaRao Puli matchTelemetryMonitor.reset(); 1074156d6b00SAppaRao Puli matchTelemetryMonitor = nullptr; 1075156d6b00SAppaRao Puli } 10767d1cc387SAppaRao Puli } 1077156d6b00SAppaRao Puli 1078156d6b00SAppaRao Puli void registerMetricReportSignal() 1079156d6b00SAppaRao Puli { 10807d1cc387SAppaRao Puli if (!serviceEnabled || matchTelemetryMonitor) 1081156d6b00SAppaRao Puli { 108262598e31SEd Tanous BMCWEB_LOG_DEBUG("Not registering metric report signal."); 1083156d6b00SAppaRao Puli return; 1084156d6b00SAppaRao Puli } 1085156d6b00SAppaRao Puli 108662598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Register"); 1087c0353249SWludzik, Jozef std::string matchStr = "type='signal',member='PropertiesChanged'," 1088c0353249SWludzik, Jozef "interface='org.freedesktop.DBus.Properties'," 1089c0353249SWludzik, Jozef "arg0=xyz.openbmc_project.Telemetry.Report"; 1090156d6b00SAppaRao Puli 109159d494eeSPatrick Williams matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>( 109256d2396dSEd Tanous *crow::connections::systemBus, matchStr, getReadingsForReport); 1093156d6b00SAppaRao Puli } 109423a21a1cSEd Tanous }; 1095b52664e2SAppaRao Puli 1096b52664e2SAppaRao Puli } // namespace redfish 1097