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> 46a14c9113SEd Tanous #include <format> 471bf712bcSAyushi Smriti #include <fstream> 48b52664e2SAppaRao Puli #include <memory> 493544d2a7SEd Tanous #include <ranges> 5026702d01SEd Tanous #include <span> 51a14c9113SEd 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 270*4b712a29SEd Tanous class Subscription 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 { 282*4b712a29SEd Tanous userSub.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 { 306*4b712a29SEd Tanous client->sendData(std::move(msg), userSub.destinationUrl, 307*4b712a29SEd Tanous static_cast<ensuressl::VerifyCertificate>( 308*4b712a29SEd Tanous userSub.verifyCertificate), 309*4b712a29SEd Tanous userSub.httpHeaders, 310*4b712a29SEd Tanous boost::beast::http::verb::post); 3115e44e3d8SAppaRao Puli return true; 3125e44e3d8SAppaRao Puli } 3137adb85acSSunitha Harish 3144bbf237fSAppaRao Puli if (sseConn != nullptr) 3154bbf237fSAppaRao Puli { 3165e44e3d8SAppaRao Puli eventSeqNum++; 3176d799e14SEd Tanous sseConn->sendSseEvent(std::to_string(eventSeqNum), msg); 3184bbf237fSAppaRao Puli } 3196ba8c82eSsunharis_in return true; 3204bbf237fSAppaRao Puli } 3214bbf237fSAppaRao Puli 322f80a87f2SEd Tanous bool eventMatchesFilter(const nlohmann::json::object_t& eventMessage, 323f80a87f2SEd Tanous std::string_view resType) 324f80a87f2SEd Tanous { 325f80a87f2SEd Tanous // If resourceTypes list is empty, assume all 326*4b712a29SEd Tanous if (!userSub.resourceTypes.empty()) 327f80a87f2SEd Tanous { 328f80a87f2SEd Tanous // Search the resourceTypes list for the subscription. 329f80a87f2SEd Tanous auto resourceTypeIndex = std::ranges::find_if( 330*4b712a29SEd Tanous userSub.resourceTypes, [resType](const std::string& rtEntry) { 331f80a87f2SEd Tanous return rtEntry == resType; 332f80a87f2SEd Tanous }); 333*4b712a29SEd Tanous if (resourceTypeIndex == userSub.resourceTypes.end()) 334f80a87f2SEd Tanous { 335f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Not subscribed to this resource"); 336f80a87f2SEd Tanous return false; 337f80a87f2SEd Tanous } 338f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("ResourceType {} found in the subscribed list", 339f80a87f2SEd Tanous resType); 340f80a87f2SEd Tanous } 341f80a87f2SEd Tanous 342a14c9113SEd Tanous // If registryPrefixes list is empty, don't filter events 343a14c9113SEd Tanous // send everything. 344*4b712a29SEd Tanous if (!userSub.registryPrefixes.empty()) 345a14c9113SEd Tanous { 346a14c9113SEd Tanous auto eventJson = eventMessage.find("MessageId"); 347a14c9113SEd Tanous if (eventJson == eventMessage.end()) 348a14c9113SEd Tanous { 349a14c9113SEd Tanous return false; 350a14c9113SEd Tanous } 351a14c9113SEd Tanous 352a14c9113SEd Tanous const std::string* messageId = 353a14c9113SEd Tanous eventJson->second.get_ptr<const std::string*>(); 354a14c9113SEd Tanous if (messageId == nullptr) 355a14c9113SEd Tanous { 356a14c9113SEd Tanous BMCWEB_LOG_ERROR("MessageId wasn't a string???"); 357a14c9113SEd Tanous return false; 358a14c9113SEd Tanous } 359a14c9113SEd Tanous 360a14c9113SEd Tanous std::string registry; 361a14c9113SEd Tanous std::string messageKey; 362a14c9113SEd Tanous event_log::getRegistryAndMessageKey(*messageId, registry, 363a14c9113SEd Tanous messageKey); 364a14c9113SEd Tanous 365*4b712a29SEd Tanous auto obj = std::ranges::find(userSub.registryPrefixes, registry); 366*4b712a29SEd Tanous if (obj == userSub.registryPrefixes.end()) 367a14c9113SEd Tanous { 368a14c9113SEd Tanous return false; 369a14c9113SEd Tanous } 370a14c9113SEd Tanous } 371a14c9113SEd Tanous 372*4b712a29SEd Tanous if (!userSub.originResources.empty()) 373a14c9113SEd Tanous { 374a14c9113SEd Tanous auto eventJson = eventMessage.find("OriginOfCondition"); 375a14c9113SEd Tanous if (eventJson == eventMessage.end()) 376a14c9113SEd Tanous { 377a14c9113SEd Tanous return false; 378a14c9113SEd Tanous } 379a14c9113SEd Tanous 380a14c9113SEd Tanous const std::string* originOfCondition = 381a14c9113SEd Tanous eventJson->second.get_ptr<const std::string*>(); 382a14c9113SEd Tanous if (originOfCondition == nullptr) 383a14c9113SEd Tanous { 384a14c9113SEd Tanous BMCWEB_LOG_ERROR("OriginOfCondition wasn't a string???"); 385a14c9113SEd Tanous return false; 386a14c9113SEd Tanous } 387a14c9113SEd Tanous 388*4b712a29SEd Tanous auto obj = 389*4b712a29SEd Tanous std::ranges::find(userSub.originResources, *originOfCondition); 390a14c9113SEd Tanous 391*4b712a29SEd Tanous if (obj == userSub.originResources.end()) 392a14c9113SEd Tanous { 393a14c9113SEd Tanous return false; 394a14c9113SEd Tanous } 395a14c9113SEd Tanous } 396a14c9113SEd Tanous 397f80a87f2SEd Tanous // If registryMsgIds list is empty, assume all 398*4b712a29SEd Tanous if (!userSub.registryMsgIds.empty()) 399f80a87f2SEd Tanous { 400f80a87f2SEd Tanous auto eventJson = eventMessage.find("MessageId"); 401f80a87f2SEd Tanous if (eventJson == eventMessage.end()) 402f80a87f2SEd Tanous { 40303d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("'MessageId' not present"); 404f80a87f2SEd Tanous return false; 405f80a87f2SEd Tanous } 406f80a87f2SEd Tanous 407f80a87f2SEd Tanous const std::string* messageId = 408f80a87f2SEd Tanous eventJson->second.get_ptr<const std::string*>(); 409f80a87f2SEd Tanous if (messageId == nullptr) 410f80a87f2SEd Tanous { 411f80a87f2SEd Tanous BMCWEB_LOG_ERROR("EventType wasn't a string???"); 412f80a87f2SEd Tanous return false; 413f80a87f2SEd Tanous } 414f80a87f2SEd Tanous 415f80a87f2SEd Tanous std::string registry; 416f80a87f2SEd Tanous std::string messageKey; 417f80a87f2SEd Tanous event_log::getRegistryAndMessageKey(*messageId, registry, 418f80a87f2SEd Tanous messageKey); 419f80a87f2SEd Tanous 42003d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("extracted registry {}", registry); 42103d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("extracted message key {}", messageKey); 42203d4d37cSAlexander Hansen 423*4b712a29SEd Tanous auto obj = 424*4b712a29SEd Tanous std::ranges::find(userSub.registryMsgIds, 425*4b712a29SEd Tanous std::format("{}.{}", registry, messageKey)); 426*4b712a29SEd Tanous if (obj == userSub.registryMsgIds.end()) 427f80a87f2SEd Tanous { 42803d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("did not find registry {} in registryMsgIds", 42903d4d37cSAlexander Hansen registry); 43003d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("registryMsgIds has {} entries", 431*4b712a29SEd Tanous userSub.registryMsgIds.size()); 432f80a87f2SEd Tanous return false; 433f80a87f2SEd Tanous } 434f80a87f2SEd Tanous } 435f80a87f2SEd Tanous 436f80a87f2SEd Tanous if (filter) 437f80a87f2SEd Tanous { 438f80a87f2SEd Tanous if (!memberMatches(eventMessage, *filter)) 439f80a87f2SEd Tanous { 440f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 441f80a87f2SEd Tanous return false; 442f80a87f2SEd Tanous } 443f80a87f2SEd Tanous } 444f80a87f2SEd Tanous 445f80a87f2SEd Tanous return true; 446f80a87f2SEd Tanous } 447f80a87f2SEd Tanous 4486ba8c82eSsunharis_in bool sendTestEventLog() 4490b4bdd93SAppaRao Puli { 450f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 451f80a87f2SEd Tanous nlohmann::json& logEntryJson = logEntryArray.emplace_back(); 4520b4bdd93SAppaRao Puli 453613dabeaSEd Tanous logEntryJson["EventId"] = "TestID"; 454539d8c6bSEd Tanous logEntryJson["Severity"] = log_entry::EventSeverity::OK; 455613dabeaSEd Tanous logEntryJson["Message"] = "Generated test event"; 456613dabeaSEd Tanous logEntryJson["MessageId"] = "OpenBMC.0.2.TestEventLog"; 457d2cdd478SChandra Harkude // MemberId is 0 : since we are sending one event record. 458d2cdd478SChandra Harkude logEntryJson["MemberId"] = 0; 459613dabeaSEd Tanous logEntryJson["MessageArgs"] = nlohmann::json::array(); 460613dabeaSEd Tanous logEntryJson["EventTimestamp"] = 461613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 462*4b712a29SEd Tanous logEntryJson["Context"] = userSub.customText; 4630b4bdd93SAppaRao Puli 4641476687dSEd Tanous nlohmann::json msg; 4651476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 4661476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 4671476687dSEd Tanous msg["Name"] = "Event Log"; 4681476687dSEd Tanous msg["Events"] = logEntryArray; 4690b4bdd93SAppaRao Puli 470bd79bce8SPatrick Williams std::string strMsg = 471bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 4726d799e14SEd Tanous return sendEventToSubscriber(std::move(strMsg)); 4730b4bdd93SAppaRao Puli } 4740b4bdd93SAppaRao Puli 4757f4eb588SAppaRao Puli void filterAndSendEventLogs( 4767f4eb588SAppaRao Puli const std::vector<EventLogObjectsType>& eventRecords) 4777f4eb588SAppaRao Puli { 478f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 4797f4eb588SAppaRao Puli for (const EventLogObjectsType& logEntry : eventRecords) 4807f4eb588SAppaRao Puli { 481f80a87f2SEd Tanous std::vector<std::string_view> messageArgsView( 482f80a87f2SEd Tanous logEntry.messageArgs.begin(), logEntry.messageArgs.end()); 4837f4eb588SAppaRao Puli 484f80a87f2SEd Tanous nlohmann::json::object_t bmcLogEntry; 485f80a87f2SEd Tanous if (event_log::formatEventLogEntry( 486f80a87f2SEd Tanous logEntry.id, logEntry.messageId, messageArgsView, 487*4b712a29SEd Tanous logEntry.timestamp, userSub.customText, bmcLogEntry) != 0) 4887f4eb588SAppaRao Puli { 48962598e31SEd Tanous BMCWEB_LOG_DEBUG("Read eventLog entry failed"); 4907f4eb588SAppaRao Puli continue; 4917f4eb588SAppaRao Puli } 492f80a87f2SEd Tanous 493f80a87f2SEd Tanous if (!eventMatchesFilter(bmcLogEntry, "")) 494f80a87f2SEd Tanous { 49503d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Event {} did not match the filter", 49603d4d37cSAlexander Hansen nlohmann::json(bmcLogEntry).dump()); 497f80a87f2SEd Tanous continue; 498f80a87f2SEd Tanous } 499f80a87f2SEd Tanous 500f80a87f2SEd Tanous logEntryArray.emplace_back(std::move(bmcLogEntry)); 5017f4eb588SAppaRao Puli } 5027f4eb588SAppaRao Puli 50326f6976fSEd Tanous if (logEntryArray.empty()) 5047f4eb588SAppaRao Puli { 50562598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 5067f4eb588SAppaRao Puli return; 5077f4eb588SAppaRao Puli } 5087f4eb588SAppaRao Puli 5091476687dSEd Tanous nlohmann::json msg; 5101476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 5111476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 5121476687dSEd Tanous msg["Name"] = "Event Log"; 513f80a87f2SEd Tanous msg["Events"] = std::move(logEntryArray); 514bd79bce8SPatrick Williams std::string strMsg = 515bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 5166d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 5175e44e3d8SAppaRao Puli eventSeqNum++; 5187f4eb588SAppaRao Puli } 5197f4eb588SAppaRao Puli 520248d0230SEd Tanous void filterAndSendReports(const std::string& reportId, 5211e1e598dSJonathan Doman const telemetry::TimestampReadings& var) 522156d6b00SAppaRao Puli { 523ef4c65b7SEd Tanous boost::urls::url mrdUri = boost::urls::format( 524ef4c65b7SEd Tanous "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", 525ef4c65b7SEd Tanous reportId); 526156d6b00SAppaRao Puli 527156d6b00SAppaRao Puli // Empty list means no filter. Send everything. 528*4b712a29SEd Tanous if (!userSub.metricReportDefinitions.empty()) 529156d6b00SAppaRao Puli { 530*4b712a29SEd Tanous if (std::ranges::find(userSub.metricReportDefinitions, 531*4b712a29SEd Tanous mrdUri.buffer()) == 532*4b712a29SEd Tanous userSub.metricReportDefinitions.end()) 533156d6b00SAppaRao Puli { 534156d6b00SAppaRao Puli return; 535156d6b00SAppaRao Puli } 536156d6b00SAppaRao Puli } 537156d6b00SAppaRao Puli 538c0353249SWludzik, Jozef nlohmann::json msg; 539248d0230SEd Tanous if (!telemetry::fillReport(msg, reportId, var)) 540156d6b00SAppaRao Puli { 54162598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to fill the MetricReport for DBus " 54262598e31SEd Tanous "Report with id {}", 54362598e31SEd Tanous reportId); 544c0353249SWludzik, Jozef return; 545156d6b00SAppaRao Puli } 546156d6b00SAppaRao Puli 54722daffd7SAppaRao Puli // Context is set by user during Event subscription and it must be 54822daffd7SAppaRao Puli // set for MetricReport response. 549*4b712a29SEd Tanous if (!userSub.customText.empty()) 55022daffd7SAppaRao Puli { 551*4b712a29SEd Tanous msg["Context"] = userSub.customText; 55222daffd7SAppaRao Puli } 55322daffd7SAppaRao Puli 554bd79bce8SPatrick Williams std::string strMsg = 555bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 5566d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 557156d6b00SAppaRao Puli } 558156d6b00SAppaRao Puli 559d14a48ffSCarson Labrado void updateRetryConfig(uint32_t retryAttempts, 560d14a48ffSCarson Labrado uint32_t retryTimeoutInterval) 561fe44eb0bSAyushi Smriti { 56293cf0ac2SEd Tanous if (policy == nullptr) 56393cf0ac2SEd Tanous { 56493cf0ac2SEd Tanous BMCWEB_LOG_DEBUG("Retry policy was nullptr, ignoring set"); 56593cf0ac2SEd Tanous return; 56693cf0ac2SEd Tanous } 567d14a48ffSCarson Labrado policy->maxRetryAttempts = retryAttempts; 568d14a48ffSCarson Labrado policy->retryIntervalSecs = std::chrono::seconds(retryTimeoutInterval); 56962de0c68SAppaRao Puli } 570fe44eb0bSAyushi Smriti 5719eb808c1SEd Tanous uint64_t getEventSeqNum() const 57296330b99SSunitha Harish { 57396330b99SSunitha Harish return eventSeqNum; 57496330b99SSunitha Harish } 57596330b99SSunitha Harish 5765e44e3d8SAppaRao Puli void setSubscriptionId(const std::string& id2) 5775e44e3d8SAppaRao Puli { 57862598e31SEd Tanous BMCWEB_LOG_DEBUG("Subscription ID: {}", id2); 5795e44e3d8SAppaRao Puli subId = id2; 5805e44e3d8SAppaRao Puli } 5815e44e3d8SAppaRao Puli 5825e44e3d8SAppaRao Puli std::string getSubscriptionId() 5835e44e3d8SAppaRao Puli { 5845e44e3d8SAppaRao Puli return subId; 5855e44e3d8SAppaRao Puli } 5865e44e3d8SAppaRao Puli 5875e44e3d8SAppaRao Puli bool matchSseId(const crow::sse_socket::Connection& thisConn) 5885e44e3d8SAppaRao Puli { 5895e44e3d8SAppaRao Puli return &thisConn == sseConn; 5905e44e3d8SAppaRao Puli } 5915e44e3d8SAppaRao Puli 592a7a80296SCarson Labrado // Check used to indicate what response codes are valid as part of our retry 593a7a80296SCarson Labrado // policy. 2XX is considered acceptable 594a7a80296SCarson Labrado static boost::system::error_code retryRespHandler(unsigned int respCode) 595a7a80296SCarson Labrado { 59662598e31SEd Tanous BMCWEB_LOG_DEBUG( 59762598e31SEd Tanous "Checking response code validity for SubscriptionEvent"); 598a7a80296SCarson Labrado if ((respCode < 200) || (respCode >= 300)) 599a7a80296SCarson Labrado { 600a7a80296SCarson Labrado return boost::system::errc::make_error_code( 601a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 602a7a80296SCarson Labrado } 603a7a80296SCarson Labrado 604a7a80296SCarson Labrado // Return 0 if the response code is valid 605a7a80296SCarson Labrado return boost::system::errc::make_error_code( 606a7a80296SCarson Labrado boost::system::errc::success); 6079fa6d147SNan Zhou } 608f80a87f2SEd Tanous 609*4b712a29SEd Tanous persistent_data::UserSubscription userSub; 610*4b712a29SEd Tanous 611f80a87f2SEd Tanous private: 612f80a87f2SEd Tanous std::string subId; 613f80a87f2SEd Tanous uint64_t eventSeqNum = 1; 614f80a87f2SEd Tanous boost::urls::url host; 615f80a87f2SEd Tanous std::shared_ptr<crow::ConnectionPolicy> policy; 616f80a87f2SEd Tanous crow::sse_socket::Connection* sseConn = nullptr; 617f80a87f2SEd Tanous 618f80a87f2SEd Tanous std::optional<crow::HttpClient> client; 619f80a87f2SEd Tanous 620f80a87f2SEd Tanous public: 621f80a87f2SEd Tanous std::optional<filter_ast::LogicalAnd> filter; 622b52664e2SAppaRao Puli }; 623b52664e2SAppaRao Puli 624b52664e2SAppaRao Puli class EventServiceManager 625b52664e2SAppaRao Puli { 626b52664e2SAppaRao Puli private: 627d3a9e084SEd Tanous bool serviceEnabled = false; 628d3a9e084SEd Tanous uint32_t retryAttempts = 0; 629d3a9e084SEd Tanous uint32_t retryTimeoutInterval = 0; 6307d1cc387SAppaRao Puli 6312558979cSP Dheeraj Srujan Kumar std::streampos redfishLogFilePosition{0}; 6329f616dd1SEd Tanous size_t noOfEventLogSubscribers{0}; 6339f616dd1SEd Tanous size_t noOfMetricReportSubscribers{0}; 63459d494eeSPatrick Williams std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor; 635b52664e2SAppaRao Puli boost::container::flat_map<std::string, std::shared_ptr<Subscription>> 636b52664e2SAppaRao Puli subscriptionsMap; 637b52664e2SAppaRao Puli 6389f616dd1SEd Tanous uint64_t eventId{1}; 63996330b99SSunitha Harish 640f80a87f2SEd Tanous struct Event 641f80a87f2SEd Tanous { 642f80a87f2SEd Tanous std::string id; 643f80a87f2SEd Tanous nlohmann::json message; 644f80a87f2SEd Tanous }; 645f80a87f2SEd Tanous 646f80a87f2SEd Tanous constexpr static size_t maxMessages = 200; 647f80a87f2SEd Tanous boost::circular_buffer<Event> messages{maxMessages}; 648f80a87f2SEd Tanous 649f8ca6d79SEd Tanous boost::asio::io_context& ioc; 650f8ca6d79SEd Tanous 651b52664e2SAppaRao Puli public: 6529f616dd1SEd Tanous EventServiceManager(const EventServiceManager&) = delete; 6539f616dd1SEd Tanous EventServiceManager& operator=(const EventServiceManager&) = delete; 6549f616dd1SEd Tanous EventServiceManager(EventServiceManager&&) = delete; 6559f616dd1SEd Tanous EventServiceManager& operator=(EventServiceManager&&) = delete; 656ecd6a3a2SEd Tanous ~EventServiceManager() = default; 6579f616dd1SEd Tanous 658f8ca6d79SEd Tanous explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn) 659b52664e2SAppaRao Puli { 660f8ca6d79SEd Tanous // Load config from persist store. 661f8ca6d79SEd Tanous initConfig(); 662f8ca6d79SEd Tanous } 663f8ca6d79SEd Tanous 664f8ca6d79SEd Tanous static EventServiceManager& 665f8ca6d79SEd Tanous getInstance(boost::asio::io_context* ioc = nullptr) 666f8ca6d79SEd Tanous { 667f8ca6d79SEd Tanous static EventServiceManager handler(*ioc); 668b52664e2SAppaRao Puli return handler; 669b52664e2SAppaRao Puli } 670b52664e2SAppaRao Puli 6711bf712bcSAyushi Smriti void initConfig() 6721bf712bcSAyushi Smriti { 67328afb49cSJunLin Chen loadOldBehavior(); 6741bf712bcSAyushi Smriti 67528afb49cSJunLin Chen persistent_data::EventServiceConfig eventServiceConfig = 67628afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 67728afb49cSJunLin Chen .getEventServiceConfig(); 6781bf712bcSAyushi Smriti 67928afb49cSJunLin Chen serviceEnabled = eventServiceConfig.enabled; 68028afb49cSJunLin Chen retryAttempts = eventServiceConfig.retryAttempts; 68128afb49cSJunLin Chen retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval; 6821bf712bcSAyushi Smriti 68328afb49cSJunLin Chen for (const auto& it : persistent_data::EventServiceStore::getInstance() 68428afb49cSJunLin Chen .subscriptionsConfigMap) 6851bf712bcSAyushi Smriti { 686*4b712a29SEd Tanous const persistent_data::UserSubscription& newSub = it.second; 6874bbf237fSAppaRao Puli 6886fd29553SEd Tanous boost::system::result<boost::urls::url> url = 689*4b712a29SEd Tanous boost::urls::parse_absolute_uri(newSub.destinationUrl); 6901bf712bcSAyushi Smriti 691a716aa74SEd Tanous if (!url) 6921bf712bcSAyushi Smriti { 69362598e31SEd Tanous BMCWEB_LOG_ERROR( 69462598e31SEd Tanous "Failed to validate and split destination url"); 6951bf712bcSAyushi Smriti continue; 6961bf712bcSAyushi Smriti } 6971bf712bcSAyushi Smriti std::shared_ptr<Subscription> subValue = 698a716aa74SEd Tanous std::make_shared<Subscription>(*url, ioc); 699*4b712a29SEd Tanous subValue->userSub = newSub; 7001bf712bcSAyushi Smriti 701*4b712a29SEd Tanous subscriptionsMap.insert(std::pair(subValue->userSub.id, subValue)); 70228afb49cSJunLin Chen 70328afb49cSJunLin Chen updateNoOfSubscribersCount(); 70428afb49cSJunLin Chen 70583328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 70683328316SEd Tanous { 7072558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 70883328316SEd Tanous } 7092558979cSP Dheeraj Srujan Kumar 71028afb49cSJunLin Chen // Update retry configuration. 71128afb49cSJunLin Chen subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 7121bf712bcSAyushi Smriti } 7131bf712bcSAyushi Smriti } 7141bf712bcSAyushi Smriti 71556d2396dSEd Tanous static void loadOldBehavior() 716b52664e2SAppaRao Puli { 71728afb49cSJunLin Chen std::ifstream eventConfigFile(eventServiceFile); 71828afb49cSJunLin Chen if (!eventConfigFile.good()) 7191bf712bcSAyushi Smriti { 72062598e31SEd Tanous BMCWEB_LOG_DEBUG("Old eventService config not exist"); 72128afb49cSJunLin Chen return; 72228afb49cSJunLin Chen } 72328afb49cSJunLin Chen auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false); 72428afb49cSJunLin Chen if (jsonData.is_discarded()) 7254bbf237fSAppaRao Puli { 72662598e31SEd Tanous BMCWEB_LOG_ERROR("Old eventService config parse error."); 72728afb49cSJunLin Chen return; 72828afb49cSJunLin Chen } 72928afb49cSJunLin Chen 7300bdda665SEd Tanous const nlohmann::json::object_t* obj = 7310bdda665SEd Tanous jsonData.get_ptr<const nlohmann::json::object_t*>(); 7320bdda665SEd Tanous for (const auto& item : *obj) 73328afb49cSJunLin Chen { 7340bdda665SEd Tanous if (item.first == "Configuration") 73528afb49cSJunLin Chen { 73628afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 73728afb49cSJunLin Chen .getEventServiceConfig() 7380bdda665SEd Tanous .fromJson(item.second); 73928afb49cSJunLin Chen } 7400bdda665SEd Tanous else if (item.first == "Subscriptions") 74128afb49cSJunLin Chen { 7420bdda665SEd Tanous for (const auto& elem : item.second) 74328afb49cSJunLin Chen { 744*4b712a29SEd Tanous std::optional<persistent_data::UserSubscription> 74528afb49cSJunLin Chen newSubscription = 74628afb49cSJunLin Chen persistent_data::UserSubscription::fromJson(elem, 74728afb49cSJunLin Chen true); 748*4b712a29SEd Tanous if (!newSubscription) 74928afb49cSJunLin Chen { 75062598e31SEd Tanous BMCWEB_LOG_ERROR("Problem reading subscription " 75162598e31SEd Tanous "from old persistent store"); 7524bbf237fSAppaRao Puli continue; 7534bbf237fSAppaRao Puli } 754*4b712a29SEd Tanous persistent_data::UserSubscription& newSub = 755*4b712a29SEd Tanous *newSubscription; 7561bf712bcSAyushi Smriti 75728afb49cSJunLin Chen std::uniform_int_distribution<uint32_t> dist(0); 75828afb49cSJunLin Chen bmcweb::OpenSSLGenerator gen; 7591bf712bcSAyushi Smriti 76028afb49cSJunLin Chen std::string id; 7611bf712bcSAyushi Smriti 76228afb49cSJunLin Chen int retry = 3; 763e662eae8SEd Tanous while (retry != 0) 7641bf712bcSAyushi Smriti { 76528afb49cSJunLin Chen id = std::to_string(dist(gen)); 76628afb49cSJunLin Chen if (gen.error()) 7677d1cc387SAppaRao Puli { 76828afb49cSJunLin Chen retry = 0; 76928afb49cSJunLin Chen break; 77028afb49cSJunLin Chen } 771*4b712a29SEd Tanous newSub.id = id; 77228afb49cSJunLin Chen auto inserted = 77328afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 77428afb49cSJunLin Chen .subscriptionsConfigMap.insert( 775*4b712a29SEd Tanous std::pair(id, newSub)); 77628afb49cSJunLin Chen if (inserted.second) 77728afb49cSJunLin Chen { 77828afb49cSJunLin Chen break; 77928afb49cSJunLin Chen } 78028afb49cSJunLin Chen --retry; 7817d1cc387SAppaRao Puli } 7827d1cc387SAppaRao Puli 78328afb49cSJunLin Chen if (retry <= 0) 78428afb49cSJunLin Chen { 78562598e31SEd Tanous BMCWEB_LOG_ERROR( 78662598e31SEd Tanous "Failed to generate random number from old " 78762598e31SEd Tanous "persistent store"); 78828afb49cSJunLin Chen continue; 78928afb49cSJunLin Chen } 79028afb49cSJunLin Chen } 79128afb49cSJunLin Chen } 79228afb49cSJunLin Chen 79328afb49cSJunLin Chen persistent_data::getConfig().writeData(); 7944c521c3cSEd Tanous std::error_code ec; 7954c521c3cSEd Tanous std::filesystem::remove(eventServiceFile, ec); 7964c521c3cSEd Tanous if (ec) 7974c521c3cSEd Tanous { 7984c521c3cSEd Tanous BMCWEB_LOG_DEBUG( 7994c521c3cSEd Tanous "Failed to remove old event service file. Ignoring"); 8004c521c3cSEd Tanous } 8014c521c3cSEd Tanous else 8024c521c3cSEd Tanous { 80362598e31SEd Tanous BMCWEB_LOG_DEBUG("Remove old eventservice config"); 80428afb49cSJunLin Chen } 80528afb49cSJunLin Chen } 8064c521c3cSEd Tanous } 80728afb49cSJunLin Chen 8089eb808c1SEd Tanous void updateSubscriptionData() const 80928afb49cSJunLin Chen { 81028afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 81128afb49cSJunLin Chen .eventServiceConfig.enabled = serviceEnabled; 81228afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 81328afb49cSJunLin Chen .eventServiceConfig.retryAttempts = retryAttempts; 81428afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 81528afb49cSJunLin Chen .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval; 81628afb49cSJunLin Chen 81728afb49cSJunLin Chen persistent_data::getConfig().writeData(); 81828afb49cSJunLin Chen } 81928afb49cSJunLin Chen 82028afb49cSJunLin Chen void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg) 8217d1cc387SAppaRao Puli { 8227d1cc387SAppaRao Puli bool updateConfig = false; 823fe44eb0bSAyushi Smriti bool updateRetryCfg = false; 8247d1cc387SAppaRao Puli 82528afb49cSJunLin Chen if (serviceEnabled != cfg.enabled) 8267d1cc387SAppaRao Puli { 82728afb49cSJunLin Chen serviceEnabled = cfg.enabled; 828e662eae8SEd Tanous if (serviceEnabled && noOfMetricReportSubscribers != 0U) 8297d1cc387SAppaRao Puli { 8307d1cc387SAppaRao Puli registerMetricReportSignal(); 8317d1cc387SAppaRao Puli } 8327d1cc387SAppaRao Puli else 8337d1cc387SAppaRao Puli { 8347d1cc387SAppaRao Puli unregisterMetricReportSignal(); 8357d1cc387SAppaRao Puli } 8367d1cc387SAppaRao Puli updateConfig = true; 8377d1cc387SAppaRao Puli } 8387d1cc387SAppaRao Puli 83928afb49cSJunLin Chen if (retryAttempts != cfg.retryAttempts) 8407d1cc387SAppaRao Puli { 84128afb49cSJunLin Chen retryAttempts = cfg.retryAttempts; 8427d1cc387SAppaRao Puli updateConfig = true; 843fe44eb0bSAyushi Smriti updateRetryCfg = true; 8447d1cc387SAppaRao Puli } 8457d1cc387SAppaRao Puli 84628afb49cSJunLin Chen if (retryTimeoutInterval != cfg.retryTimeoutInterval) 8477d1cc387SAppaRao Puli { 84828afb49cSJunLin Chen retryTimeoutInterval = cfg.retryTimeoutInterval; 8497d1cc387SAppaRao Puli updateConfig = true; 850fe44eb0bSAyushi Smriti updateRetryCfg = true; 8517d1cc387SAppaRao Puli } 8527d1cc387SAppaRao Puli 8537d1cc387SAppaRao Puli if (updateConfig) 8547d1cc387SAppaRao Puli { 8557d1cc387SAppaRao Puli updateSubscriptionData(); 8567d1cc387SAppaRao Puli } 857fe44eb0bSAyushi Smriti 858fe44eb0bSAyushi Smriti if (updateRetryCfg) 859fe44eb0bSAyushi Smriti { 860fe44eb0bSAyushi Smriti // Update the changed retry config to all subscriptions 861fe44eb0bSAyushi Smriti for (const auto& it : 862fe44eb0bSAyushi Smriti EventServiceManager::getInstance().subscriptionsMap) 863fe44eb0bSAyushi Smriti { 8645e44e3d8SAppaRao Puli Subscription& entry = *it.second; 8655e44e3d8SAppaRao Puli entry.updateRetryConfig(retryAttempts, retryTimeoutInterval); 866fe44eb0bSAyushi Smriti } 867fe44eb0bSAyushi Smriti } 8687d1cc387SAppaRao Puli } 8697d1cc387SAppaRao Puli 8707d1cc387SAppaRao Puli void updateNoOfSubscribersCount() 8717d1cc387SAppaRao Puli { 8727d1cc387SAppaRao Puli size_t eventLogSubCount = 0; 8737d1cc387SAppaRao Puli size_t metricReportSubCount = 0; 8747d1cc387SAppaRao Puli for (const auto& it : subscriptionsMap) 8757d1cc387SAppaRao Puli { 8767d1cc387SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 877*4b712a29SEd Tanous if (entry->userSub.eventFormatType == eventFormatType) 8787d1cc387SAppaRao Puli { 8797d1cc387SAppaRao Puli eventLogSubCount++; 8807d1cc387SAppaRao Puli } 881*4b712a29SEd Tanous else if (entry->userSub.eventFormatType == metricReportFormatType) 8827d1cc387SAppaRao Puli { 8837d1cc387SAppaRao Puli metricReportSubCount++; 8847d1cc387SAppaRao Puli } 8857d1cc387SAppaRao Puli } 8867d1cc387SAppaRao Puli 8877d1cc387SAppaRao Puli noOfEventLogSubscribers = eventLogSubCount; 8887d1cc387SAppaRao Puli if (noOfMetricReportSubscribers != metricReportSubCount) 8897d1cc387SAppaRao Puli { 8907d1cc387SAppaRao Puli noOfMetricReportSubscribers = metricReportSubCount; 891e662eae8SEd Tanous if (noOfMetricReportSubscribers != 0U) 8927d1cc387SAppaRao Puli { 8937d1cc387SAppaRao Puli registerMetricReportSignal(); 8947d1cc387SAppaRao Puli } 8957d1cc387SAppaRao Puli else 8967d1cc387SAppaRao Puli { 8977d1cc387SAppaRao Puli unregisterMetricReportSignal(); 8987d1cc387SAppaRao Puli } 8997d1cc387SAppaRao Puli } 9007d1cc387SAppaRao Puli } 9017d1cc387SAppaRao Puli 902b52664e2SAppaRao Puli std::shared_ptr<Subscription> getSubscription(const std::string& id) 903b52664e2SAppaRao Puli { 904b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 905b52664e2SAppaRao Puli if (obj == subscriptionsMap.end()) 906b52664e2SAppaRao Puli { 90762598e31SEd Tanous BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id); 908b52664e2SAppaRao Puli return nullptr; 909b52664e2SAppaRao Puli } 910b52664e2SAppaRao Puli std::shared_ptr<Subscription> subValue = obj->second; 911b52664e2SAppaRao Puli return subValue; 912b52664e2SAppaRao Puli } 913b52664e2SAppaRao Puli 914f80a87f2SEd Tanous std::string 915f80a87f2SEd Tanous addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue) 916b52664e2SAppaRao Puli { 917fc76b8acSEd Tanous std::uniform_int_distribution<uint32_t> dist(0); 918fc76b8acSEd Tanous bmcweb::OpenSSLGenerator gen; 919fc76b8acSEd Tanous 920b52664e2SAppaRao Puli std::string id; 921b52664e2SAppaRao Puli 922b52664e2SAppaRao Puli int retry = 3; 923e662eae8SEd Tanous while (retry != 0) 924b52664e2SAppaRao Puli { 925fc76b8acSEd Tanous id = std::to_string(dist(gen)); 926fc76b8acSEd Tanous if (gen.error()) 927fc76b8acSEd Tanous { 928fc76b8acSEd Tanous retry = 0; 929fc76b8acSEd Tanous break; 930fc76b8acSEd Tanous } 931b52664e2SAppaRao Puli auto inserted = subscriptionsMap.insert(std::pair(id, subValue)); 932b52664e2SAppaRao Puli if (inserted.second) 933b52664e2SAppaRao Puli { 934b52664e2SAppaRao Puli break; 935b52664e2SAppaRao Puli } 936b52664e2SAppaRao Puli --retry; 93723a21a1cSEd Tanous } 938b52664e2SAppaRao Puli 939b52664e2SAppaRao Puli if (retry <= 0) 940b52664e2SAppaRao Puli { 94162598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to generate random number"); 942abb93cddSEd Tanous return ""; 943b52664e2SAppaRao Puli } 944b52664e2SAppaRao Puli 945*4b712a29SEd Tanous persistent_data::UserSubscription newSub(subValue->userSub); 946a14c9113SEd Tanous 94728afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 948*4b712a29SEd Tanous .subscriptionsConfigMap.emplace(newSub.id, newSub); 94928afb49cSJunLin Chen 9507d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 9511bf712bcSAyushi Smriti 95283328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 95383328316SEd Tanous { 9542558979cSP Dheeraj Srujan Kumar if (redfishLogFilePosition != 0) 9557f4eb588SAppaRao Puli { 9562558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 9577f4eb588SAppaRao Puli } 95883328316SEd Tanous } 959fe44eb0bSAyushi Smriti // Update retry configuration. 960fe44eb0bSAyushi Smriti subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 961fe44eb0bSAyushi Smriti 9625e44e3d8SAppaRao Puli // Set Subscription ID for back trace 9635e44e3d8SAppaRao Puli subValue->setSubscriptionId(id); 964f80a87f2SEd Tanous 965f80a87f2SEd Tanous return id; 966f80a87f2SEd Tanous } 967f80a87f2SEd Tanous 968f80a87f2SEd Tanous std::string 969f80a87f2SEd Tanous addSSESubscription(const std::shared_ptr<Subscription>& subValue, 970f80a87f2SEd Tanous std::string_view lastEventId) 971f80a87f2SEd Tanous { 972f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 973f80a87f2SEd Tanous 974f80a87f2SEd Tanous if (!lastEventId.empty()) 975f80a87f2SEd Tanous { 976f80a87f2SEd Tanous BMCWEB_LOG_INFO("Attempting to find message for last id {}", 977f80a87f2SEd Tanous lastEventId); 978f80a87f2SEd Tanous boost::circular_buffer<Event>::iterator lastEvent = 979f80a87f2SEd Tanous std::find_if(messages.begin(), messages.end(), 980f80a87f2SEd Tanous [&lastEventId](const Event& event) { 981f80a87f2SEd Tanous return event.id == lastEventId; 982f80a87f2SEd Tanous }); 983f80a87f2SEd Tanous // Can't find a matching ID 984f80a87f2SEd Tanous if (lastEvent == messages.end()) 985f80a87f2SEd Tanous { 986f80a87f2SEd Tanous nlohmann::json msg = messages::eventBufferExceeded(); 987f80a87f2SEd Tanous // If the buffer overloaded, send all messages. 9886d799e14SEd Tanous subValue->sendEventToSubscriber(msg); 989f80a87f2SEd Tanous lastEvent = messages.begin(); 990f80a87f2SEd Tanous } 991f80a87f2SEd Tanous else 992f80a87f2SEd Tanous { 993f80a87f2SEd Tanous // Skip the last event the user already has 994f80a87f2SEd Tanous lastEvent++; 995f80a87f2SEd Tanous } 996f80a87f2SEd Tanous 997f80a87f2SEd Tanous for (boost::circular_buffer<Event>::const_iterator event = 998f80a87f2SEd Tanous lastEvent; 999f80a87f2SEd Tanous lastEvent != messages.end(); lastEvent++) 1000f80a87f2SEd Tanous { 10016d799e14SEd Tanous subValue->sendEventToSubscriber(event->message); 1002f80a87f2SEd Tanous } 1003f80a87f2SEd Tanous } 1004f80a87f2SEd Tanous return id; 1005f80a87f2SEd Tanous } 1006f80a87f2SEd Tanous 1007f80a87f2SEd Tanous std::string 1008f80a87f2SEd Tanous addPushSubscription(const std::shared_ptr<Subscription>& subValue) 1009f80a87f2SEd Tanous { 1010f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 1011f80a87f2SEd Tanous 1012f80a87f2SEd Tanous updateSubscriptionData(); 1013b52664e2SAppaRao Puli return id; 1014b52664e2SAppaRao Puli } 1015b52664e2SAppaRao Puli 1016b52664e2SAppaRao Puli bool isSubscriptionExist(const std::string& id) 1017b52664e2SAppaRao Puli { 1018b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 101955f79e6fSEd Tanous return obj != subscriptionsMap.end(); 1020b52664e2SAppaRao Puli } 1021b52664e2SAppaRao Puli 1022*4b712a29SEd Tanous bool deleteSubscription(const std::string& id) 1023b52664e2SAppaRao Puli { 1024b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 1025*4b712a29SEd Tanous if (obj == subscriptionsMap.end()) 1026b52664e2SAppaRao Puli { 1027*4b712a29SEd Tanous BMCWEB_LOG_WARNING("Could not find subscription with id {}", id); 1028*4b712a29SEd Tanous return false; 1029*4b712a29SEd Tanous } 1030b52664e2SAppaRao Puli subscriptionsMap.erase(obj); 1031*4b712a29SEd Tanous auto& event = persistent_data::EventServiceStore::getInstance(); 1032*4b712a29SEd Tanous auto persistentObj = event.subscriptionsConfigMap.find(id); 1033*4b712a29SEd Tanous if (persistentObj == event.subscriptionsConfigMap.end()) 1034*4b712a29SEd Tanous { 1035*4b712a29SEd Tanous BMCWEB_LOG_ERROR("Subscription wasn't in persistent data"); 1036*4b712a29SEd Tanous return true; 1037*4b712a29SEd Tanous } 103828afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 1039*4b712a29SEd Tanous .subscriptionsConfigMap.erase(persistentObj); 10407d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 1041b52664e2SAppaRao Puli updateSubscriptionData(); 1042*4b712a29SEd Tanous 1043*4b712a29SEd Tanous return true; 1044b52664e2SAppaRao Puli } 1045b52664e2SAppaRao Puli 10465e44e3d8SAppaRao Puli void deleteSseSubscription(const crow::sse_socket::Connection& thisConn) 10475e44e3d8SAppaRao Puli { 1048bdbfae2aSEd Tanous for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();) 10495e44e3d8SAppaRao Puli { 1050bdbfae2aSEd Tanous std::shared_ptr<Subscription> entry = it->second; 10515e44e3d8SAppaRao Puli bool entryIsThisConn = entry->matchSseId(thisConn); 10525e44e3d8SAppaRao Puli if (entryIsThisConn) 10535e44e3d8SAppaRao Puli { 10545e44e3d8SAppaRao Puli persistent_data::EventServiceStore::getInstance() 10555e44e3d8SAppaRao Puli .subscriptionsConfigMap.erase( 1056bdbfae2aSEd Tanous it->second->getSubscriptionId()); 1057bdbfae2aSEd Tanous it = subscriptionsMap.erase(it); 10585e44e3d8SAppaRao Puli return; 10595e44e3d8SAppaRao Puli } 1060bdbfae2aSEd Tanous it++; 10615e44e3d8SAppaRao Puli } 10625e44e3d8SAppaRao Puli } 10635e44e3d8SAppaRao Puli 10645e44e3d8SAppaRao Puli size_t getNumberOfSubscriptions() const 1065b52664e2SAppaRao Puli { 1066b52664e2SAppaRao Puli return subscriptionsMap.size(); 1067b52664e2SAppaRao Puli } 1068b52664e2SAppaRao Puli 10695e44e3d8SAppaRao Puli size_t getNumberOfSSESubscriptions() const 10705e44e3d8SAppaRao Puli { 10713544d2a7SEd Tanous auto size = std::ranges::count_if( 10723544d2a7SEd Tanous subscriptionsMap, 10735e44e3d8SAppaRao Puli [](const std::pair<std::string, std::shared_ptr<Subscription>>& 10745e44e3d8SAppaRao Puli entry) { 1075*4b712a29SEd Tanous return (entry.second->userSub.subscriptionType == 1076*4b712a29SEd Tanous subscriptionTypeSSE); 10775e44e3d8SAppaRao Puli }); 10785e44e3d8SAppaRao Puli return static_cast<size_t>(size); 10795e44e3d8SAppaRao Puli } 10805e44e3d8SAppaRao Puli 1081b52664e2SAppaRao Puli std::vector<std::string> getAllIDs() 1082b52664e2SAppaRao Puli { 1083b52664e2SAppaRao Puli std::vector<std::string> idList; 1084b52664e2SAppaRao Puli for (const auto& it : subscriptionsMap) 1085b52664e2SAppaRao Puli { 1086b52664e2SAppaRao Puli idList.emplace_back(it.first); 1087b52664e2SAppaRao Puli } 1088b52664e2SAppaRao Puli return idList; 1089b52664e2SAppaRao Puli } 1090b52664e2SAppaRao Puli 10916ba8c82eSsunharis_in bool sendTestEventLog() 10920b4bdd93SAppaRao Puli { 10935e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 10940b4bdd93SAppaRao Puli { 10950b4bdd93SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 10966ba8c82eSsunharis_in if (!entry->sendTestEventLog()) 10976ba8c82eSsunharis_in { 10986ba8c82eSsunharis_in return false; 10990b4bdd93SAppaRao Puli } 11000b4bdd93SAppaRao Puli } 11016ba8c82eSsunharis_in return true; 11026ba8c82eSsunharis_in } 1103e9a14131SAppaRao Puli 1104f80a87f2SEd Tanous void sendEvent(nlohmann::json::object_t eventMessage, 1105f80a87f2SEd Tanous std::string_view origin, std::string_view resourceType) 110696330b99SSunitha Harish { 1107613dabeaSEd Tanous eventMessage["EventId"] = eventId; 1108f80a87f2SEd Tanous 1109613dabeaSEd Tanous eventMessage["EventTimestamp"] = 1110613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 1111613dabeaSEd Tanous eventMessage["OriginOfCondition"] = origin; 1112613dabeaSEd Tanous 1113f80a87f2SEd Tanous // MemberId is 0 : since we are sending one event record. 1114f80a87f2SEd Tanous eventMessage["MemberId"] = 0; 111596330b99SSunitha Harish 1116f80a87f2SEd Tanous messages.push_back(Event(std::to_string(eventId), eventMessage)); 1117f80a87f2SEd Tanous 1118f80a87f2SEd Tanous for (auto& it : subscriptionsMap) 111996330b99SSunitha Harish { 1120f80a87f2SEd Tanous std::shared_ptr<Subscription>& entry = it.second; 1121f80a87f2SEd Tanous if (!entry->eventMatchesFilter(eventMessage, resourceType)) 112296330b99SSunitha Harish { 1123f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 1124f80a87f2SEd Tanous continue; 112596330b99SSunitha Harish } 1126f80a87f2SEd Tanous 1127f80a87f2SEd Tanous nlohmann::json::array_t eventRecord; 1128f80a87f2SEd Tanous eventRecord.emplace_back(eventMessage); 1129f80a87f2SEd Tanous 1130613dabeaSEd Tanous nlohmann::json msgJson; 1131613dabeaSEd Tanous 1132613dabeaSEd Tanous msgJson["@odata.type"] = "#Event.v1_4_0.Event"; 1133613dabeaSEd Tanous msgJson["Name"] = "Event Log"; 1134613dabeaSEd Tanous msgJson["Id"] = eventId; 1135f80a87f2SEd Tanous msgJson["Events"] = std::move(eventRecord); 1136f52c03c1SCarson Labrado 1137f52c03c1SCarson Labrado std::string strMsg = msgJson.dump( 1138f52c03c1SCarson Labrado 2, ' ', true, nlohmann::json::error_handler_t::replace); 11396d799e14SEd Tanous entry->sendEventToSubscriber(std::move(strMsg)); 11408ece0e45SEd Tanous eventId++; // increment the eventId 114196330b99SSunitha Harish } 114296330b99SSunitha Harish } 114396330b99SSunitha Harish 11442558979cSP Dheeraj Srujan Kumar void resetRedfishFilePosition() 11457f4eb588SAppaRao Puli { 11462558979cSP Dheeraj Srujan Kumar // Control would be here when Redfish file is created. 11472558979cSP Dheeraj Srujan Kumar // Reset File Position as new file is created 11482558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = 0; 11492558979cSP Dheeraj Srujan Kumar } 11502558979cSP Dheeraj Srujan Kumar 11512558979cSP Dheeraj Srujan Kumar void cacheRedfishLogFile() 11522558979cSP Dheeraj Srujan Kumar { 11532558979cSP Dheeraj Srujan Kumar // Open the redfish file and read till the last record. 11542558979cSP Dheeraj Srujan Kumar 11557f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 11567f4eb588SAppaRao Puli if (!logStream.good()) 11577f4eb588SAppaRao Puli { 115862598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed "); 11597f4eb588SAppaRao Puli return; 11607f4eb588SAppaRao Puli } 11617f4eb588SAppaRao Puli std::string logEntry; 11627f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 11637f4eb588SAppaRao Puli { 11642558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 11657f4eb588SAppaRao Puli } 11667f4eb588SAppaRao Puli } 11677f4eb588SAppaRao Puli 11687f4eb588SAppaRao Puli void readEventLogsFromFile() 11697f4eb588SAppaRao Puli { 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 11777f4eb588SAppaRao Puli std::vector<EventLogObjectsType> eventRecords; 11787f4eb588SAppaRao Puli 11797f4eb588SAppaRao Puli std::string logEntry; 11802558979cSP Dheeraj Srujan Kumar 118103d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: seek to {}", 118203d4d37cSAlexander Hansen static_cast<int>(redfishLogFilePosition)); 118303d4d37cSAlexander Hansen 11842558979cSP Dheeraj Srujan Kumar // Get the read pointer to the next log to be read. 11852558979cSP Dheeraj Srujan Kumar logStream.seekg(redfishLogFilePosition); 11862558979cSP Dheeraj Srujan Kumar 11877f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 11887f4eb588SAppaRao Puli { 118903d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry"); 11902558979cSP Dheeraj Srujan Kumar // Update Pointer position 11912558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 11922558979cSP Dheeraj Srujan Kumar 11932558979cSP Dheeraj Srujan Kumar std::string idStr; 11942558979cSP Dheeraj Srujan Kumar if (!event_log::getUniqueEntryID(logEntry, idStr)) 11957f4eb588SAppaRao Puli { 119603d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 119703d4d37cSAlexander Hansen "Redfish log file: could not get unique entry id for {}", 119803d4d37cSAlexander Hansen logEntry); 11997f4eb588SAppaRao Puli continue; 12007f4eb588SAppaRao Puli } 12017f4eb588SAppaRao Puli 1202e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 12037f4eb588SAppaRao Puli { 12042558979cSP Dheeraj Srujan Kumar // If Service is not enabled, no need to compute 12052558979cSP Dheeraj Srujan Kumar // the remaining items below. 12062558979cSP Dheeraj Srujan Kumar // But, Loop must continue to keep track of Timestamp 120703d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 120803d4d37cSAlexander Hansen "Redfish log file: no subscribers / event service not enabled"); 12097f4eb588SAppaRao Puli continue; 12107f4eb588SAppaRao Puli } 12117f4eb588SAppaRao Puli 12127f4eb588SAppaRao Puli std::string timestamp; 12137f4eb588SAppaRao Puli std::string messageID; 12145e715de6SAppaRao Puli std::vector<std::string> messageArgs; 12157f4eb588SAppaRao Puli if (event_log::getEventLogParams(logEntry, timestamp, messageID, 12167f4eb588SAppaRao Puli messageArgs) != 0) 12177f4eb588SAppaRao Puli { 121803d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}", 121903d4d37cSAlexander Hansen logEntry); 12207f4eb588SAppaRao Puli continue; 12217f4eb588SAppaRao Puli } 12227f4eb588SAppaRao Puli 1223f80a87f2SEd Tanous eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs); 12247f4eb588SAppaRao Puli } 12257f4eb588SAppaRao Puli 1226e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 12272558979cSP Dheeraj Srujan Kumar { 122862598e31SEd Tanous BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions."); 12292558979cSP Dheeraj Srujan Kumar return; 12302558979cSP Dheeraj Srujan Kumar } 12312558979cSP Dheeraj Srujan Kumar 12322558979cSP Dheeraj Srujan Kumar if (eventRecords.empty()) 12332558979cSP Dheeraj Srujan Kumar { 12342558979cSP Dheeraj Srujan Kumar // No Records to send 123562598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 12362558979cSP Dheeraj Srujan Kumar return; 12372558979cSP Dheeraj Srujan Kumar } 12382558979cSP Dheeraj Srujan Kumar 12395e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 12407f4eb588SAppaRao Puli { 12417f4eb588SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 1242*4b712a29SEd Tanous if (entry->userSub.eventFormatType == "Event") 12437f4eb588SAppaRao Puli { 12447f4eb588SAppaRao Puli entry->filterAndSendEventLogs(eventRecords); 12457f4eb588SAppaRao Puli } 12467f4eb588SAppaRao Puli } 12477f4eb588SAppaRao Puli } 12487f4eb588SAppaRao Puli 12497f4eb588SAppaRao Puli static void watchRedfishEventLogFile() 12507f4eb588SAppaRao Puli { 12516a9f85f9SAppaRao Puli if (!inotifyConn) 12527f4eb588SAppaRao Puli { 125303d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("inotify Connection is not present"); 12547f4eb588SAppaRao Puli return; 12557f4eb588SAppaRao Puli } 12567f4eb588SAppaRao Puli 12577f4eb588SAppaRao Puli static std::array<char, 1024> readBuffer; 12587f4eb588SAppaRao Puli 1259bd79bce8SPatrick Williams inotifyConn->async_read_some( 1260bd79bce8SPatrick Williams boost::asio::buffer(readBuffer), 12617f4eb588SAppaRao Puli [&](const boost::system::error_code& ec, 12627f4eb588SAppaRao Puli const std::size_t& bytesTransferred) { 12639ed3f90aSEd Tanous if (ec == boost::asio::error::operation_aborted) 12649ed3f90aSEd Tanous { 12659ed3f90aSEd Tanous BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)"); 12669ed3f90aSEd Tanous return; 12679ed3f90aSEd Tanous } 12687f4eb588SAppaRao Puli if (ec) 12697f4eb588SAppaRao Puli { 127062598e31SEd Tanous BMCWEB_LOG_ERROR("Callback Error: {}", ec.message()); 12717f4eb588SAppaRao Puli return; 12727f4eb588SAppaRao Puli } 127303d4d37cSAlexander Hansen 127403d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred); 127503d4d37cSAlexander Hansen 12767f4eb588SAppaRao Puli std::size_t index = 0; 1277b792cc56SAppaRao Puli while ((index + iEventSize) <= bytesTransferred) 12787f4eb588SAppaRao Puli { 1279d3a9e084SEd Tanous struct inotify_event event 1280d3a9e084SEd Tanous {}; 1281b792cc56SAppaRao Puli std::memcpy(&event, &readBuffer[index], iEventSize); 1282b792cc56SAppaRao Puli if (event.wd == dirWatchDesc) 1283b792cc56SAppaRao Puli { 1284b792cc56SAppaRao Puli if ((event.len == 0) || 1285b792cc56SAppaRao Puli (index + iEventSize + event.len > bytesTransferred)) 1286b792cc56SAppaRao Puli { 1287b792cc56SAppaRao Puli index += (iEventSize + event.len); 1288b792cc56SAppaRao Puli continue; 1289b792cc56SAppaRao Puli } 1290b792cc56SAppaRao Puli 12914f568f74SJiaqing Zhao std::string fileName(&readBuffer[index + iEventSize]); 12924f568f74SJiaqing Zhao if (fileName != "redfish") 1293b792cc56SAppaRao Puli { 1294b792cc56SAppaRao Puli index += (iEventSize + event.len); 1295b792cc56SAppaRao Puli continue; 1296b792cc56SAppaRao Puli } 1297b792cc56SAppaRao Puli 129862598e31SEd Tanous BMCWEB_LOG_DEBUG( 129962598e31SEd Tanous "Redfish log file created/deleted. event.name: {}", 130062598e31SEd Tanous fileName); 1301b792cc56SAppaRao Puli if (event.mask == IN_CREATE) 1302b792cc56SAppaRao Puli { 1303b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1304b792cc56SAppaRao Puli { 130562598e31SEd Tanous BMCWEB_LOG_DEBUG( 130662598e31SEd Tanous "Remove and Add inotify watcher on " 130762598e31SEd Tanous "redfish event log file"); 1308016761afSAppaRao Puli // Remove existing inotify watcher and add 1309016761afSAppaRao Puli // with new redfish event log file. 1310016761afSAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1311016761afSAppaRao Puli fileWatchDesc = -1; 1312b792cc56SAppaRao Puli } 1313b792cc56SAppaRao Puli 1314b792cc56SAppaRao Puli fileWatchDesc = inotify_add_watch( 1315b792cc56SAppaRao Puli inotifyFd, redfishEventLogFile, IN_MODIFY); 1316b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1317b792cc56SAppaRao Puli { 131862598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for " 131962598e31SEd Tanous "redfish log file."); 1320b792cc56SAppaRao Puli return; 1321b792cc56SAppaRao Puli } 1322b792cc56SAppaRao Puli 1323b792cc56SAppaRao Puli EventServiceManager::getInstance() 13242558979cSP Dheeraj Srujan Kumar .resetRedfishFilePosition(); 1325b792cc56SAppaRao Puli EventServiceManager::getInstance() 1326b792cc56SAppaRao Puli .readEventLogsFromFile(); 1327b792cc56SAppaRao Puli } 1328b792cc56SAppaRao Puli else if ((event.mask == IN_DELETE) || 1329b792cc56SAppaRao Puli (event.mask == IN_MOVED_TO)) 1330b792cc56SAppaRao Puli { 1331b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1332b792cc56SAppaRao Puli { 1333b792cc56SAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1334b792cc56SAppaRao Puli fileWatchDesc = -1; 1335b792cc56SAppaRao Puli } 1336b792cc56SAppaRao Puli } 1337b792cc56SAppaRao Puli } 1338b792cc56SAppaRao Puli else if (event.wd == fileWatchDesc) 1339b792cc56SAppaRao Puli { 1340b792cc56SAppaRao Puli if (event.mask == IN_MODIFY) 13417f4eb588SAppaRao Puli { 13427f4eb588SAppaRao Puli EventServiceManager::getInstance() 13437f4eb588SAppaRao Puli .readEventLogsFromFile(); 13447f4eb588SAppaRao Puli } 1345b792cc56SAppaRao Puli } 1346b792cc56SAppaRao Puli index += (iEventSize + event.len); 13477f4eb588SAppaRao Puli } 13487f4eb588SAppaRao Puli 13497f4eb588SAppaRao Puli watchRedfishEventLogFile(); 13507f4eb588SAppaRao Puli }); 13517f4eb588SAppaRao Puli } 13527f4eb588SAppaRao Puli 13537f4eb588SAppaRao Puli static int startEventLogMonitor(boost::asio::io_context& ioc) 13547f4eb588SAppaRao Puli { 135503d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 135603d4d37cSAlexander Hansen 135723a21a1cSEd Tanous inotifyConn.emplace(ioc); 1358b792cc56SAppaRao Puli inotifyFd = inotify_init1(IN_NONBLOCK); 1359b792cc56SAppaRao Puli if (inotifyFd == -1) 13607f4eb588SAppaRao Puli { 136162598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_init1 failed."); 13627f4eb588SAppaRao Puli return -1; 13637f4eb588SAppaRao Puli } 1364b792cc56SAppaRao Puli 1365b792cc56SAppaRao Puli // Add watch on directory to handle redfish event log file 1366b792cc56SAppaRao Puli // create/delete. 1367b792cc56SAppaRao Puli dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 1368b792cc56SAppaRao Puli IN_CREATE | IN_MOVED_TO | IN_DELETE); 1369b792cc56SAppaRao Puli if (dirWatchDesc == -1) 13707f4eb588SAppaRao Puli { 137162598e31SEd Tanous BMCWEB_LOG_ERROR( 137262598e31SEd Tanous "inotify_add_watch failed for event log directory."); 13737f4eb588SAppaRao Puli return -1; 13747f4eb588SAppaRao Puli } 13757f4eb588SAppaRao Puli 1376b792cc56SAppaRao Puli // Watch redfish event log file for modifications. 1377bd79bce8SPatrick Williams fileWatchDesc = 1378bd79bce8SPatrick Williams inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 1379b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1380b792cc56SAppaRao Puli { 138162598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 1382b792cc56SAppaRao Puli // Don't return error if file not exist. 1383b792cc56SAppaRao Puli // Watch on directory will handle create/delete of file. 1384b792cc56SAppaRao Puli } 1385b792cc56SAppaRao Puli 13867f4eb588SAppaRao Puli // monitor redfish event log file 1387b792cc56SAppaRao Puli inotifyConn->assign(inotifyFd); 13887f4eb588SAppaRao Puli watchRedfishEventLogFile(); 13897f4eb588SAppaRao Puli 13907f4eb588SAppaRao Puli return 0; 13917f4eb588SAppaRao Puli } 13927f4eb588SAppaRao Puli 13939ed3f90aSEd Tanous static void stopEventLogMonitor() 13949ed3f90aSEd Tanous { 13959ed3f90aSEd Tanous inotifyConn.reset(); 13969ed3f90aSEd Tanous } 13979ed3f90aSEd Tanous 139859d494eeSPatrick Williams static void getReadingsForReport(sdbusplus::message_t& msg) 1399156d6b00SAppaRao Puli { 140056d2396dSEd Tanous if (msg.is_method_error()) 140156d2396dSEd Tanous { 140262598e31SEd Tanous BMCWEB_LOG_ERROR("TelemetryMonitor Signal error"); 140356d2396dSEd Tanous return; 140456d2396dSEd Tanous } 140556d2396dSEd Tanous 1406c0353249SWludzik, Jozef sdbusplus::message::object_path path(msg.get_path()); 1407c0353249SWludzik, Jozef std::string id = path.filename(); 1408c0353249SWludzik, Jozef if (id.empty()) 1409156d6b00SAppaRao Puli { 141062598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to get Id from path"); 1411156d6b00SAppaRao Puli return; 1412156d6b00SAppaRao Puli } 1413156d6b00SAppaRao Puli 1414c0353249SWludzik, Jozef std::string interface; 1415b9d36b47SEd Tanous dbus::utility::DBusPropertiesMap props; 1416c0353249SWludzik, Jozef std::vector<std::string> invalidProps; 1417c0353249SWludzik, Jozef msg.read(interface, props, invalidProps); 1418c0353249SWludzik, Jozef 1419bd79bce8SPatrick Williams auto found = std::ranges::find_if(props, [](const auto& x) { 1420bd79bce8SPatrick Williams return x.first == "Readings"; 1421bd79bce8SPatrick Williams }); 1422c0353249SWludzik, Jozef if (found == props.end()) 1423156d6b00SAppaRao Puli { 142462598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 1425156d6b00SAppaRao Puli return; 1426156d6b00SAppaRao Puli } 1427156d6b00SAppaRao Puli 14281e1e598dSJonathan Doman const telemetry::TimestampReadings* readings = 14291e1e598dSJonathan Doman std::get_if<telemetry::TimestampReadings>(&found->second); 1430e662eae8SEd Tanous if (readings == nullptr) 14311e1e598dSJonathan Doman { 143262598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 14331e1e598dSJonathan Doman return; 14341e1e598dSJonathan Doman } 14351e1e598dSJonathan Doman 1436156d6b00SAppaRao Puli for (const auto& it : 1437156d6b00SAppaRao Puli EventServiceManager::getInstance().subscriptionsMap) 1438156d6b00SAppaRao Puli { 1439e05aec50SEd Tanous Subscription& entry = *it.second; 1440*4b712a29SEd Tanous if (entry.userSub.eventFormatType == metricReportFormatType) 1441156d6b00SAppaRao Puli { 14421e1e598dSJonathan Doman entry.filterAndSendReports(id, *readings); 1443156d6b00SAppaRao Puli } 1444156d6b00SAppaRao Puli } 1445156d6b00SAppaRao Puli } 1446156d6b00SAppaRao Puli 1447156d6b00SAppaRao Puli void unregisterMetricReportSignal() 1448156d6b00SAppaRao Puli { 14497d1cc387SAppaRao Puli if (matchTelemetryMonitor) 14507d1cc387SAppaRao Puli { 145162598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Unregister"); 1452156d6b00SAppaRao Puli matchTelemetryMonitor.reset(); 1453156d6b00SAppaRao Puli matchTelemetryMonitor = nullptr; 1454156d6b00SAppaRao Puli } 14557d1cc387SAppaRao Puli } 1456156d6b00SAppaRao Puli 1457156d6b00SAppaRao Puli void registerMetricReportSignal() 1458156d6b00SAppaRao Puli { 14597d1cc387SAppaRao Puli if (!serviceEnabled || matchTelemetryMonitor) 1460156d6b00SAppaRao Puli { 146162598e31SEd Tanous BMCWEB_LOG_DEBUG("Not registering metric report signal."); 1462156d6b00SAppaRao Puli return; 1463156d6b00SAppaRao Puli } 1464156d6b00SAppaRao Puli 146562598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Register"); 1466c0353249SWludzik, Jozef std::string matchStr = "type='signal',member='PropertiesChanged'," 1467c0353249SWludzik, Jozef "interface='org.freedesktop.DBus.Properties'," 1468c0353249SWludzik, Jozef "arg0=xyz.openbmc_project.Telemetry.Report"; 1469156d6b00SAppaRao Puli 147059d494eeSPatrick Williams matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>( 147156d2396dSEd Tanous *crow::connections::systemBus, matchStr, getReadingsForReport); 1472156d6b00SAppaRao Puli } 147323a21a1cSEd Tanous }; 1474b52664e2SAppaRao Puli 1475b52664e2SAppaRao Puli } // namespace redfish 1476