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" 193ccb3adbSEd Tanous #include "event_service_store.hpp" 20f80a87f2SEd Tanous #include "filter_expr_executor.hpp" 21539d8c6bSEd Tanous #include "generated/enums/event.hpp" 22539d8c6bSEd Tanous #include "generated/enums/log_entry.hpp" 233ccb3adbSEd Tanous #include "http_client.hpp" 24c0353249SWludzik, Jozef #include "metric_report.hpp" 252c6ffdb0SEd Tanous #include "ossl_random.hpp" 263ccb3adbSEd Tanous #include "persistent_data.hpp" 277f4eb588SAppaRao Puli #include "registries.hpp" 288dab0f58SEd Tanous #include "registries_selector.hpp" 2950ebd4afSEd Tanous #include "str_utility.hpp" 3077665bdaSNan Zhou #include "utility.hpp" 313ccb3adbSEd Tanous #include "utils/json_utils.hpp" 325b90429aSEd Tanous #include "utils/time_utils.hpp" 337f4eb588SAppaRao Puli 347f4eb588SAppaRao Puli #include <sys/inotify.h> 35b52664e2SAppaRao Puli 36fb4fd5d4SZhenfei Tai #include <boost/asio/io_context.hpp> 37f80a87f2SEd Tanous #include <boost/circular_buffer.hpp> 38b52664e2SAppaRao Puli #include <boost/container/flat_map.hpp> 39ef4c65b7SEd Tanous #include <boost/url/format.hpp> 404a7fbefdSEd Tanous #include <boost/url/url_view_base.hpp> 41b5b40605Snitroglycerine #include <sdbusplus/bus/match.hpp> 421214b7e7SGunnar Mills 435e44e3d8SAppaRao Puli #include <algorithm> 44b52664e2SAppaRao Puli #include <cstdlib> 45b52664e2SAppaRao Puli #include <ctime> 46*a14c9113SEd Tanous #include <format> 471bf712bcSAyushi Smriti #include <fstream> 48b52664e2SAppaRao Puli #include <memory> 493544d2a7SEd Tanous #include <ranges> 5026702d01SEd Tanous #include <span> 51*a14c9113SEd Tanous #include <string> 52b52664e2SAppaRao Puli 53b52664e2SAppaRao Puli namespace redfish 54b52664e2SAppaRao Puli { 55156d6b00SAppaRao Puli 56156d6b00SAppaRao Puli static constexpr const char* eventFormatType = "Event"; 57156d6b00SAppaRao Puli static constexpr const char* metricReportFormatType = "MetricReport"; 58156d6b00SAppaRao Puli 595e44e3d8SAppaRao Puli static constexpr const char* subscriptionTypeSSE = "SSE"; 601bf712bcSAyushi Smriti static constexpr const char* eventServiceFile = 611bf712bcSAyushi Smriti "/var/lib/bmcweb/eventservice_config.json"; 621bf712bcSAyushi Smriti 635e44e3d8SAppaRao Puli static constexpr const uint8_t maxNoOfSubscriptions = 20; 645e44e3d8SAppaRao Puli static constexpr const uint8_t maxNoOfSSESubscriptions = 10; 655e44e3d8SAppaRao Puli 66cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 674642bf8fSGeorge Liu static std::optional<boost::asio::posix::stream_descriptor> inotifyConn; 684642bf8fSGeorge Liu static constexpr const char* redfishEventLogDir = "/var/log"; 694642bf8fSGeorge Liu static constexpr const char* redfishEventLogFile = "/var/log/redfish"; 704642bf8fSGeorge Liu static constexpr const size_t iEventSize = sizeof(inotify_event); 71cf9e417dSEd Tanous 72cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 734642bf8fSGeorge Liu static int inotifyFd = -1; 74cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 754642bf8fSGeorge Liu static int dirWatchDesc = -1; 76cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 774642bf8fSGeorge Liu static int fileWatchDesc = -1; 78f80a87f2SEd Tanous struct EventLogObjectsType 79f80a87f2SEd Tanous { 80f80a87f2SEd Tanous std::string id; 81f80a87f2SEd Tanous std::string timestamp; 82f80a87f2SEd Tanous std::string messageId; 83f80a87f2SEd Tanous std::vector<std::string> messageArgs; 84f80a87f2SEd Tanous }; 854642bf8fSGeorge Liu 86fffb8c1fSEd Tanous namespace registries 874642bf8fSGeorge Liu { 887f4eb588SAppaRao Puli static const Message* 897f4eb588SAppaRao Puli getMsgFromRegistry(const std::string& messageKey, 9026702d01SEd Tanous const std::span<const MessageEntry>& registry) 917f4eb588SAppaRao Puli { 923544d2a7SEd Tanous std::span<const MessageEntry>::iterator messageIt = std::ranges::find_if( 933544d2a7SEd Tanous registry, [&messageKey](const MessageEntry& messageEntry) { 9455f79e6fSEd Tanous return messageKey == messageEntry.first; 957f4eb588SAppaRao Puli }); 9626702d01SEd Tanous if (messageIt != registry.end()) 977f4eb588SAppaRao Puli { 987f4eb588SAppaRao Puli return &messageIt->second; 997f4eb588SAppaRao Puli } 1007f4eb588SAppaRao Puli 1017f4eb588SAppaRao Puli return nullptr; 1027f4eb588SAppaRao Puli } 1037f4eb588SAppaRao Puli 10426ccae32SEd Tanous static const Message* formatMessage(std::string_view messageID) 1057f4eb588SAppaRao Puli { 1067f4eb588SAppaRao Puli // Redfish MessageIds are in the form 1077f4eb588SAppaRao Puli // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 1087f4eb588SAppaRao Puli // the right Message 1097f4eb588SAppaRao Puli std::vector<std::string> fields; 1107f4eb588SAppaRao Puli fields.reserve(4); 11150ebd4afSEd Tanous 11250ebd4afSEd Tanous bmcweb::split(fields, messageID, '.'); 1137f4eb588SAppaRao Puli if (fields.size() != 4) 1147f4eb588SAppaRao Puli { 1157f4eb588SAppaRao Puli return nullptr; 1167f4eb588SAppaRao Puli } 11702cad96eSEd Tanous const std::string& registryName = fields[0]; 11802cad96eSEd Tanous const std::string& messageKey = fields[3]; 1197f4eb588SAppaRao Puli 1207f4eb588SAppaRao Puli // Find the right registry and check it for the MessageKey 121b304bd79SP Dheeraj Srujan Kumar return getMsgFromRegistry(messageKey, getRegistryFromPrefix(registryName)); 1227f4eb588SAppaRao Puli } 123fffb8c1fSEd Tanous } // namespace registries 1247f4eb588SAppaRao Puli 1257f4eb588SAppaRao Puli namespace event_log 1267f4eb588SAppaRao Puli { 1272558979cSP Dheeraj Srujan Kumar inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) 1287f4eb588SAppaRao Puli { 1297f4eb588SAppaRao Puli static time_t prevTs = 0; 1307f4eb588SAppaRao Puli static int index = 0; 1317f4eb588SAppaRao Puli 1327f4eb588SAppaRao Puli // Get the entry timestamp 1337f4eb588SAppaRao Puli std::time_t curTs = 0; 1347f4eb588SAppaRao Puli std::tm timeStruct = {}; 1357f4eb588SAppaRao Puli std::istringstream entryStream(logEntry); 1367f4eb588SAppaRao Puli if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 1377f4eb588SAppaRao Puli { 1387f4eb588SAppaRao Puli curTs = std::mktime(&timeStruct); 1397f4eb588SAppaRao Puli if (curTs == -1) 1407f4eb588SAppaRao Puli { 1417f4eb588SAppaRao Puli return false; 1427f4eb588SAppaRao Puli } 1437f4eb588SAppaRao Puli } 1447f4eb588SAppaRao Puli // If the timestamp isn't unique, increment the index 1457f4eb588SAppaRao Puli index = (curTs == prevTs) ? index + 1 : 0; 1467f4eb588SAppaRao Puli 1477f4eb588SAppaRao Puli // Save the timestamp 1487f4eb588SAppaRao Puli prevTs = curTs; 1497f4eb588SAppaRao Puli 1507f4eb588SAppaRao Puli entryID = std::to_string(curTs); 1517f4eb588SAppaRao Puli if (index > 0) 1527f4eb588SAppaRao Puli { 1537f4eb588SAppaRao Puli entryID += "_" + std::to_string(index); 1547f4eb588SAppaRao Puli } 1557f4eb588SAppaRao Puli return true; 1567f4eb588SAppaRao Puli } 1577f4eb588SAppaRao Puli 15823a21a1cSEd Tanous inline int getEventLogParams(const std::string& logEntry, 15923a21a1cSEd Tanous std::string& timestamp, std::string& messageID, 1605e715de6SAppaRao Puli std::vector<std::string>& messageArgs) 1617f4eb588SAppaRao Puli { 1627f4eb588SAppaRao Puli // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 1637f4eb588SAppaRao Puli // First get the Timestamp 164f23b7296SEd Tanous size_t space = logEntry.find_first_of(' '); 1657f4eb588SAppaRao Puli if (space == std::string::npos) 1667f4eb588SAppaRao Puli { 16703d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", 16803d4d37cSAlexander Hansen logEntry); 1697f4eb588SAppaRao Puli return -EINVAL; 1707f4eb588SAppaRao Puli } 1717f4eb588SAppaRao Puli timestamp = logEntry.substr(0, space); 1727f4eb588SAppaRao Puli // Then get the log contents 173f23b7296SEd Tanous size_t entryStart = logEntry.find_first_not_of(' ', space); 1747f4eb588SAppaRao Puli if (entryStart == std::string::npos) 1757f4eb588SAppaRao Puli { 17603d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", 17703d4d37cSAlexander Hansen logEntry); 1787f4eb588SAppaRao Puli return -EINVAL; 1797f4eb588SAppaRao Puli } 1807f4eb588SAppaRao Puli std::string_view entry(logEntry); 1817f4eb588SAppaRao Puli entry.remove_prefix(entryStart); 1827f4eb588SAppaRao Puli // Use split to separate the entry into its fields 1837f4eb588SAppaRao Puli std::vector<std::string> logEntryFields; 18450ebd4afSEd Tanous bmcweb::split(logEntryFields, entry, ','); 1857f4eb588SAppaRao Puli // We need at least a MessageId to be valid 18626f6976fSEd Tanous if (logEntryFields.empty()) 1877f4eb588SAppaRao Puli { 18803d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", 18903d4d37cSAlexander Hansen logEntry); 1907f4eb588SAppaRao Puli return -EINVAL; 1917f4eb588SAppaRao Puli } 1927f4eb588SAppaRao Puli messageID = logEntryFields[0]; 1937f4eb588SAppaRao Puli 1947f4eb588SAppaRao Puli // Get the MessageArgs from the log if there are any 1957f4eb588SAppaRao Puli if (logEntryFields.size() > 1) 1967f4eb588SAppaRao Puli { 19702cad96eSEd Tanous const std::string& messageArgsStart = logEntryFields[1]; 1987f4eb588SAppaRao Puli // If the first string is empty, assume there are no MessageArgs 1997f4eb588SAppaRao Puli if (!messageArgsStart.empty()) 2007f4eb588SAppaRao Puli { 2015e715de6SAppaRao Puli messageArgs.assign(logEntryFields.begin() + 1, 2025e715de6SAppaRao Puli logEntryFields.end()); 2037f4eb588SAppaRao Puli } 2047f4eb588SAppaRao Puli } 2057f4eb588SAppaRao Puli 2067f4eb588SAppaRao Puli return 0; 2077f4eb588SAppaRao Puli } 2087f4eb588SAppaRao Puli 20923a21a1cSEd Tanous inline void getRegistryAndMessageKey(const std::string& messageID, 2107f4eb588SAppaRao Puli std::string& registryName, 2117f4eb588SAppaRao Puli std::string& messageKey) 2127f4eb588SAppaRao Puli { 2137f4eb588SAppaRao Puli // Redfish MessageIds are in the form 2147f4eb588SAppaRao Puli // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 2157f4eb588SAppaRao Puli // the right Message 2167f4eb588SAppaRao Puli std::vector<std::string> fields; 2177f4eb588SAppaRao Puli fields.reserve(4); 21850ebd4afSEd Tanous bmcweb::split(fields, messageID, '.'); 2197f4eb588SAppaRao Puli if (fields.size() == 4) 2207f4eb588SAppaRao Puli { 2217f4eb588SAppaRao Puli registryName = fields[0]; 2227f4eb588SAppaRao Puli messageKey = fields[3]; 2237f4eb588SAppaRao Puli } 2247f4eb588SAppaRao Puli } 2257f4eb588SAppaRao Puli 226bd79bce8SPatrick Williams inline int formatEventLogEntry( 227bd79bce8SPatrick Williams const std::string& logEntryID, const std::string& messageID, 228bd79bce8SPatrick Williams const std::span<std::string_view> messageArgs, std::string timestamp, 229bd79bce8SPatrick Williams const std::string& customText, nlohmann::json::object_t& logEntryJson) 2307f4eb588SAppaRao Puli { 2317f4eb588SAppaRao Puli // Get the Message from the MessageRegistry 232fffb8c1fSEd Tanous const registries::Message* message = registries::formatMessage(messageID); 2337f4eb588SAppaRao Puli 23480f595e7SEd Tanous if (message == nullptr) 2357f4eb588SAppaRao Puli { 23680f595e7SEd Tanous return -1; 2377f4eb588SAppaRao Puli } 2387f4eb588SAppaRao Puli 239bd79bce8SPatrick Williams std::string msg = 240bd79bce8SPatrick Williams redfish::registries::fillMessageArgs(messageArgs, message->message); 24180f595e7SEd Tanous if (msg.empty()) 24280f595e7SEd Tanous { 24380f595e7SEd Tanous return -1; 24480f595e7SEd Tanous } 2457f4eb588SAppaRao Puli 2467f4eb588SAppaRao Puli // Get the Created time from the timestamp. The log timestamp is in 2477f4eb588SAppaRao Puli // RFC3339 format which matches the Redfish format except for the 2487f4eb588SAppaRao Puli // fractional seconds between the '.' and the '+', so just remove them. 249f23b7296SEd Tanous std::size_t dot = timestamp.find_first_of('.'); 250b2f7609bSEd Tanous std::size_t plus = timestamp.find_first_of('+', dot); 2517f4eb588SAppaRao Puli if (dot != std::string::npos && plus != std::string::npos) 2527f4eb588SAppaRao Puli { 2537f4eb588SAppaRao Puli timestamp.erase(dot, plus - dot); 2547f4eb588SAppaRao Puli } 2557f4eb588SAppaRao Puli 2567f4eb588SAppaRao Puli // Fill in the log entry with the gathered data 2571476687dSEd Tanous logEntryJson["EventId"] = logEntryID; 258539d8c6bSEd Tanous 25980f595e7SEd Tanous logEntryJson["Severity"] = message->messageSeverity; 2601476687dSEd Tanous logEntryJson["Message"] = std::move(msg); 2611476687dSEd Tanous logEntryJson["MessageId"] = messageID; 2621476687dSEd Tanous logEntryJson["MessageArgs"] = messageArgs; 2631476687dSEd Tanous logEntryJson["EventTimestamp"] = std::move(timestamp); 2641476687dSEd Tanous logEntryJson["Context"] = customText; 2657f4eb588SAppaRao Puli return 0; 2667f4eb588SAppaRao Puli } 2677f4eb588SAppaRao Puli 2687f4eb588SAppaRao Puli } // namespace event_log 2697f4eb588SAppaRao Puli 27028afb49cSJunLin Chen class Subscription : public persistent_data::UserSubscription 271b52664e2SAppaRao Puli { 272b52664e2SAppaRao Puli public: 273b52664e2SAppaRao Puli Subscription(const Subscription&) = delete; 274b52664e2SAppaRao Puli Subscription& operator=(const Subscription&) = delete; 275b52664e2SAppaRao Puli Subscription(Subscription&&) = delete; 276b52664e2SAppaRao Puli Subscription& operator=(Subscription&&) = delete; 277b52664e2SAppaRao Puli 2784a7fbefdSEd Tanous Subscription(const boost::urls::url_view_base& url, 2794a7fbefdSEd Tanous boost::asio::io_context& ioc) : 280a716aa74SEd Tanous policy(std::make_shared<crow::ConnectionPolicy>()) 281b52664e2SAppaRao Puli { 282a716aa74SEd Tanous destinationUrl = url; 2835e44e3d8SAppaRao Puli client.emplace(ioc, policy); 2847adb85acSSunitha Harish // Subscription constructor 285d14a48ffSCarson Labrado policy->invalidResp = retryRespHandler; 286b52664e2SAppaRao Puli } 2874bbf237fSAppaRao Puli 2885e44e3d8SAppaRao Puli explicit Subscription(crow::sse_socket::Connection& connIn) : 2895e44e3d8SAppaRao Puli sseConn(&connIn) 2905e44e3d8SAppaRao Puli {} 2915e44e3d8SAppaRao Puli 2929f616dd1SEd Tanous ~Subscription() = default; 293b52664e2SAppaRao Puli 2946d799e14SEd Tanous bool sendEventToSubscriber(std::string&& msg) 295b52664e2SAppaRao Puli { 2966ba8c82eSsunharis_in persistent_data::EventServiceConfig eventServiceConfig = 2976ba8c82eSsunharis_in persistent_data::EventServiceStore::getInstance() 2986ba8c82eSsunharis_in .getEventServiceConfig(); 2996ba8c82eSsunharis_in if (!eventServiceConfig.enabled) 3006ba8c82eSsunharis_in { 3016ba8c82eSsunharis_in return false; 3026ba8c82eSsunharis_in } 3036ba8c82eSsunharis_in 3045e44e3d8SAppaRao Puli if (client) 3055e44e3d8SAppaRao Puli { 30619bb362bSEd Tanous client->sendData( 30719bb362bSEd Tanous std::move(msg), destinationUrl, 30819bb362bSEd Tanous static_cast<ensuressl::VerifyCertificate>(verifyCertificate), 30919bb362bSEd Tanous httpHeaders, boost::beast::http::verb::post); 3105e44e3d8SAppaRao Puli return true; 3115e44e3d8SAppaRao Puli } 3127adb85acSSunitha Harish 3134bbf237fSAppaRao Puli if (sseConn != nullptr) 3144bbf237fSAppaRao Puli { 3155e44e3d8SAppaRao Puli eventSeqNum++; 3166d799e14SEd Tanous sseConn->sendSseEvent(std::to_string(eventSeqNum), msg); 3174bbf237fSAppaRao Puli } 3186ba8c82eSsunharis_in return true; 3194bbf237fSAppaRao Puli } 3204bbf237fSAppaRao Puli 321f80a87f2SEd Tanous bool eventMatchesFilter(const nlohmann::json::object_t& eventMessage, 322f80a87f2SEd Tanous std::string_view resType) 323f80a87f2SEd Tanous { 324f80a87f2SEd Tanous // If resourceTypes list is empty, assume all 325f80a87f2SEd Tanous if (!resourceTypes.empty()) 326f80a87f2SEd Tanous { 327f80a87f2SEd Tanous // Search the resourceTypes list for the subscription. 328f80a87f2SEd Tanous auto resourceTypeIndex = std::ranges::find_if( 329f80a87f2SEd Tanous resourceTypes, [resType](const std::string& rtEntry) { 330f80a87f2SEd Tanous return rtEntry == resType; 331f80a87f2SEd Tanous }); 332f80a87f2SEd Tanous if (resourceTypeIndex == resourceTypes.end()) 333f80a87f2SEd Tanous { 334f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Not subscribed to this resource"); 335f80a87f2SEd Tanous return false; 336f80a87f2SEd Tanous } 337f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("ResourceType {} found in the subscribed list", 338f80a87f2SEd Tanous resType); 339f80a87f2SEd Tanous } 340f80a87f2SEd Tanous 341*a14c9113SEd Tanous // If registryPrefixes list is empty, don't filter events 342*a14c9113SEd Tanous // send everything. 343*a14c9113SEd Tanous if (!registryPrefixes.empty()) 344*a14c9113SEd Tanous { 345*a14c9113SEd Tanous auto eventJson = eventMessage.find("MessageId"); 346*a14c9113SEd Tanous if (eventJson == eventMessage.end()) 347*a14c9113SEd Tanous { 348*a14c9113SEd Tanous return false; 349*a14c9113SEd Tanous } 350*a14c9113SEd Tanous 351*a14c9113SEd Tanous const std::string* messageId = 352*a14c9113SEd Tanous eventJson->second.get_ptr<const std::string*>(); 353*a14c9113SEd Tanous if (messageId == nullptr) 354*a14c9113SEd Tanous { 355*a14c9113SEd Tanous BMCWEB_LOG_ERROR("MessageId wasn't a string???"); 356*a14c9113SEd Tanous return false; 357*a14c9113SEd Tanous } 358*a14c9113SEd Tanous 359*a14c9113SEd Tanous std::string registry; 360*a14c9113SEd Tanous std::string messageKey; 361*a14c9113SEd Tanous event_log::getRegistryAndMessageKey(*messageId, registry, 362*a14c9113SEd Tanous messageKey); 363*a14c9113SEd Tanous 364*a14c9113SEd Tanous auto obj = std::ranges::find(registryPrefixes, registry); 365*a14c9113SEd Tanous if (obj == registryPrefixes.end()) 366*a14c9113SEd Tanous { 367*a14c9113SEd Tanous return false; 368*a14c9113SEd Tanous } 369*a14c9113SEd Tanous } 370*a14c9113SEd Tanous 371*a14c9113SEd Tanous if (!originResources.empty()) 372*a14c9113SEd Tanous { 373*a14c9113SEd Tanous auto eventJson = eventMessage.find("OriginOfCondition"); 374*a14c9113SEd Tanous if (eventJson == eventMessage.end()) 375*a14c9113SEd Tanous { 376*a14c9113SEd Tanous return false; 377*a14c9113SEd Tanous } 378*a14c9113SEd Tanous 379*a14c9113SEd Tanous const std::string* originOfCondition = 380*a14c9113SEd Tanous eventJson->second.get_ptr<const std::string*>(); 381*a14c9113SEd Tanous if (originOfCondition == nullptr) 382*a14c9113SEd Tanous { 383*a14c9113SEd Tanous BMCWEB_LOG_ERROR("OriginOfCondition wasn't a string???"); 384*a14c9113SEd Tanous return false; 385*a14c9113SEd Tanous } 386*a14c9113SEd Tanous 387*a14c9113SEd Tanous auto obj = std::ranges::find(originResources, *originOfCondition); 388*a14c9113SEd Tanous 389*a14c9113SEd Tanous if (obj == originResources.end()) 390*a14c9113SEd Tanous { 391*a14c9113SEd Tanous return false; 392*a14c9113SEd Tanous } 393*a14c9113SEd Tanous } 394*a14c9113SEd Tanous 395f80a87f2SEd Tanous // If registryMsgIds list is empty, assume all 396f80a87f2SEd Tanous if (!registryMsgIds.empty()) 397f80a87f2SEd Tanous { 398f80a87f2SEd Tanous auto eventJson = eventMessage.find("MessageId"); 399f80a87f2SEd Tanous if (eventJson == eventMessage.end()) 400f80a87f2SEd Tanous { 40103d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("'MessageId' not present"); 402f80a87f2SEd Tanous return false; 403f80a87f2SEd Tanous } 404f80a87f2SEd Tanous 405f80a87f2SEd Tanous const std::string* messageId = 406f80a87f2SEd Tanous eventJson->second.get_ptr<const std::string*>(); 407f80a87f2SEd Tanous if (messageId == nullptr) 408f80a87f2SEd Tanous { 409f80a87f2SEd Tanous BMCWEB_LOG_ERROR("EventType wasn't a string???"); 410f80a87f2SEd Tanous return false; 411f80a87f2SEd Tanous } 412f80a87f2SEd Tanous 413f80a87f2SEd Tanous std::string registry; 414f80a87f2SEd Tanous std::string messageKey; 415f80a87f2SEd Tanous event_log::getRegistryAndMessageKey(*messageId, registry, 416f80a87f2SEd Tanous messageKey); 417f80a87f2SEd Tanous 41803d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("extracted registry {}", registry); 41903d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("extracted message key {}", messageKey); 42003d4d37cSAlexander Hansen 421*a14c9113SEd Tanous auto obj = std::ranges::find( 422*a14c9113SEd Tanous registryMsgIds, std::format("{}.{}", registry, messageKey)); 423f80a87f2SEd Tanous if (obj == registryMsgIds.end()) 424f80a87f2SEd Tanous { 42503d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("did not find registry {} in registryMsgIds", 42603d4d37cSAlexander Hansen registry); 42703d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("registryMsgIds has {} entries", 42803d4d37cSAlexander Hansen registryMsgIds.size()); 429f80a87f2SEd Tanous return false; 430f80a87f2SEd Tanous } 431f80a87f2SEd Tanous } 432f80a87f2SEd Tanous 433f80a87f2SEd Tanous if (filter) 434f80a87f2SEd Tanous { 435f80a87f2SEd Tanous if (!memberMatches(eventMessage, *filter)) 436f80a87f2SEd Tanous { 437f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 438f80a87f2SEd Tanous return false; 439f80a87f2SEd Tanous } 440f80a87f2SEd Tanous } 441f80a87f2SEd Tanous 442f80a87f2SEd Tanous return true; 443f80a87f2SEd Tanous } 444f80a87f2SEd Tanous 4456ba8c82eSsunharis_in bool sendTestEventLog() 4460b4bdd93SAppaRao Puli { 447f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 448f80a87f2SEd Tanous nlohmann::json& logEntryJson = logEntryArray.emplace_back(); 4490b4bdd93SAppaRao Puli 450613dabeaSEd Tanous logEntryJson["EventId"] = "TestID"; 451539d8c6bSEd Tanous logEntryJson["Severity"] = log_entry::EventSeverity::OK; 452613dabeaSEd Tanous logEntryJson["Message"] = "Generated test event"; 453613dabeaSEd Tanous logEntryJson["MessageId"] = "OpenBMC.0.2.TestEventLog"; 454d2cdd478SChandra Harkude // MemberId is 0 : since we are sending one event record. 455d2cdd478SChandra Harkude logEntryJson["MemberId"] = 0; 456613dabeaSEd Tanous logEntryJson["MessageArgs"] = nlohmann::json::array(); 457613dabeaSEd Tanous logEntryJson["EventTimestamp"] = 458613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 459613dabeaSEd Tanous logEntryJson["Context"] = customText; 4600b4bdd93SAppaRao Puli 4611476687dSEd Tanous nlohmann::json msg; 4621476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 4631476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 4641476687dSEd Tanous msg["Name"] = "Event Log"; 4651476687dSEd Tanous msg["Events"] = logEntryArray; 4660b4bdd93SAppaRao Puli 467bd79bce8SPatrick Williams std::string strMsg = 468bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 4696d799e14SEd Tanous return sendEventToSubscriber(std::move(strMsg)); 4700b4bdd93SAppaRao Puli } 4710b4bdd93SAppaRao Puli 4727f4eb588SAppaRao Puli void filterAndSendEventLogs( 4737f4eb588SAppaRao Puli const std::vector<EventLogObjectsType>& eventRecords) 4747f4eb588SAppaRao Puli { 475f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 4767f4eb588SAppaRao Puli for (const EventLogObjectsType& logEntry : eventRecords) 4777f4eb588SAppaRao Puli { 478f80a87f2SEd Tanous std::vector<std::string_view> messageArgsView( 479f80a87f2SEd Tanous logEntry.messageArgs.begin(), logEntry.messageArgs.end()); 4807f4eb588SAppaRao Puli 481f80a87f2SEd Tanous nlohmann::json::object_t bmcLogEntry; 482f80a87f2SEd Tanous if (event_log::formatEventLogEntry( 483f80a87f2SEd Tanous logEntry.id, logEntry.messageId, messageArgsView, 484f80a87f2SEd Tanous logEntry.timestamp, customText, bmcLogEntry) != 0) 4857f4eb588SAppaRao Puli { 48662598e31SEd Tanous BMCWEB_LOG_DEBUG("Read eventLog entry failed"); 4877f4eb588SAppaRao Puli continue; 4887f4eb588SAppaRao Puli } 489f80a87f2SEd Tanous 490f80a87f2SEd Tanous if (!eventMatchesFilter(bmcLogEntry, "")) 491f80a87f2SEd Tanous { 49203d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Event {} did not match the filter", 49303d4d37cSAlexander Hansen nlohmann::json(bmcLogEntry).dump()); 494f80a87f2SEd Tanous continue; 495f80a87f2SEd Tanous } 496f80a87f2SEd Tanous 497f80a87f2SEd Tanous logEntryArray.emplace_back(std::move(bmcLogEntry)); 4987f4eb588SAppaRao Puli } 4997f4eb588SAppaRao Puli 50026f6976fSEd Tanous if (logEntryArray.empty()) 5017f4eb588SAppaRao Puli { 50262598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 5037f4eb588SAppaRao Puli return; 5047f4eb588SAppaRao Puli } 5057f4eb588SAppaRao Puli 5061476687dSEd Tanous nlohmann::json msg; 5071476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 5081476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 5091476687dSEd Tanous msg["Name"] = "Event Log"; 510f80a87f2SEd Tanous msg["Events"] = std::move(logEntryArray); 511bd79bce8SPatrick Williams std::string strMsg = 512bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 5136d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 5145e44e3d8SAppaRao Puli eventSeqNum++; 5157f4eb588SAppaRao Puli } 5167f4eb588SAppaRao Puli 517248d0230SEd Tanous void filterAndSendReports(const std::string& reportId, 5181e1e598dSJonathan Doman const telemetry::TimestampReadings& var) 519156d6b00SAppaRao Puli { 520ef4c65b7SEd Tanous boost::urls::url mrdUri = boost::urls::format( 521ef4c65b7SEd Tanous "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", 522ef4c65b7SEd Tanous reportId); 523156d6b00SAppaRao Puli 524156d6b00SAppaRao Puli // Empty list means no filter. Send everything. 52526f6976fSEd Tanous if (!metricReportDefinitions.empty()) 526156d6b00SAppaRao Puli { 5273544d2a7SEd Tanous if (std::ranges::find(metricReportDefinitions, mrdUri.buffer()) == 5283544d2a7SEd Tanous metricReportDefinitions.end()) 529156d6b00SAppaRao Puli { 530156d6b00SAppaRao Puli return; 531156d6b00SAppaRao Puli } 532156d6b00SAppaRao Puli } 533156d6b00SAppaRao Puli 534c0353249SWludzik, Jozef nlohmann::json msg; 535248d0230SEd Tanous if (!telemetry::fillReport(msg, reportId, var)) 536156d6b00SAppaRao Puli { 53762598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to fill the MetricReport for DBus " 53862598e31SEd Tanous "Report with id {}", 53962598e31SEd Tanous reportId); 540c0353249SWludzik, Jozef return; 541156d6b00SAppaRao Puli } 542156d6b00SAppaRao Puli 54322daffd7SAppaRao Puli // Context is set by user during Event subscription and it must be 54422daffd7SAppaRao Puli // set for MetricReport response. 54522daffd7SAppaRao Puli if (!customText.empty()) 54622daffd7SAppaRao Puli { 54722daffd7SAppaRao Puli msg["Context"] = customText; 54822daffd7SAppaRao Puli } 54922daffd7SAppaRao Puli 550bd79bce8SPatrick Williams std::string strMsg = 551bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 5526d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 553156d6b00SAppaRao Puli } 554156d6b00SAppaRao Puli 555d14a48ffSCarson Labrado void updateRetryConfig(uint32_t retryAttempts, 556d14a48ffSCarson Labrado uint32_t retryTimeoutInterval) 557fe44eb0bSAyushi Smriti { 55893cf0ac2SEd Tanous if (policy == nullptr) 55993cf0ac2SEd Tanous { 56093cf0ac2SEd Tanous BMCWEB_LOG_DEBUG("Retry policy was nullptr, ignoring set"); 56193cf0ac2SEd Tanous return; 56293cf0ac2SEd Tanous } 563d14a48ffSCarson Labrado policy->maxRetryAttempts = retryAttempts; 564d14a48ffSCarson Labrado policy->retryIntervalSecs = std::chrono::seconds(retryTimeoutInterval); 56562de0c68SAppaRao Puli } 566fe44eb0bSAyushi Smriti 5679eb808c1SEd Tanous uint64_t getEventSeqNum() const 56896330b99SSunitha Harish { 56996330b99SSunitha Harish return eventSeqNum; 57096330b99SSunitha Harish } 57196330b99SSunitha Harish 5725e44e3d8SAppaRao Puli void setSubscriptionId(const std::string& id2) 5735e44e3d8SAppaRao Puli { 57462598e31SEd Tanous BMCWEB_LOG_DEBUG("Subscription ID: {}", id2); 5755e44e3d8SAppaRao Puli subId = id2; 5765e44e3d8SAppaRao Puli } 5775e44e3d8SAppaRao Puli 5785e44e3d8SAppaRao Puli std::string getSubscriptionId() 5795e44e3d8SAppaRao Puli { 5805e44e3d8SAppaRao Puli return subId; 5815e44e3d8SAppaRao Puli } 5825e44e3d8SAppaRao Puli 5835e44e3d8SAppaRao Puli bool matchSseId(const crow::sse_socket::Connection& thisConn) 5845e44e3d8SAppaRao Puli { 5855e44e3d8SAppaRao Puli return &thisConn == sseConn; 5865e44e3d8SAppaRao Puli } 5875e44e3d8SAppaRao Puli 588a7a80296SCarson Labrado // Check used to indicate what response codes are valid as part of our retry 589a7a80296SCarson Labrado // policy. 2XX is considered acceptable 590a7a80296SCarson Labrado static boost::system::error_code retryRespHandler(unsigned int respCode) 591a7a80296SCarson Labrado { 59262598e31SEd Tanous BMCWEB_LOG_DEBUG( 59362598e31SEd Tanous "Checking response code validity for SubscriptionEvent"); 594a7a80296SCarson Labrado if ((respCode < 200) || (respCode >= 300)) 595a7a80296SCarson Labrado { 596a7a80296SCarson Labrado return boost::system::errc::make_error_code( 597a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 598a7a80296SCarson Labrado } 599a7a80296SCarson Labrado 600a7a80296SCarson Labrado // Return 0 if the response code is valid 601a7a80296SCarson Labrado return boost::system::errc::make_error_code( 602a7a80296SCarson Labrado boost::system::errc::success); 6039fa6d147SNan Zhou } 604f80a87f2SEd Tanous 605f80a87f2SEd Tanous private: 606f80a87f2SEd Tanous std::string subId; 607f80a87f2SEd Tanous uint64_t eventSeqNum = 1; 608f80a87f2SEd Tanous boost::urls::url host; 609f80a87f2SEd Tanous std::shared_ptr<crow::ConnectionPolicy> policy; 610f80a87f2SEd Tanous crow::sse_socket::Connection* sseConn = nullptr; 611f80a87f2SEd Tanous 612f80a87f2SEd Tanous std::optional<crow::HttpClient> client; 613f80a87f2SEd Tanous 614f80a87f2SEd Tanous public: 615f80a87f2SEd Tanous std::optional<filter_ast::LogicalAnd> filter; 616b52664e2SAppaRao Puli }; 617b52664e2SAppaRao Puli 618b52664e2SAppaRao Puli class EventServiceManager 619b52664e2SAppaRao Puli { 620b52664e2SAppaRao Puli private: 621d3a9e084SEd Tanous bool serviceEnabled = false; 622d3a9e084SEd Tanous uint32_t retryAttempts = 0; 623d3a9e084SEd Tanous uint32_t retryTimeoutInterval = 0; 6247d1cc387SAppaRao Puli 6252558979cSP Dheeraj Srujan Kumar std::streampos redfishLogFilePosition{0}; 6269f616dd1SEd Tanous size_t noOfEventLogSubscribers{0}; 6279f616dd1SEd Tanous size_t noOfMetricReportSubscribers{0}; 62859d494eeSPatrick Williams std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor; 629b52664e2SAppaRao Puli boost::container::flat_map<std::string, std::shared_ptr<Subscription>> 630b52664e2SAppaRao Puli subscriptionsMap; 631b52664e2SAppaRao Puli 6329f616dd1SEd Tanous uint64_t eventId{1}; 63396330b99SSunitha Harish 634f80a87f2SEd Tanous struct Event 635f80a87f2SEd Tanous { 636f80a87f2SEd Tanous std::string id; 637f80a87f2SEd Tanous nlohmann::json message; 638f80a87f2SEd Tanous }; 639f80a87f2SEd Tanous 640f80a87f2SEd Tanous constexpr static size_t maxMessages = 200; 641f80a87f2SEd Tanous boost::circular_buffer<Event> messages{maxMessages}; 642f80a87f2SEd Tanous 643f8ca6d79SEd Tanous boost::asio::io_context& ioc; 644f8ca6d79SEd Tanous 645b52664e2SAppaRao Puli public: 6469f616dd1SEd Tanous EventServiceManager(const EventServiceManager&) = delete; 6479f616dd1SEd Tanous EventServiceManager& operator=(const EventServiceManager&) = delete; 6489f616dd1SEd Tanous EventServiceManager(EventServiceManager&&) = delete; 6499f616dd1SEd Tanous EventServiceManager& operator=(EventServiceManager&&) = delete; 650ecd6a3a2SEd Tanous ~EventServiceManager() = default; 6519f616dd1SEd Tanous 652f8ca6d79SEd Tanous explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn) 653b52664e2SAppaRao Puli { 654f8ca6d79SEd Tanous // Load config from persist store. 655f8ca6d79SEd Tanous initConfig(); 656f8ca6d79SEd Tanous } 657f8ca6d79SEd Tanous 658f8ca6d79SEd Tanous static EventServiceManager& 659f8ca6d79SEd Tanous getInstance(boost::asio::io_context* ioc = nullptr) 660f8ca6d79SEd Tanous { 661f8ca6d79SEd Tanous static EventServiceManager handler(*ioc); 662b52664e2SAppaRao Puli return handler; 663b52664e2SAppaRao Puli } 664b52664e2SAppaRao Puli 6651bf712bcSAyushi Smriti void initConfig() 6661bf712bcSAyushi Smriti { 66728afb49cSJunLin Chen loadOldBehavior(); 6681bf712bcSAyushi Smriti 66928afb49cSJunLin Chen persistent_data::EventServiceConfig eventServiceConfig = 67028afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 67128afb49cSJunLin Chen .getEventServiceConfig(); 6721bf712bcSAyushi Smriti 67328afb49cSJunLin Chen serviceEnabled = eventServiceConfig.enabled; 67428afb49cSJunLin Chen retryAttempts = eventServiceConfig.retryAttempts; 67528afb49cSJunLin Chen retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval; 6761bf712bcSAyushi Smriti 67728afb49cSJunLin Chen for (const auto& it : persistent_data::EventServiceStore::getInstance() 67828afb49cSJunLin Chen .subscriptionsConfigMap) 6791bf712bcSAyushi Smriti { 68028afb49cSJunLin Chen std::shared_ptr<persistent_data::UserSubscription> newSub = 68128afb49cSJunLin Chen it.second; 6824bbf237fSAppaRao Puli 6836fd29553SEd Tanous boost::system::result<boost::urls::url> url = 684a716aa74SEd Tanous boost::urls::parse_absolute_uri(newSub->destinationUrl); 6851bf712bcSAyushi Smriti 686a716aa74SEd Tanous if (!url) 6871bf712bcSAyushi Smriti { 68862598e31SEd Tanous BMCWEB_LOG_ERROR( 68962598e31SEd Tanous "Failed to validate and split destination url"); 6901bf712bcSAyushi Smriti continue; 6911bf712bcSAyushi Smriti } 6921bf712bcSAyushi Smriti std::shared_ptr<Subscription> subValue = 693a716aa74SEd Tanous std::make_shared<Subscription>(*url, ioc); 6941bf712bcSAyushi Smriti 69528afb49cSJunLin Chen subValue->id = newSub->id; 69628afb49cSJunLin Chen subValue->destinationUrl = newSub->destinationUrl; 69728afb49cSJunLin Chen subValue->protocol = newSub->protocol; 69819bb362bSEd Tanous subValue->verifyCertificate = newSub->verifyCertificate; 69928afb49cSJunLin Chen subValue->retryPolicy = newSub->retryPolicy; 70028afb49cSJunLin Chen subValue->customText = newSub->customText; 70128afb49cSJunLin Chen subValue->eventFormatType = newSub->eventFormatType; 70228afb49cSJunLin Chen subValue->subscriptionType = newSub->subscriptionType; 70328afb49cSJunLin Chen subValue->registryMsgIds = newSub->registryMsgIds; 70428afb49cSJunLin Chen subValue->registryPrefixes = newSub->registryPrefixes; 70528afb49cSJunLin Chen subValue->resourceTypes = newSub->resourceTypes; 70628afb49cSJunLin Chen subValue->httpHeaders = newSub->httpHeaders; 70728afb49cSJunLin Chen subValue->metricReportDefinitions = newSub->metricReportDefinitions; 708*a14c9113SEd Tanous subValue->originResources = newSub->originResources; 7091bf712bcSAyushi Smriti 71028afb49cSJunLin Chen if (subValue->id.empty()) 7111bf712bcSAyushi Smriti { 71262598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to add subscription"); 7131bf712bcSAyushi Smriti } 71428afb49cSJunLin Chen subscriptionsMap.insert(std::pair(subValue->id, subValue)); 71528afb49cSJunLin Chen 71628afb49cSJunLin Chen updateNoOfSubscribersCount(); 71728afb49cSJunLin Chen 71883328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 71983328316SEd Tanous { 7202558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 72183328316SEd Tanous } 7222558979cSP Dheeraj Srujan Kumar 72328afb49cSJunLin Chen // Update retry configuration. 72428afb49cSJunLin Chen subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 7251bf712bcSAyushi Smriti } 7261bf712bcSAyushi Smriti } 7271bf712bcSAyushi Smriti 72856d2396dSEd Tanous static void loadOldBehavior() 729b52664e2SAppaRao Puli { 73028afb49cSJunLin Chen std::ifstream eventConfigFile(eventServiceFile); 73128afb49cSJunLin Chen if (!eventConfigFile.good()) 7321bf712bcSAyushi Smriti { 73362598e31SEd Tanous BMCWEB_LOG_DEBUG("Old eventService config not exist"); 73428afb49cSJunLin Chen return; 73528afb49cSJunLin Chen } 73628afb49cSJunLin Chen auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false); 73728afb49cSJunLin Chen if (jsonData.is_discarded()) 7384bbf237fSAppaRao Puli { 73962598e31SEd Tanous BMCWEB_LOG_ERROR("Old eventService config parse error."); 74028afb49cSJunLin Chen return; 74128afb49cSJunLin Chen } 74228afb49cSJunLin Chen 7430bdda665SEd Tanous const nlohmann::json::object_t* obj = 7440bdda665SEd Tanous jsonData.get_ptr<const nlohmann::json::object_t*>(); 7450bdda665SEd Tanous for (const auto& item : *obj) 74628afb49cSJunLin Chen { 7470bdda665SEd Tanous if (item.first == "Configuration") 74828afb49cSJunLin Chen { 74928afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 75028afb49cSJunLin Chen .getEventServiceConfig() 7510bdda665SEd Tanous .fromJson(item.second); 75228afb49cSJunLin Chen } 7530bdda665SEd Tanous else if (item.first == "Subscriptions") 75428afb49cSJunLin Chen { 7550bdda665SEd Tanous for (const auto& elem : item.second) 75628afb49cSJunLin Chen { 75728afb49cSJunLin Chen std::shared_ptr<persistent_data::UserSubscription> 75828afb49cSJunLin Chen newSubscription = 75928afb49cSJunLin Chen persistent_data::UserSubscription::fromJson(elem, 76028afb49cSJunLin Chen true); 76128afb49cSJunLin Chen if (newSubscription == nullptr) 76228afb49cSJunLin Chen { 76362598e31SEd Tanous BMCWEB_LOG_ERROR("Problem reading subscription " 76462598e31SEd Tanous "from old persistent store"); 7654bbf237fSAppaRao Puli continue; 7664bbf237fSAppaRao Puli } 7671bf712bcSAyushi Smriti 76828afb49cSJunLin Chen std::uniform_int_distribution<uint32_t> dist(0); 76928afb49cSJunLin Chen bmcweb::OpenSSLGenerator gen; 7701bf712bcSAyushi Smriti 77128afb49cSJunLin Chen std::string id; 7721bf712bcSAyushi Smriti 77328afb49cSJunLin Chen int retry = 3; 774e662eae8SEd Tanous while (retry != 0) 7751bf712bcSAyushi Smriti { 77628afb49cSJunLin Chen id = std::to_string(dist(gen)); 77728afb49cSJunLin Chen if (gen.error()) 7787d1cc387SAppaRao Puli { 77928afb49cSJunLin Chen retry = 0; 78028afb49cSJunLin Chen break; 78128afb49cSJunLin Chen } 78228afb49cSJunLin Chen newSubscription->id = id; 78328afb49cSJunLin Chen auto inserted = 78428afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 78528afb49cSJunLin Chen .subscriptionsConfigMap.insert( 78628afb49cSJunLin Chen std::pair(id, newSubscription)); 78728afb49cSJunLin Chen if (inserted.second) 78828afb49cSJunLin Chen { 78928afb49cSJunLin Chen break; 79028afb49cSJunLin Chen } 79128afb49cSJunLin Chen --retry; 7927d1cc387SAppaRao Puli } 7937d1cc387SAppaRao Puli 79428afb49cSJunLin Chen if (retry <= 0) 79528afb49cSJunLin Chen { 79662598e31SEd Tanous BMCWEB_LOG_ERROR( 79762598e31SEd Tanous "Failed to generate random number from old " 79862598e31SEd Tanous "persistent store"); 79928afb49cSJunLin Chen continue; 80028afb49cSJunLin Chen } 80128afb49cSJunLin Chen } 80228afb49cSJunLin Chen } 80328afb49cSJunLin Chen 80428afb49cSJunLin Chen persistent_data::getConfig().writeData(); 8054c521c3cSEd Tanous std::error_code ec; 8064c521c3cSEd Tanous std::filesystem::remove(eventServiceFile, ec); 8074c521c3cSEd Tanous if (ec) 8084c521c3cSEd Tanous { 8094c521c3cSEd Tanous BMCWEB_LOG_DEBUG( 8104c521c3cSEd Tanous "Failed to remove old event service file. Ignoring"); 8114c521c3cSEd Tanous } 8124c521c3cSEd Tanous else 8134c521c3cSEd Tanous { 81462598e31SEd Tanous BMCWEB_LOG_DEBUG("Remove old eventservice config"); 81528afb49cSJunLin Chen } 81628afb49cSJunLin Chen } 8174c521c3cSEd Tanous } 81828afb49cSJunLin Chen 8199eb808c1SEd Tanous void updateSubscriptionData() const 82028afb49cSJunLin Chen { 82128afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 82228afb49cSJunLin Chen .eventServiceConfig.enabled = serviceEnabled; 82328afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 82428afb49cSJunLin Chen .eventServiceConfig.retryAttempts = retryAttempts; 82528afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 82628afb49cSJunLin Chen .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval; 82728afb49cSJunLin Chen 82828afb49cSJunLin Chen persistent_data::getConfig().writeData(); 82928afb49cSJunLin Chen } 83028afb49cSJunLin Chen 83128afb49cSJunLin Chen void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg) 8327d1cc387SAppaRao Puli { 8337d1cc387SAppaRao Puli bool updateConfig = false; 834fe44eb0bSAyushi Smriti bool updateRetryCfg = false; 8357d1cc387SAppaRao Puli 83628afb49cSJunLin Chen if (serviceEnabled != cfg.enabled) 8377d1cc387SAppaRao Puli { 83828afb49cSJunLin Chen serviceEnabled = cfg.enabled; 839e662eae8SEd Tanous if (serviceEnabled && noOfMetricReportSubscribers != 0U) 8407d1cc387SAppaRao Puli { 8417d1cc387SAppaRao Puli registerMetricReportSignal(); 8427d1cc387SAppaRao Puli } 8437d1cc387SAppaRao Puli else 8447d1cc387SAppaRao Puli { 8457d1cc387SAppaRao Puli unregisterMetricReportSignal(); 8467d1cc387SAppaRao Puli } 8477d1cc387SAppaRao Puli updateConfig = true; 8487d1cc387SAppaRao Puli } 8497d1cc387SAppaRao Puli 85028afb49cSJunLin Chen if (retryAttempts != cfg.retryAttempts) 8517d1cc387SAppaRao Puli { 85228afb49cSJunLin Chen retryAttempts = cfg.retryAttempts; 8537d1cc387SAppaRao Puli updateConfig = true; 854fe44eb0bSAyushi Smriti updateRetryCfg = true; 8557d1cc387SAppaRao Puli } 8567d1cc387SAppaRao Puli 85728afb49cSJunLin Chen if (retryTimeoutInterval != cfg.retryTimeoutInterval) 8587d1cc387SAppaRao Puli { 85928afb49cSJunLin Chen retryTimeoutInterval = cfg.retryTimeoutInterval; 8607d1cc387SAppaRao Puli updateConfig = true; 861fe44eb0bSAyushi Smriti updateRetryCfg = true; 8627d1cc387SAppaRao Puli } 8637d1cc387SAppaRao Puli 8647d1cc387SAppaRao Puli if (updateConfig) 8657d1cc387SAppaRao Puli { 8667d1cc387SAppaRao Puli updateSubscriptionData(); 8677d1cc387SAppaRao Puli } 868fe44eb0bSAyushi Smriti 869fe44eb0bSAyushi Smriti if (updateRetryCfg) 870fe44eb0bSAyushi Smriti { 871fe44eb0bSAyushi Smriti // Update the changed retry config to all subscriptions 872fe44eb0bSAyushi Smriti for (const auto& it : 873fe44eb0bSAyushi Smriti EventServiceManager::getInstance().subscriptionsMap) 874fe44eb0bSAyushi Smriti { 8755e44e3d8SAppaRao Puli Subscription& entry = *it.second; 8765e44e3d8SAppaRao Puli entry.updateRetryConfig(retryAttempts, retryTimeoutInterval); 877fe44eb0bSAyushi Smriti } 878fe44eb0bSAyushi Smriti } 8797d1cc387SAppaRao Puli } 8807d1cc387SAppaRao Puli 8817d1cc387SAppaRao Puli void updateNoOfSubscribersCount() 8827d1cc387SAppaRao Puli { 8837d1cc387SAppaRao Puli size_t eventLogSubCount = 0; 8847d1cc387SAppaRao Puli size_t metricReportSubCount = 0; 8857d1cc387SAppaRao Puli for (const auto& it : subscriptionsMap) 8867d1cc387SAppaRao Puli { 8877d1cc387SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 8887d1cc387SAppaRao Puli if (entry->eventFormatType == eventFormatType) 8897d1cc387SAppaRao Puli { 8907d1cc387SAppaRao Puli eventLogSubCount++; 8917d1cc387SAppaRao Puli } 8927d1cc387SAppaRao Puli else if (entry->eventFormatType == metricReportFormatType) 8937d1cc387SAppaRao Puli { 8947d1cc387SAppaRao Puli metricReportSubCount++; 8957d1cc387SAppaRao Puli } 8967d1cc387SAppaRao Puli } 8977d1cc387SAppaRao Puli 8987d1cc387SAppaRao Puli noOfEventLogSubscribers = eventLogSubCount; 8997d1cc387SAppaRao Puli if (noOfMetricReportSubscribers != metricReportSubCount) 9007d1cc387SAppaRao Puli { 9017d1cc387SAppaRao Puli noOfMetricReportSubscribers = metricReportSubCount; 902e662eae8SEd Tanous if (noOfMetricReportSubscribers != 0U) 9037d1cc387SAppaRao Puli { 9047d1cc387SAppaRao Puli registerMetricReportSignal(); 9057d1cc387SAppaRao Puli } 9067d1cc387SAppaRao Puli else 9077d1cc387SAppaRao Puli { 9087d1cc387SAppaRao Puli unregisterMetricReportSignal(); 9097d1cc387SAppaRao Puli } 9107d1cc387SAppaRao Puli } 9117d1cc387SAppaRao Puli } 9127d1cc387SAppaRao Puli 913b52664e2SAppaRao Puli std::shared_ptr<Subscription> getSubscription(const std::string& id) 914b52664e2SAppaRao Puli { 915b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 916b52664e2SAppaRao Puli if (obj == subscriptionsMap.end()) 917b52664e2SAppaRao Puli { 91862598e31SEd Tanous BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id); 919b52664e2SAppaRao Puli return nullptr; 920b52664e2SAppaRao Puli } 921b52664e2SAppaRao Puli std::shared_ptr<Subscription> subValue = obj->second; 922b52664e2SAppaRao Puli return subValue; 923b52664e2SAppaRao Puli } 924b52664e2SAppaRao Puli 925f80a87f2SEd Tanous std::string 926f80a87f2SEd Tanous addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue) 927b52664e2SAppaRao Puli { 928fc76b8acSEd Tanous std::uniform_int_distribution<uint32_t> dist(0); 929fc76b8acSEd Tanous bmcweb::OpenSSLGenerator gen; 930fc76b8acSEd Tanous 931b52664e2SAppaRao Puli std::string id; 932b52664e2SAppaRao Puli 933b52664e2SAppaRao Puli int retry = 3; 934e662eae8SEd Tanous while (retry != 0) 935b52664e2SAppaRao Puli { 936fc76b8acSEd Tanous id = std::to_string(dist(gen)); 937fc76b8acSEd Tanous if (gen.error()) 938fc76b8acSEd Tanous { 939fc76b8acSEd Tanous retry = 0; 940fc76b8acSEd Tanous break; 941fc76b8acSEd Tanous } 942b52664e2SAppaRao Puli auto inserted = subscriptionsMap.insert(std::pair(id, subValue)); 943b52664e2SAppaRao Puli if (inserted.second) 944b52664e2SAppaRao Puli { 945b52664e2SAppaRao Puli break; 946b52664e2SAppaRao Puli } 947b52664e2SAppaRao Puli --retry; 94823a21a1cSEd Tanous } 949b52664e2SAppaRao Puli 950b52664e2SAppaRao Puli if (retry <= 0) 951b52664e2SAppaRao Puli { 95262598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to generate random number"); 953abb93cddSEd Tanous return ""; 954b52664e2SAppaRao Puli } 955b52664e2SAppaRao Puli 95628afb49cSJunLin Chen std::shared_ptr<persistent_data::UserSubscription> newSub = 95728afb49cSJunLin Chen std::make_shared<persistent_data::UserSubscription>(); 95828afb49cSJunLin Chen newSub->id = id; 95928afb49cSJunLin Chen newSub->destinationUrl = subValue->destinationUrl; 96028afb49cSJunLin Chen newSub->protocol = subValue->protocol; 96128afb49cSJunLin Chen newSub->retryPolicy = subValue->retryPolicy; 96228afb49cSJunLin Chen newSub->customText = subValue->customText; 96328afb49cSJunLin Chen newSub->eventFormatType = subValue->eventFormatType; 96428afb49cSJunLin Chen newSub->subscriptionType = subValue->subscriptionType; 96528afb49cSJunLin Chen newSub->registryMsgIds = subValue->registryMsgIds; 96628afb49cSJunLin Chen newSub->registryPrefixes = subValue->registryPrefixes; 96728afb49cSJunLin Chen newSub->resourceTypes = subValue->resourceTypes; 96828afb49cSJunLin Chen newSub->httpHeaders = subValue->httpHeaders; 96928afb49cSJunLin Chen newSub->metricReportDefinitions = subValue->metricReportDefinitions; 970*a14c9113SEd Tanous newSub->originResources = subValue->originResources; 971*a14c9113SEd Tanous 97228afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 97328afb49cSJunLin Chen .subscriptionsConfigMap.emplace(newSub->id, newSub); 97428afb49cSJunLin Chen 9757d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 9761bf712bcSAyushi Smriti 97783328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 97883328316SEd Tanous { 9792558979cSP Dheeraj Srujan Kumar if (redfishLogFilePosition != 0) 9807f4eb588SAppaRao Puli { 9812558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 9827f4eb588SAppaRao Puli } 98383328316SEd Tanous } 984fe44eb0bSAyushi Smriti // Update retry configuration. 985fe44eb0bSAyushi Smriti subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 986fe44eb0bSAyushi Smriti 9875e44e3d8SAppaRao Puli // Set Subscription ID for back trace 9885e44e3d8SAppaRao Puli subValue->setSubscriptionId(id); 989f80a87f2SEd Tanous 990f80a87f2SEd Tanous return id; 991f80a87f2SEd Tanous } 992f80a87f2SEd Tanous 993f80a87f2SEd Tanous std::string 994f80a87f2SEd Tanous addSSESubscription(const std::shared_ptr<Subscription>& subValue, 995f80a87f2SEd Tanous std::string_view lastEventId) 996f80a87f2SEd Tanous { 997f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 998f80a87f2SEd Tanous 999f80a87f2SEd Tanous if (!lastEventId.empty()) 1000f80a87f2SEd Tanous { 1001f80a87f2SEd Tanous BMCWEB_LOG_INFO("Attempting to find message for last id {}", 1002f80a87f2SEd Tanous lastEventId); 1003f80a87f2SEd Tanous boost::circular_buffer<Event>::iterator lastEvent = 1004f80a87f2SEd Tanous std::find_if(messages.begin(), messages.end(), 1005f80a87f2SEd Tanous [&lastEventId](const Event& event) { 1006f80a87f2SEd Tanous return event.id == lastEventId; 1007f80a87f2SEd Tanous }); 1008f80a87f2SEd Tanous // Can't find a matching ID 1009f80a87f2SEd Tanous if (lastEvent == messages.end()) 1010f80a87f2SEd Tanous { 1011f80a87f2SEd Tanous nlohmann::json msg = messages::eventBufferExceeded(); 1012f80a87f2SEd Tanous // If the buffer overloaded, send all messages. 10136d799e14SEd Tanous subValue->sendEventToSubscriber(msg); 1014f80a87f2SEd Tanous lastEvent = messages.begin(); 1015f80a87f2SEd Tanous } 1016f80a87f2SEd Tanous else 1017f80a87f2SEd Tanous { 1018f80a87f2SEd Tanous // Skip the last event the user already has 1019f80a87f2SEd Tanous lastEvent++; 1020f80a87f2SEd Tanous } 1021f80a87f2SEd Tanous 1022f80a87f2SEd Tanous for (boost::circular_buffer<Event>::const_iterator event = 1023f80a87f2SEd Tanous lastEvent; 1024f80a87f2SEd Tanous lastEvent != messages.end(); lastEvent++) 1025f80a87f2SEd Tanous { 10266d799e14SEd Tanous subValue->sendEventToSubscriber(event->message); 1027f80a87f2SEd Tanous } 1028f80a87f2SEd Tanous } 1029f80a87f2SEd Tanous return id; 1030f80a87f2SEd Tanous } 1031f80a87f2SEd Tanous 1032f80a87f2SEd Tanous std::string 1033f80a87f2SEd Tanous addPushSubscription(const std::shared_ptr<Subscription>& subValue) 1034f80a87f2SEd Tanous { 1035f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 1036f80a87f2SEd Tanous 1037f80a87f2SEd Tanous updateSubscriptionData(); 1038b52664e2SAppaRao Puli return id; 1039b52664e2SAppaRao Puli } 1040b52664e2SAppaRao Puli 1041b52664e2SAppaRao Puli bool isSubscriptionExist(const std::string& id) 1042b52664e2SAppaRao Puli { 1043b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 104455f79e6fSEd Tanous return obj != subscriptionsMap.end(); 1045b52664e2SAppaRao Puli } 1046b52664e2SAppaRao Puli 1047b52664e2SAppaRao Puli void deleteSubscription(const std::string& id) 1048b52664e2SAppaRao Puli { 1049b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 1050b52664e2SAppaRao Puli if (obj != subscriptionsMap.end()) 1051b52664e2SAppaRao Puli { 1052b52664e2SAppaRao Puli subscriptionsMap.erase(obj); 105328afb49cSJunLin Chen auto obj2 = persistent_data::EventServiceStore::getInstance() 105428afb49cSJunLin Chen .subscriptionsConfigMap.find(id); 105528afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 105628afb49cSJunLin Chen .subscriptionsConfigMap.erase(obj2); 10577d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 1058b52664e2SAppaRao Puli updateSubscriptionData(); 1059b52664e2SAppaRao Puli } 1060b52664e2SAppaRao Puli } 1061b52664e2SAppaRao Puli 10625e44e3d8SAppaRao Puli void deleteSseSubscription(const crow::sse_socket::Connection& thisConn) 10635e44e3d8SAppaRao Puli { 1064bdbfae2aSEd Tanous for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();) 10655e44e3d8SAppaRao Puli { 1066bdbfae2aSEd Tanous std::shared_ptr<Subscription> entry = it->second; 10675e44e3d8SAppaRao Puli bool entryIsThisConn = entry->matchSseId(thisConn); 10685e44e3d8SAppaRao Puli if (entryIsThisConn) 10695e44e3d8SAppaRao Puli { 10705e44e3d8SAppaRao Puli persistent_data::EventServiceStore::getInstance() 10715e44e3d8SAppaRao Puli .subscriptionsConfigMap.erase( 1072bdbfae2aSEd Tanous it->second->getSubscriptionId()); 1073bdbfae2aSEd Tanous it = subscriptionsMap.erase(it); 10745e44e3d8SAppaRao Puli return; 10755e44e3d8SAppaRao Puli } 1076bdbfae2aSEd Tanous it++; 10775e44e3d8SAppaRao Puli } 10785e44e3d8SAppaRao Puli } 10795e44e3d8SAppaRao Puli 10805e44e3d8SAppaRao Puli size_t getNumberOfSubscriptions() const 1081b52664e2SAppaRao Puli { 1082b52664e2SAppaRao Puli return subscriptionsMap.size(); 1083b52664e2SAppaRao Puli } 1084b52664e2SAppaRao Puli 10855e44e3d8SAppaRao Puli size_t getNumberOfSSESubscriptions() const 10865e44e3d8SAppaRao Puli { 10873544d2a7SEd Tanous auto size = std::ranges::count_if( 10883544d2a7SEd Tanous subscriptionsMap, 10895e44e3d8SAppaRao Puli [](const std::pair<std::string, std::shared_ptr<Subscription>>& 10905e44e3d8SAppaRao Puli entry) { 10915e44e3d8SAppaRao Puli return (entry.second->subscriptionType == subscriptionTypeSSE); 10925e44e3d8SAppaRao Puli }); 10935e44e3d8SAppaRao Puli return static_cast<size_t>(size); 10945e44e3d8SAppaRao Puli } 10955e44e3d8SAppaRao Puli 1096b52664e2SAppaRao Puli std::vector<std::string> getAllIDs() 1097b52664e2SAppaRao Puli { 1098b52664e2SAppaRao Puli std::vector<std::string> idList; 1099b52664e2SAppaRao Puli for (const auto& it : subscriptionsMap) 1100b52664e2SAppaRao Puli { 1101b52664e2SAppaRao Puli idList.emplace_back(it.first); 1102b52664e2SAppaRao Puli } 1103b52664e2SAppaRao Puli return idList; 1104b52664e2SAppaRao Puli } 1105b52664e2SAppaRao Puli 11066ba8c82eSsunharis_in bool sendTestEventLog() 11070b4bdd93SAppaRao Puli { 11085e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 11090b4bdd93SAppaRao Puli { 11100b4bdd93SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 11116ba8c82eSsunharis_in if (!entry->sendTestEventLog()) 11126ba8c82eSsunharis_in { 11136ba8c82eSsunharis_in return false; 11140b4bdd93SAppaRao Puli } 11150b4bdd93SAppaRao Puli } 11166ba8c82eSsunharis_in return true; 11176ba8c82eSsunharis_in } 1118e9a14131SAppaRao Puli 1119f80a87f2SEd Tanous void sendEvent(nlohmann::json::object_t eventMessage, 1120f80a87f2SEd Tanous std::string_view origin, std::string_view resourceType) 112196330b99SSunitha Harish { 1122613dabeaSEd Tanous eventMessage["EventId"] = eventId; 1123f80a87f2SEd Tanous 1124613dabeaSEd Tanous eventMessage["EventTimestamp"] = 1125613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 1126613dabeaSEd Tanous eventMessage["OriginOfCondition"] = origin; 1127613dabeaSEd Tanous 1128f80a87f2SEd Tanous // MemberId is 0 : since we are sending one event record. 1129f80a87f2SEd Tanous eventMessage["MemberId"] = 0; 113096330b99SSunitha Harish 1131f80a87f2SEd Tanous messages.push_back(Event(std::to_string(eventId), eventMessage)); 1132f80a87f2SEd Tanous 1133f80a87f2SEd Tanous for (auto& it : subscriptionsMap) 113496330b99SSunitha Harish { 1135f80a87f2SEd Tanous std::shared_ptr<Subscription>& entry = it.second; 1136f80a87f2SEd Tanous if (!entry->eventMatchesFilter(eventMessage, resourceType)) 113796330b99SSunitha Harish { 1138f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 1139f80a87f2SEd Tanous continue; 114096330b99SSunitha Harish } 1141f80a87f2SEd Tanous 1142f80a87f2SEd Tanous nlohmann::json::array_t eventRecord; 1143f80a87f2SEd Tanous eventRecord.emplace_back(eventMessage); 1144f80a87f2SEd Tanous 1145613dabeaSEd Tanous nlohmann::json msgJson; 1146613dabeaSEd Tanous 1147613dabeaSEd Tanous msgJson["@odata.type"] = "#Event.v1_4_0.Event"; 1148613dabeaSEd Tanous msgJson["Name"] = "Event Log"; 1149613dabeaSEd Tanous msgJson["Id"] = eventId; 1150f80a87f2SEd Tanous msgJson["Events"] = std::move(eventRecord); 1151f52c03c1SCarson Labrado 1152f52c03c1SCarson Labrado std::string strMsg = msgJson.dump( 1153f52c03c1SCarson Labrado 2, ' ', true, nlohmann::json::error_handler_t::replace); 11546d799e14SEd Tanous entry->sendEventToSubscriber(std::move(strMsg)); 11558ece0e45SEd Tanous eventId++; // increment the eventId 115696330b99SSunitha Harish } 115796330b99SSunitha Harish } 115896330b99SSunitha Harish 11592558979cSP Dheeraj Srujan Kumar void resetRedfishFilePosition() 11607f4eb588SAppaRao Puli { 11612558979cSP Dheeraj Srujan Kumar // Control would be here when Redfish file is created. 11622558979cSP Dheeraj Srujan Kumar // Reset File Position as new file is created 11632558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = 0; 11642558979cSP Dheeraj Srujan Kumar } 11652558979cSP Dheeraj Srujan Kumar 11662558979cSP Dheeraj Srujan Kumar void cacheRedfishLogFile() 11672558979cSP Dheeraj Srujan Kumar { 11682558979cSP Dheeraj Srujan Kumar // Open the redfish file and read till the last record. 11692558979cSP Dheeraj Srujan Kumar 11707f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 11717f4eb588SAppaRao Puli if (!logStream.good()) 11727f4eb588SAppaRao Puli { 117362598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed "); 11747f4eb588SAppaRao Puli return; 11757f4eb588SAppaRao Puli } 11767f4eb588SAppaRao Puli std::string logEntry; 11777f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 11787f4eb588SAppaRao Puli { 11792558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 11807f4eb588SAppaRao Puli } 11817f4eb588SAppaRao Puli } 11827f4eb588SAppaRao Puli 11837f4eb588SAppaRao Puli void readEventLogsFromFile() 11847f4eb588SAppaRao Puli { 11857f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 11867f4eb588SAppaRao Puli if (!logStream.good()) 11877f4eb588SAppaRao Puli { 118862598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed"); 11897f4eb588SAppaRao Puli return; 11907f4eb588SAppaRao Puli } 11917f4eb588SAppaRao Puli 11927f4eb588SAppaRao Puli std::vector<EventLogObjectsType> eventRecords; 11937f4eb588SAppaRao Puli 11947f4eb588SAppaRao Puli std::string logEntry; 11952558979cSP Dheeraj Srujan Kumar 119603d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: seek to {}", 119703d4d37cSAlexander Hansen static_cast<int>(redfishLogFilePosition)); 119803d4d37cSAlexander Hansen 11992558979cSP Dheeraj Srujan Kumar // Get the read pointer to the next log to be read. 12002558979cSP Dheeraj Srujan Kumar logStream.seekg(redfishLogFilePosition); 12012558979cSP Dheeraj Srujan Kumar 12027f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 12037f4eb588SAppaRao Puli { 120403d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry"); 12052558979cSP Dheeraj Srujan Kumar // Update Pointer position 12062558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 12072558979cSP Dheeraj Srujan Kumar 12082558979cSP Dheeraj Srujan Kumar std::string idStr; 12092558979cSP Dheeraj Srujan Kumar if (!event_log::getUniqueEntryID(logEntry, idStr)) 12107f4eb588SAppaRao Puli { 121103d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 121203d4d37cSAlexander Hansen "Redfish log file: could not get unique entry id for {}", 121303d4d37cSAlexander Hansen logEntry); 12147f4eb588SAppaRao Puli continue; 12157f4eb588SAppaRao Puli } 12167f4eb588SAppaRao Puli 1217e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 12187f4eb588SAppaRao Puli { 12192558979cSP Dheeraj Srujan Kumar // If Service is not enabled, no need to compute 12202558979cSP Dheeraj Srujan Kumar // the remaining items below. 12212558979cSP Dheeraj Srujan Kumar // But, Loop must continue to keep track of Timestamp 122203d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 122303d4d37cSAlexander Hansen "Redfish log file: no subscribers / event service not enabled"); 12247f4eb588SAppaRao Puli continue; 12257f4eb588SAppaRao Puli } 12267f4eb588SAppaRao Puli 12277f4eb588SAppaRao Puli std::string timestamp; 12287f4eb588SAppaRao Puli std::string messageID; 12295e715de6SAppaRao Puli std::vector<std::string> messageArgs; 12307f4eb588SAppaRao Puli if (event_log::getEventLogParams(logEntry, timestamp, messageID, 12317f4eb588SAppaRao Puli messageArgs) != 0) 12327f4eb588SAppaRao Puli { 123303d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}", 123403d4d37cSAlexander Hansen logEntry); 12357f4eb588SAppaRao Puli continue; 12367f4eb588SAppaRao Puli } 12377f4eb588SAppaRao Puli 1238f80a87f2SEd Tanous eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs); 12397f4eb588SAppaRao Puli } 12407f4eb588SAppaRao Puli 1241e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 12422558979cSP Dheeraj Srujan Kumar { 124362598e31SEd Tanous BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions."); 12442558979cSP Dheeraj Srujan Kumar return; 12452558979cSP Dheeraj Srujan Kumar } 12462558979cSP Dheeraj Srujan Kumar 12472558979cSP Dheeraj Srujan Kumar if (eventRecords.empty()) 12482558979cSP Dheeraj Srujan Kumar { 12492558979cSP Dheeraj Srujan Kumar // No Records to send 125062598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 12512558979cSP Dheeraj Srujan Kumar return; 12522558979cSP Dheeraj Srujan Kumar } 12532558979cSP Dheeraj Srujan Kumar 12545e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 12557f4eb588SAppaRao Puli { 12567f4eb588SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 12577f4eb588SAppaRao Puli if (entry->eventFormatType == "Event") 12587f4eb588SAppaRao Puli { 12597f4eb588SAppaRao Puli entry->filterAndSendEventLogs(eventRecords); 12607f4eb588SAppaRao Puli } 12617f4eb588SAppaRao Puli } 12627f4eb588SAppaRao Puli } 12637f4eb588SAppaRao Puli 12647f4eb588SAppaRao Puli static void watchRedfishEventLogFile() 12657f4eb588SAppaRao Puli { 12666a9f85f9SAppaRao Puli if (!inotifyConn) 12677f4eb588SAppaRao Puli { 126803d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("inotify Connection is not present"); 12697f4eb588SAppaRao Puli return; 12707f4eb588SAppaRao Puli } 12717f4eb588SAppaRao Puli 12727f4eb588SAppaRao Puli static std::array<char, 1024> readBuffer; 12737f4eb588SAppaRao Puli 1274bd79bce8SPatrick Williams inotifyConn->async_read_some( 1275bd79bce8SPatrick Williams boost::asio::buffer(readBuffer), 12767f4eb588SAppaRao Puli [&](const boost::system::error_code& ec, 12777f4eb588SAppaRao Puli const std::size_t& bytesTransferred) { 12789ed3f90aSEd Tanous if (ec == boost::asio::error::operation_aborted) 12799ed3f90aSEd Tanous { 12809ed3f90aSEd Tanous BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)"); 12819ed3f90aSEd Tanous return; 12829ed3f90aSEd Tanous } 12837f4eb588SAppaRao Puli if (ec) 12847f4eb588SAppaRao Puli { 128562598e31SEd Tanous BMCWEB_LOG_ERROR("Callback Error: {}", ec.message()); 12867f4eb588SAppaRao Puli return; 12877f4eb588SAppaRao Puli } 128803d4d37cSAlexander Hansen 128903d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred); 129003d4d37cSAlexander Hansen 12917f4eb588SAppaRao Puli std::size_t index = 0; 1292b792cc56SAppaRao Puli while ((index + iEventSize) <= bytesTransferred) 12937f4eb588SAppaRao Puli { 1294d3a9e084SEd Tanous struct inotify_event event 1295d3a9e084SEd Tanous {}; 1296b792cc56SAppaRao Puli std::memcpy(&event, &readBuffer[index], iEventSize); 1297b792cc56SAppaRao Puli if (event.wd == dirWatchDesc) 1298b792cc56SAppaRao Puli { 1299b792cc56SAppaRao Puli if ((event.len == 0) || 1300b792cc56SAppaRao Puli (index + iEventSize + event.len > bytesTransferred)) 1301b792cc56SAppaRao Puli { 1302b792cc56SAppaRao Puli index += (iEventSize + event.len); 1303b792cc56SAppaRao Puli continue; 1304b792cc56SAppaRao Puli } 1305b792cc56SAppaRao Puli 13064f568f74SJiaqing Zhao std::string fileName(&readBuffer[index + iEventSize]); 13074f568f74SJiaqing Zhao if (fileName != "redfish") 1308b792cc56SAppaRao Puli { 1309b792cc56SAppaRao Puli index += (iEventSize + event.len); 1310b792cc56SAppaRao Puli continue; 1311b792cc56SAppaRao Puli } 1312b792cc56SAppaRao Puli 131362598e31SEd Tanous BMCWEB_LOG_DEBUG( 131462598e31SEd Tanous "Redfish log file created/deleted. event.name: {}", 131562598e31SEd Tanous fileName); 1316b792cc56SAppaRao Puli if (event.mask == IN_CREATE) 1317b792cc56SAppaRao Puli { 1318b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1319b792cc56SAppaRao Puli { 132062598e31SEd Tanous BMCWEB_LOG_DEBUG( 132162598e31SEd Tanous "Remove and Add inotify watcher on " 132262598e31SEd Tanous "redfish event log file"); 1323016761afSAppaRao Puli // Remove existing inotify watcher and add 1324016761afSAppaRao Puli // with new redfish event log file. 1325016761afSAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1326016761afSAppaRao Puli fileWatchDesc = -1; 1327b792cc56SAppaRao Puli } 1328b792cc56SAppaRao Puli 1329b792cc56SAppaRao Puli fileWatchDesc = inotify_add_watch( 1330b792cc56SAppaRao Puli inotifyFd, redfishEventLogFile, IN_MODIFY); 1331b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1332b792cc56SAppaRao Puli { 133362598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for " 133462598e31SEd Tanous "redfish log file."); 1335b792cc56SAppaRao Puli return; 1336b792cc56SAppaRao Puli } 1337b792cc56SAppaRao Puli 1338b792cc56SAppaRao Puli EventServiceManager::getInstance() 13392558979cSP Dheeraj Srujan Kumar .resetRedfishFilePosition(); 1340b792cc56SAppaRao Puli EventServiceManager::getInstance() 1341b792cc56SAppaRao Puli .readEventLogsFromFile(); 1342b792cc56SAppaRao Puli } 1343b792cc56SAppaRao Puli else if ((event.mask == IN_DELETE) || 1344b792cc56SAppaRao Puli (event.mask == IN_MOVED_TO)) 1345b792cc56SAppaRao Puli { 1346b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1347b792cc56SAppaRao Puli { 1348b792cc56SAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1349b792cc56SAppaRao Puli fileWatchDesc = -1; 1350b792cc56SAppaRao Puli } 1351b792cc56SAppaRao Puli } 1352b792cc56SAppaRao Puli } 1353b792cc56SAppaRao Puli else if (event.wd == fileWatchDesc) 1354b792cc56SAppaRao Puli { 1355b792cc56SAppaRao Puli if (event.mask == IN_MODIFY) 13567f4eb588SAppaRao Puli { 13577f4eb588SAppaRao Puli EventServiceManager::getInstance() 13587f4eb588SAppaRao Puli .readEventLogsFromFile(); 13597f4eb588SAppaRao Puli } 1360b792cc56SAppaRao Puli } 1361b792cc56SAppaRao Puli index += (iEventSize + event.len); 13627f4eb588SAppaRao Puli } 13637f4eb588SAppaRao Puli 13647f4eb588SAppaRao Puli watchRedfishEventLogFile(); 13657f4eb588SAppaRao Puli }); 13667f4eb588SAppaRao Puli } 13677f4eb588SAppaRao Puli 13687f4eb588SAppaRao Puli static int startEventLogMonitor(boost::asio::io_context& ioc) 13697f4eb588SAppaRao Puli { 137003d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 137103d4d37cSAlexander Hansen 137223a21a1cSEd Tanous inotifyConn.emplace(ioc); 1373b792cc56SAppaRao Puli inotifyFd = inotify_init1(IN_NONBLOCK); 1374b792cc56SAppaRao Puli if (inotifyFd == -1) 13757f4eb588SAppaRao Puli { 137662598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_init1 failed."); 13777f4eb588SAppaRao Puli return -1; 13787f4eb588SAppaRao Puli } 1379b792cc56SAppaRao Puli 1380b792cc56SAppaRao Puli // Add watch on directory to handle redfish event log file 1381b792cc56SAppaRao Puli // create/delete. 1382b792cc56SAppaRao Puli dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 1383b792cc56SAppaRao Puli IN_CREATE | IN_MOVED_TO | IN_DELETE); 1384b792cc56SAppaRao Puli if (dirWatchDesc == -1) 13857f4eb588SAppaRao Puli { 138662598e31SEd Tanous BMCWEB_LOG_ERROR( 138762598e31SEd Tanous "inotify_add_watch failed for event log directory."); 13887f4eb588SAppaRao Puli return -1; 13897f4eb588SAppaRao Puli } 13907f4eb588SAppaRao Puli 1391b792cc56SAppaRao Puli // Watch redfish event log file for modifications. 1392bd79bce8SPatrick Williams fileWatchDesc = 1393bd79bce8SPatrick Williams inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 1394b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1395b792cc56SAppaRao Puli { 139662598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 1397b792cc56SAppaRao Puli // Don't return error if file not exist. 1398b792cc56SAppaRao Puli // Watch on directory will handle create/delete of file. 1399b792cc56SAppaRao Puli } 1400b792cc56SAppaRao Puli 14017f4eb588SAppaRao Puli // monitor redfish event log file 1402b792cc56SAppaRao Puli inotifyConn->assign(inotifyFd); 14037f4eb588SAppaRao Puli watchRedfishEventLogFile(); 14047f4eb588SAppaRao Puli 14057f4eb588SAppaRao Puli return 0; 14067f4eb588SAppaRao Puli } 14077f4eb588SAppaRao Puli 14089ed3f90aSEd Tanous static void stopEventLogMonitor() 14099ed3f90aSEd Tanous { 14109ed3f90aSEd Tanous inotifyConn.reset(); 14119ed3f90aSEd Tanous } 14129ed3f90aSEd Tanous 141359d494eeSPatrick Williams static void getReadingsForReport(sdbusplus::message_t& msg) 1414156d6b00SAppaRao Puli { 141556d2396dSEd Tanous if (msg.is_method_error()) 141656d2396dSEd Tanous { 141762598e31SEd Tanous BMCWEB_LOG_ERROR("TelemetryMonitor Signal error"); 141856d2396dSEd Tanous return; 141956d2396dSEd Tanous } 142056d2396dSEd Tanous 1421c0353249SWludzik, Jozef sdbusplus::message::object_path path(msg.get_path()); 1422c0353249SWludzik, Jozef std::string id = path.filename(); 1423c0353249SWludzik, Jozef if (id.empty()) 1424156d6b00SAppaRao Puli { 142562598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to get Id from path"); 1426156d6b00SAppaRao Puli return; 1427156d6b00SAppaRao Puli } 1428156d6b00SAppaRao Puli 1429c0353249SWludzik, Jozef std::string interface; 1430b9d36b47SEd Tanous dbus::utility::DBusPropertiesMap props; 1431c0353249SWludzik, Jozef std::vector<std::string> invalidProps; 1432c0353249SWludzik, Jozef msg.read(interface, props, invalidProps); 1433c0353249SWludzik, Jozef 1434bd79bce8SPatrick Williams auto found = std::ranges::find_if(props, [](const auto& x) { 1435bd79bce8SPatrick Williams return x.first == "Readings"; 1436bd79bce8SPatrick Williams }); 1437c0353249SWludzik, Jozef if (found == props.end()) 1438156d6b00SAppaRao Puli { 143962598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 1440156d6b00SAppaRao Puli return; 1441156d6b00SAppaRao Puli } 1442156d6b00SAppaRao Puli 14431e1e598dSJonathan Doman const telemetry::TimestampReadings* readings = 14441e1e598dSJonathan Doman std::get_if<telemetry::TimestampReadings>(&found->second); 1445e662eae8SEd Tanous if (readings == nullptr) 14461e1e598dSJonathan Doman { 144762598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 14481e1e598dSJonathan Doman return; 14491e1e598dSJonathan Doman } 14501e1e598dSJonathan Doman 1451156d6b00SAppaRao Puli for (const auto& it : 1452156d6b00SAppaRao Puli EventServiceManager::getInstance().subscriptionsMap) 1453156d6b00SAppaRao Puli { 1454e05aec50SEd Tanous Subscription& entry = *it.second; 1455c0353249SWludzik, Jozef if (entry.eventFormatType == metricReportFormatType) 1456156d6b00SAppaRao Puli { 14571e1e598dSJonathan Doman entry.filterAndSendReports(id, *readings); 1458156d6b00SAppaRao Puli } 1459156d6b00SAppaRao Puli } 1460156d6b00SAppaRao Puli } 1461156d6b00SAppaRao Puli 1462156d6b00SAppaRao Puli void unregisterMetricReportSignal() 1463156d6b00SAppaRao Puli { 14647d1cc387SAppaRao Puli if (matchTelemetryMonitor) 14657d1cc387SAppaRao Puli { 146662598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Unregister"); 1467156d6b00SAppaRao Puli matchTelemetryMonitor.reset(); 1468156d6b00SAppaRao Puli matchTelemetryMonitor = nullptr; 1469156d6b00SAppaRao Puli } 14707d1cc387SAppaRao Puli } 1471156d6b00SAppaRao Puli 1472156d6b00SAppaRao Puli void registerMetricReportSignal() 1473156d6b00SAppaRao Puli { 14747d1cc387SAppaRao Puli if (!serviceEnabled || matchTelemetryMonitor) 1475156d6b00SAppaRao Puli { 147662598e31SEd Tanous BMCWEB_LOG_DEBUG("Not registering metric report signal."); 1477156d6b00SAppaRao Puli return; 1478156d6b00SAppaRao Puli } 1479156d6b00SAppaRao Puli 148062598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Register"); 1481c0353249SWludzik, Jozef std::string matchStr = "type='signal',member='PropertiesChanged'," 1482c0353249SWludzik, Jozef "interface='org.freedesktop.DBus.Properties'," 1483c0353249SWludzik, Jozef "arg0=xyz.openbmc_project.Telemetry.Report"; 1484156d6b00SAppaRao Puli 148559d494eeSPatrick Williams matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>( 148656d2396dSEd Tanous *crow::connections::systemBus, matchStr, getReadingsForReport); 1487156d6b00SAppaRao Puli } 148823a21a1cSEd Tanous }; 1489b52664e2SAppaRao Puli 1490b52664e2SAppaRao Puli } // namespace redfish 1491