1b52664e2SAppaRao Puli /* 26be832e2SEd Tanous Copyright (c) 2020 Intel Corporation 36be832e2SEd Tanous 46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License"); 56be832e2SEd Tanous you may not use this file except in compliance with the License. 66be832e2SEd Tanous You may obtain a copy of the License at 76be832e2SEd Tanous 86be832e2SEd Tanous http://www.apache.org/licenses/LICENSE-2.0 96be832e2SEd Tanous 106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software 116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS, 126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136be832e2SEd Tanous See the License for the specific language governing permissions and 146be832e2SEd Tanous limitations under the License. 15b52664e2SAppaRao Puli */ 16b52664e2SAppaRao Puli #pragma once 173ccb3adbSEd Tanous #include "dbus_utility.hpp" 183ccb3adbSEd Tanous #include "error_messages.hpp" 19d3a48a14SEd Tanous #include "event_matches_filter.hpp" 203ccb3adbSEd Tanous #include "event_service_store.hpp" 21f80a87f2SEd Tanous #include "filter_expr_executor.hpp" 22539d8c6bSEd Tanous #include "generated/enums/event.hpp" 23539d8c6bSEd Tanous #include "generated/enums/log_entry.hpp" 243ccb3adbSEd Tanous #include "http_client.hpp" 25c0353249SWludzik, Jozef #include "metric_report.hpp" 262c6ffdb0SEd Tanous #include "ossl_random.hpp" 273ccb3adbSEd Tanous #include "persistent_data.hpp" 287f4eb588SAppaRao Puli #include "registries.hpp" 298dab0f58SEd Tanous #include "registries_selector.hpp" 3050ebd4afSEd Tanous #include "str_utility.hpp" 3177665bdaSNan Zhou #include "utility.hpp" 323ccb3adbSEd Tanous #include "utils/json_utils.hpp" 335b90429aSEd Tanous #include "utils/time_utils.hpp" 347f4eb588SAppaRao Puli 357f4eb588SAppaRao Puli #include <sys/inotify.h> 36b52664e2SAppaRao Puli 37fb4fd5d4SZhenfei Tai #include <boost/asio/io_context.hpp> 38f80a87f2SEd Tanous #include <boost/circular_buffer.hpp> 39b52664e2SAppaRao Puli #include <boost/container/flat_map.hpp> 40ef4c65b7SEd Tanous #include <boost/url/format.hpp> 414a7fbefdSEd Tanous #include <boost/url/url_view_base.hpp> 42b5b40605Snitroglycerine #include <sdbusplus/bus/match.hpp> 431214b7e7SGunnar Mills 445e44e3d8SAppaRao Puli #include <algorithm> 45b52664e2SAppaRao Puli #include <cstdlib> 46b52664e2SAppaRao Puli #include <ctime> 47a14c9113SEd Tanous #include <format> 481bf712bcSAyushi Smriti #include <fstream> 49b52664e2SAppaRao Puli #include <memory> 503544d2a7SEd Tanous #include <ranges> 5126702d01SEd Tanous #include <span> 52a14c9113SEd Tanous #include <string> 5356ba386dSMyung Bae #include <string_view> 54b52664e2SAppaRao Puli 55b52664e2SAppaRao Puli namespace redfish 56b52664e2SAppaRao Puli { 57156d6b00SAppaRao Puli 58156d6b00SAppaRao Puli static constexpr const char* eventFormatType = "Event"; 59156d6b00SAppaRao Puli static constexpr const char* metricReportFormatType = "MetricReport"; 60156d6b00SAppaRao Puli 615e44e3d8SAppaRao Puli static constexpr const char* subscriptionTypeSSE = "SSE"; 621bf712bcSAyushi Smriti static constexpr const char* eventServiceFile = 631bf712bcSAyushi Smriti "/var/lib/bmcweb/eventservice_config.json"; 641bf712bcSAyushi Smriti 655e44e3d8SAppaRao Puli static constexpr const uint8_t maxNoOfSubscriptions = 20; 665e44e3d8SAppaRao Puli static constexpr const uint8_t maxNoOfSSESubscriptions = 10; 675e44e3d8SAppaRao Puli 68cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 694642bf8fSGeorge Liu static std::optional<boost::asio::posix::stream_descriptor> inotifyConn; 704642bf8fSGeorge Liu static constexpr const char* redfishEventLogDir = "/var/log"; 714642bf8fSGeorge Liu static constexpr const char* redfishEventLogFile = "/var/log/redfish"; 724642bf8fSGeorge Liu static constexpr const size_t iEventSize = sizeof(inotify_event); 73cf9e417dSEd Tanous 74cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 754642bf8fSGeorge Liu static int inotifyFd = -1; 76cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 774642bf8fSGeorge Liu static int dirWatchDesc = -1; 78cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 794642bf8fSGeorge Liu static int fileWatchDesc = -1; 80f80a87f2SEd Tanous struct EventLogObjectsType 81f80a87f2SEd Tanous { 82f80a87f2SEd Tanous std::string id; 83f80a87f2SEd Tanous std::string timestamp; 84f80a87f2SEd Tanous std::string messageId; 85f80a87f2SEd Tanous std::vector<std::string> messageArgs; 86f80a87f2SEd Tanous }; 874642bf8fSGeorge Liu 88fffb8c1fSEd Tanous namespace registries 894642bf8fSGeorge Liu { 907f4eb588SAppaRao Puli static const Message* 917f4eb588SAppaRao Puli getMsgFromRegistry(const std::string& messageKey, 9226702d01SEd Tanous const std::span<const MessageEntry>& registry) 937f4eb588SAppaRao Puli { 943544d2a7SEd Tanous std::span<const MessageEntry>::iterator messageIt = std::ranges::find_if( 953544d2a7SEd Tanous registry, [&messageKey](const MessageEntry& messageEntry) { 9655f79e6fSEd Tanous return messageKey == messageEntry.first; 977f4eb588SAppaRao Puli }); 9826702d01SEd Tanous if (messageIt != registry.end()) 997f4eb588SAppaRao Puli { 1007f4eb588SAppaRao Puli return &messageIt->second; 1017f4eb588SAppaRao Puli } 1027f4eb588SAppaRao Puli 1037f4eb588SAppaRao Puli return nullptr; 1047f4eb588SAppaRao Puli } 1057f4eb588SAppaRao Puli 10626ccae32SEd Tanous static const Message* formatMessage(std::string_view messageID) 1077f4eb588SAppaRao Puli { 1087f4eb588SAppaRao Puli // Redfish MessageIds are in the form 1097f4eb588SAppaRao Puli // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 1107f4eb588SAppaRao Puli // the right Message 1117f4eb588SAppaRao Puli std::vector<std::string> fields; 1127f4eb588SAppaRao Puli fields.reserve(4); 11350ebd4afSEd Tanous 11450ebd4afSEd Tanous bmcweb::split(fields, messageID, '.'); 1157f4eb588SAppaRao Puli if (fields.size() != 4) 1167f4eb588SAppaRao Puli { 1177f4eb588SAppaRao Puli return nullptr; 1187f4eb588SAppaRao Puli } 11902cad96eSEd Tanous const std::string& registryName = fields[0]; 12002cad96eSEd Tanous const std::string& messageKey = fields[3]; 1217f4eb588SAppaRao Puli 1227f4eb588SAppaRao Puli // Find the right registry and check it for the MessageKey 123b304bd79SP Dheeraj Srujan Kumar return getMsgFromRegistry(messageKey, getRegistryFromPrefix(registryName)); 1247f4eb588SAppaRao Puli } 125fffb8c1fSEd Tanous } // namespace registries 1267f4eb588SAppaRao Puli 1277f4eb588SAppaRao Puli namespace event_log 1287f4eb588SAppaRao Puli { 1292558979cSP Dheeraj Srujan Kumar inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) 1307f4eb588SAppaRao Puli { 1317f4eb588SAppaRao Puli static time_t prevTs = 0; 1327f4eb588SAppaRao Puli static int index = 0; 1337f4eb588SAppaRao Puli 1347f4eb588SAppaRao Puli // Get the entry timestamp 1357f4eb588SAppaRao Puli std::time_t curTs = 0; 1367f4eb588SAppaRao Puli std::tm timeStruct = {}; 1377f4eb588SAppaRao Puli std::istringstream entryStream(logEntry); 1387f4eb588SAppaRao Puli if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 1397f4eb588SAppaRao Puli { 1407f4eb588SAppaRao Puli curTs = std::mktime(&timeStruct); 1417f4eb588SAppaRao Puli if (curTs == -1) 1427f4eb588SAppaRao Puli { 1437f4eb588SAppaRao Puli return false; 1447f4eb588SAppaRao Puli } 1457f4eb588SAppaRao Puli } 1467f4eb588SAppaRao Puli // If the timestamp isn't unique, increment the index 1477f4eb588SAppaRao Puli index = (curTs == prevTs) ? index + 1 : 0; 1487f4eb588SAppaRao Puli 1497f4eb588SAppaRao Puli // Save the timestamp 1507f4eb588SAppaRao Puli prevTs = curTs; 1517f4eb588SAppaRao Puli 1527f4eb588SAppaRao Puli entryID = std::to_string(curTs); 1537f4eb588SAppaRao Puli if (index > 0) 1547f4eb588SAppaRao Puli { 1557f4eb588SAppaRao Puli entryID += "_" + std::to_string(index); 1567f4eb588SAppaRao Puli } 1577f4eb588SAppaRao Puli return true; 1587f4eb588SAppaRao Puli } 1597f4eb588SAppaRao Puli 16023a21a1cSEd Tanous inline int getEventLogParams(const std::string& logEntry, 16123a21a1cSEd Tanous std::string& timestamp, std::string& messageID, 1625e715de6SAppaRao Puli std::vector<std::string>& messageArgs) 1637f4eb588SAppaRao Puli { 1647f4eb588SAppaRao Puli // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 1657f4eb588SAppaRao Puli // First get the Timestamp 166f23b7296SEd Tanous size_t space = logEntry.find_first_of(' '); 1677f4eb588SAppaRao Puli if (space == std::string::npos) 1687f4eb588SAppaRao Puli { 16903d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", 17003d4d37cSAlexander Hansen logEntry); 1717f4eb588SAppaRao Puli return -EINVAL; 1727f4eb588SAppaRao Puli } 1737f4eb588SAppaRao Puli timestamp = logEntry.substr(0, space); 1747f4eb588SAppaRao Puli // Then get the log contents 175f23b7296SEd Tanous size_t entryStart = logEntry.find_first_not_of(' ', space); 1767f4eb588SAppaRao Puli if (entryStart == std::string::npos) 1777f4eb588SAppaRao Puli { 17803d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", 17903d4d37cSAlexander Hansen logEntry); 1807f4eb588SAppaRao Puli return -EINVAL; 1817f4eb588SAppaRao Puli } 1827f4eb588SAppaRao Puli std::string_view entry(logEntry); 1837f4eb588SAppaRao Puli entry.remove_prefix(entryStart); 1847f4eb588SAppaRao Puli // Use split to separate the entry into its fields 1857f4eb588SAppaRao Puli std::vector<std::string> logEntryFields; 18650ebd4afSEd Tanous bmcweb::split(logEntryFields, entry, ','); 1877f4eb588SAppaRao Puli // We need at least a MessageId to be valid 18826f6976fSEd Tanous if (logEntryFields.empty()) 1897f4eb588SAppaRao Puli { 19003d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", 19103d4d37cSAlexander Hansen logEntry); 1927f4eb588SAppaRao Puli return -EINVAL; 1937f4eb588SAppaRao Puli } 1947f4eb588SAppaRao Puli messageID = logEntryFields[0]; 1957f4eb588SAppaRao Puli 1967f4eb588SAppaRao Puli // Get the MessageArgs from the log if there are any 1977f4eb588SAppaRao Puli if (logEntryFields.size() > 1) 1987f4eb588SAppaRao Puli { 19902cad96eSEd Tanous const std::string& messageArgsStart = logEntryFields[1]; 2007f4eb588SAppaRao Puli // If the first string is empty, assume there are no MessageArgs 2017f4eb588SAppaRao Puli if (!messageArgsStart.empty()) 2027f4eb588SAppaRao Puli { 2035e715de6SAppaRao Puli messageArgs.assign(logEntryFields.begin() + 1, 2045e715de6SAppaRao Puli logEntryFields.end()); 2057f4eb588SAppaRao Puli } 2067f4eb588SAppaRao Puli } 2077f4eb588SAppaRao Puli 2087f4eb588SAppaRao Puli return 0; 2097f4eb588SAppaRao Puli } 2107f4eb588SAppaRao Puli 211bd79bce8SPatrick Williams inline int formatEventLogEntry( 212bd79bce8SPatrick Williams const std::string& logEntryID, const std::string& messageID, 213bd79bce8SPatrick Williams const std::span<std::string_view> messageArgs, std::string timestamp, 214bd79bce8SPatrick Williams const std::string& customText, nlohmann::json::object_t& logEntryJson) 2157f4eb588SAppaRao Puli { 2167f4eb588SAppaRao Puli // Get the Message from the MessageRegistry 217fffb8c1fSEd Tanous const registries::Message* message = registries::formatMessage(messageID); 2187f4eb588SAppaRao Puli 21980f595e7SEd Tanous if (message == nullptr) 2207f4eb588SAppaRao Puli { 22180f595e7SEd Tanous return -1; 2227f4eb588SAppaRao Puli } 2237f4eb588SAppaRao Puli 224bd79bce8SPatrick Williams std::string msg = 225bd79bce8SPatrick Williams redfish::registries::fillMessageArgs(messageArgs, message->message); 22680f595e7SEd Tanous if (msg.empty()) 22780f595e7SEd Tanous { 22880f595e7SEd Tanous return -1; 22980f595e7SEd Tanous } 2307f4eb588SAppaRao Puli 2317f4eb588SAppaRao Puli // Get the Created time from the timestamp. The log timestamp is in 2327f4eb588SAppaRao Puli // RFC3339 format which matches the Redfish format except for the 2337f4eb588SAppaRao Puli // fractional seconds between the '.' and the '+', so just remove them. 234f23b7296SEd Tanous std::size_t dot = timestamp.find_first_of('.'); 235b2f7609bSEd Tanous std::size_t plus = timestamp.find_first_of('+', dot); 2367f4eb588SAppaRao Puli if (dot != std::string::npos && plus != std::string::npos) 2377f4eb588SAppaRao Puli { 2387f4eb588SAppaRao Puli timestamp.erase(dot, plus - dot); 2397f4eb588SAppaRao Puli } 2407f4eb588SAppaRao Puli 2417f4eb588SAppaRao Puli // Fill in the log entry with the gathered data 2421476687dSEd Tanous logEntryJson["EventId"] = logEntryID; 243539d8c6bSEd Tanous 24480f595e7SEd Tanous logEntryJson["Severity"] = message->messageSeverity; 2451476687dSEd Tanous logEntryJson["Message"] = std::move(msg); 2461476687dSEd Tanous logEntryJson["MessageId"] = messageID; 2471476687dSEd Tanous logEntryJson["MessageArgs"] = messageArgs; 2481476687dSEd Tanous logEntryJson["EventTimestamp"] = std::move(timestamp); 2491476687dSEd Tanous logEntryJson["Context"] = customText; 2507f4eb588SAppaRao Puli return 0; 2517f4eb588SAppaRao Puli } 2527f4eb588SAppaRao Puli 2537f4eb588SAppaRao Puli } // namespace event_log 2547f4eb588SAppaRao Puli 255*a0969c70SMyung Bae class Subscription : public std::enable_shared_from_this<Subscription> 256b52664e2SAppaRao Puli { 257b52664e2SAppaRao Puli public: 258b52664e2SAppaRao Puli Subscription(const Subscription&) = delete; 259b52664e2SAppaRao Puli Subscription& operator=(const Subscription&) = delete; 260b52664e2SAppaRao Puli Subscription(Subscription&&) = delete; 261b52664e2SAppaRao Puli Subscription& operator=(Subscription&&) = delete; 262b52664e2SAppaRao Puli 26321a94d5cSMyung Bae Subscription(const persistent_data::UserSubscription& userSubIn, 26421a94d5cSMyung Bae const boost::urls::url_view_base& url, 2654a7fbefdSEd Tanous boost::asio::io_context& ioc) : 26621a94d5cSMyung Bae userSub(userSubIn), policy(std::make_shared<crow::ConnectionPolicy>()) 267b52664e2SAppaRao Puli { 2684b712a29SEd Tanous userSub.destinationUrl = url; 2695e44e3d8SAppaRao Puli client.emplace(ioc, policy); 2707adb85acSSunitha Harish // Subscription constructor 271d14a48ffSCarson Labrado policy->invalidResp = retryRespHandler; 272b52664e2SAppaRao Puli } 2734bbf237fSAppaRao Puli 2745e44e3d8SAppaRao Puli explicit Subscription(crow::sse_socket::Connection& connIn) : 2755e44e3d8SAppaRao Puli sseConn(&connIn) 2765e44e3d8SAppaRao Puli {} 2775e44e3d8SAppaRao Puli 2789f616dd1SEd Tanous ~Subscription() = default; 279b52664e2SAppaRao Puli 280*a0969c70SMyung Bae // callback for subscription sendData 281*a0969c70SMyung Bae void resHandler(const std::shared_ptr<Subscription>& /*unused*/, 282*a0969c70SMyung Bae const crow::Response& res) 283*a0969c70SMyung Bae { 284*a0969c70SMyung Bae BMCWEB_LOG_DEBUG("Response handled with return code: {}", 285*a0969c70SMyung Bae res.resultInt()); 286*a0969c70SMyung Bae 287*a0969c70SMyung Bae if (!client) 288*a0969c70SMyung Bae { 289*a0969c70SMyung Bae BMCWEB_LOG_ERROR( 290*a0969c70SMyung Bae "Http client wasn't filled but http client callback was called."); 291*a0969c70SMyung Bae return; 292*a0969c70SMyung Bae } 293*a0969c70SMyung Bae 294*a0969c70SMyung Bae if (userSub.retryPolicy != "TerminateAfterRetries") 295*a0969c70SMyung Bae { 296*a0969c70SMyung Bae return; 297*a0969c70SMyung Bae } 298*a0969c70SMyung Bae if (client->isTerminated()) 299*a0969c70SMyung Bae { 300*a0969c70SMyung Bae if (deleter) 301*a0969c70SMyung Bae { 302*a0969c70SMyung Bae BMCWEB_LOG_INFO( 303*a0969c70SMyung Bae "Subscription {} is deleted after MaxRetryAttempts", 304*a0969c70SMyung Bae userSub.id); 305*a0969c70SMyung Bae deleter(); 306*a0969c70SMyung Bae } 307*a0969c70SMyung Bae } 308*a0969c70SMyung Bae } 309*a0969c70SMyung Bae 3106d799e14SEd Tanous bool sendEventToSubscriber(std::string&& msg) 311b52664e2SAppaRao Puli { 3126ba8c82eSsunharis_in persistent_data::EventServiceConfig eventServiceConfig = 3136ba8c82eSsunharis_in persistent_data::EventServiceStore::getInstance() 3146ba8c82eSsunharis_in .getEventServiceConfig(); 3156ba8c82eSsunharis_in if (!eventServiceConfig.enabled) 3166ba8c82eSsunharis_in { 3176ba8c82eSsunharis_in return false; 3186ba8c82eSsunharis_in } 3196ba8c82eSsunharis_in 3205e44e3d8SAppaRao Puli if (client) 3215e44e3d8SAppaRao Puli { 322*a0969c70SMyung Bae client->sendDataWithCallback( 323*a0969c70SMyung Bae std::move(msg), userSub.destinationUrl, 3244b712a29SEd Tanous static_cast<ensuressl::VerifyCertificate>( 3254b712a29SEd Tanous userSub.verifyCertificate), 326*a0969c70SMyung Bae userSub.httpHeaders, boost::beast::http::verb::post, 327*a0969c70SMyung Bae std::bind_front(&Subscription::resHandler, this, 328*a0969c70SMyung Bae shared_from_this())); 3295e44e3d8SAppaRao Puli return true; 3305e44e3d8SAppaRao Puli } 3317adb85acSSunitha Harish 3324bbf237fSAppaRao Puli if (sseConn != nullptr) 3334bbf237fSAppaRao Puli { 3345e44e3d8SAppaRao Puli eventSeqNum++; 3356d799e14SEd Tanous sseConn->sendSseEvent(std::to_string(eventSeqNum), msg); 3364bbf237fSAppaRao Puli } 3376ba8c82eSsunharis_in return true; 3384bbf237fSAppaRao Puli } 3394bbf237fSAppaRao Puli 3406ba8c82eSsunharis_in bool sendTestEventLog() 3410b4bdd93SAppaRao Puli { 342f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 343f80a87f2SEd Tanous nlohmann::json& logEntryJson = logEntryArray.emplace_back(); 3440b4bdd93SAppaRao Puli 345613dabeaSEd Tanous logEntryJson["EventId"] = "TestID"; 346539d8c6bSEd Tanous logEntryJson["Severity"] = log_entry::EventSeverity::OK; 347613dabeaSEd Tanous logEntryJson["Message"] = "Generated test event"; 348613dabeaSEd Tanous logEntryJson["MessageId"] = "OpenBMC.0.2.TestEventLog"; 349d2cdd478SChandra Harkude // MemberId is 0 : since we are sending one event record. 350788b091bSIgor Kanyuka logEntryJson["MemberId"] = "0"; 351613dabeaSEd Tanous logEntryJson["MessageArgs"] = nlohmann::json::array(); 352613dabeaSEd Tanous logEntryJson["EventTimestamp"] = 353613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 3544b712a29SEd Tanous logEntryJson["Context"] = userSub.customText; 3550b4bdd93SAppaRao Puli 3561476687dSEd Tanous nlohmann::json msg; 3571476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 3581476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 3591476687dSEd Tanous msg["Name"] = "Event Log"; 3601476687dSEd Tanous msg["Events"] = logEntryArray; 3610b4bdd93SAppaRao Puli 362bd79bce8SPatrick Williams std::string strMsg = 363bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 3646d799e14SEd Tanous return sendEventToSubscriber(std::move(strMsg)); 3650b4bdd93SAppaRao Puli } 3660b4bdd93SAppaRao Puli 3677f4eb588SAppaRao Puli void filterAndSendEventLogs( 3687f4eb588SAppaRao Puli const std::vector<EventLogObjectsType>& eventRecords) 3697f4eb588SAppaRao Puli { 370f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 3717f4eb588SAppaRao Puli for (const EventLogObjectsType& logEntry : eventRecords) 3727f4eb588SAppaRao Puli { 373f80a87f2SEd Tanous std::vector<std::string_view> messageArgsView( 374f80a87f2SEd Tanous logEntry.messageArgs.begin(), logEntry.messageArgs.end()); 3757f4eb588SAppaRao Puli 376f80a87f2SEd Tanous nlohmann::json::object_t bmcLogEntry; 377f80a87f2SEd Tanous if (event_log::formatEventLogEntry( 378f80a87f2SEd Tanous logEntry.id, logEntry.messageId, messageArgsView, 3794b712a29SEd Tanous logEntry.timestamp, userSub.customText, bmcLogEntry) != 0) 3807f4eb588SAppaRao Puli { 38162598e31SEd Tanous BMCWEB_LOG_DEBUG("Read eventLog entry failed"); 3827f4eb588SAppaRao Puli continue; 3837f4eb588SAppaRao Puli } 384f80a87f2SEd Tanous 385d3a48a14SEd Tanous if (!eventMatchesFilter(userSub, bmcLogEntry, "")) 386f80a87f2SEd Tanous { 38703d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Event {} did not match the filter", 38803d4d37cSAlexander Hansen nlohmann::json(bmcLogEntry).dump()); 389f80a87f2SEd Tanous continue; 390f80a87f2SEd Tanous } 391f80a87f2SEd Tanous 392d3a48a14SEd Tanous if (filter) 393d3a48a14SEd Tanous { 394d3a48a14SEd Tanous if (!memberMatches(bmcLogEntry, *filter)) 395d3a48a14SEd Tanous { 396d3a48a14SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 397d3a48a14SEd Tanous continue; 398d3a48a14SEd Tanous } 399d3a48a14SEd Tanous } 400d3a48a14SEd Tanous 401f80a87f2SEd Tanous logEntryArray.emplace_back(std::move(bmcLogEntry)); 4027f4eb588SAppaRao Puli } 4037f4eb588SAppaRao Puli 40426f6976fSEd Tanous if (logEntryArray.empty()) 4057f4eb588SAppaRao Puli { 40662598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 4077f4eb588SAppaRao Puli return; 4087f4eb588SAppaRao Puli } 4097f4eb588SAppaRao Puli 4101476687dSEd Tanous nlohmann::json msg; 4111476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 4121476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 4131476687dSEd Tanous msg["Name"] = "Event Log"; 414f80a87f2SEd Tanous msg["Events"] = std::move(logEntryArray); 415bd79bce8SPatrick Williams std::string strMsg = 416bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 4176d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 4185e44e3d8SAppaRao Puli eventSeqNum++; 4197f4eb588SAppaRao Puli } 4207f4eb588SAppaRao Puli 421248d0230SEd Tanous void filterAndSendReports(const std::string& reportId, 4221e1e598dSJonathan Doman const telemetry::TimestampReadings& var) 423156d6b00SAppaRao Puli { 424ef4c65b7SEd Tanous boost::urls::url mrdUri = boost::urls::format( 425ef4c65b7SEd Tanous "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", 426ef4c65b7SEd Tanous reportId); 427156d6b00SAppaRao Puli 428156d6b00SAppaRao Puli // Empty list means no filter. Send everything. 4294b712a29SEd Tanous if (!userSub.metricReportDefinitions.empty()) 430156d6b00SAppaRao Puli { 4314b712a29SEd Tanous if (std::ranges::find(userSub.metricReportDefinitions, 4324b712a29SEd Tanous mrdUri.buffer()) == 4334b712a29SEd Tanous userSub.metricReportDefinitions.end()) 434156d6b00SAppaRao Puli { 435156d6b00SAppaRao Puli return; 436156d6b00SAppaRao Puli } 437156d6b00SAppaRao Puli } 438156d6b00SAppaRao Puli 439c0353249SWludzik, Jozef nlohmann::json msg; 440248d0230SEd Tanous if (!telemetry::fillReport(msg, reportId, var)) 441156d6b00SAppaRao Puli { 44262598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to fill the MetricReport for DBus " 44362598e31SEd Tanous "Report with id {}", 44462598e31SEd Tanous reportId); 445c0353249SWludzik, Jozef return; 446156d6b00SAppaRao Puli } 447156d6b00SAppaRao Puli 44822daffd7SAppaRao Puli // Context is set by user during Event subscription and it must be 44922daffd7SAppaRao Puli // set for MetricReport response. 4504b712a29SEd Tanous if (!userSub.customText.empty()) 45122daffd7SAppaRao Puli { 4524b712a29SEd Tanous msg["Context"] = userSub.customText; 45322daffd7SAppaRao Puli } 45422daffd7SAppaRao Puli 455bd79bce8SPatrick Williams std::string strMsg = 456bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 4576d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 458156d6b00SAppaRao Puli } 459156d6b00SAppaRao Puli 460d14a48ffSCarson Labrado void updateRetryConfig(uint32_t retryAttempts, 461d14a48ffSCarson Labrado uint32_t retryTimeoutInterval) 462fe44eb0bSAyushi Smriti { 46393cf0ac2SEd Tanous if (policy == nullptr) 46493cf0ac2SEd Tanous { 46593cf0ac2SEd Tanous BMCWEB_LOG_DEBUG("Retry policy was nullptr, ignoring set"); 46693cf0ac2SEd Tanous return; 46793cf0ac2SEd Tanous } 468d14a48ffSCarson Labrado policy->maxRetryAttempts = retryAttempts; 469d14a48ffSCarson Labrado policy->retryIntervalSecs = std::chrono::seconds(retryTimeoutInterval); 47062de0c68SAppaRao Puli } 471fe44eb0bSAyushi Smriti 4729eb808c1SEd Tanous uint64_t getEventSeqNum() const 47396330b99SSunitha Harish { 47496330b99SSunitha Harish return eventSeqNum; 47596330b99SSunitha Harish } 47696330b99SSunitha Harish 47756ba386dSMyung Bae void setSubscriptionId(const std::string& idIn) 4785e44e3d8SAppaRao Puli { 47956ba386dSMyung Bae BMCWEB_LOG_DEBUG("Subscription ID: {}", idIn); 48056ba386dSMyung Bae userSub.id = idIn; 4815e44e3d8SAppaRao Puli } 4825e44e3d8SAppaRao Puli 48356ba386dSMyung Bae std::string getSubscriptionId() const 4845e44e3d8SAppaRao Puli { 48556ba386dSMyung Bae return userSub.id; 4865e44e3d8SAppaRao Puli } 4875e44e3d8SAppaRao Puli 4885e44e3d8SAppaRao Puli bool matchSseId(const crow::sse_socket::Connection& thisConn) 4895e44e3d8SAppaRao Puli { 4905e44e3d8SAppaRao Puli return &thisConn == sseConn; 4915e44e3d8SAppaRao Puli } 4925e44e3d8SAppaRao Puli 493a7a80296SCarson Labrado // Check used to indicate what response codes are valid as part of our retry 494a7a80296SCarson Labrado // policy. 2XX is considered acceptable 495a7a80296SCarson Labrado static boost::system::error_code retryRespHandler(unsigned int respCode) 496a7a80296SCarson Labrado { 49762598e31SEd Tanous BMCWEB_LOG_DEBUG( 49862598e31SEd Tanous "Checking response code validity for SubscriptionEvent"); 499a7a80296SCarson Labrado if ((respCode < 200) || (respCode >= 300)) 500a7a80296SCarson Labrado { 501a7a80296SCarson Labrado return boost::system::errc::make_error_code( 502a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 503a7a80296SCarson Labrado } 504a7a80296SCarson Labrado 505a7a80296SCarson Labrado // Return 0 if the response code is valid 506a7a80296SCarson Labrado return boost::system::errc::make_error_code( 507a7a80296SCarson Labrado boost::system::errc::success); 5089fa6d147SNan Zhou } 509f80a87f2SEd Tanous 5104b712a29SEd Tanous persistent_data::UserSubscription userSub; 511*a0969c70SMyung Bae std::function<void()> deleter; 5124b712a29SEd Tanous 513f80a87f2SEd Tanous private: 514f80a87f2SEd Tanous uint64_t eventSeqNum = 1; 515f80a87f2SEd Tanous boost::urls::url host; 516f80a87f2SEd Tanous std::shared_ptr<crow::ConnectionPolicy> policy; 517f80a87f2SEd Tanous crow::sse_socket::Connection* sseConn = nullptr; 518f80a87f2SEd Tanous 519f80a87f2SEd Tanous std::optional<crow::HttpClient> client; 520f80a87f2SEd Tanous 521f80a87f2SEd Tanous public: 522f80a87f2SEd Tanous std::optional<filter_ast::LogicalAnd> filter; 523b52664e2SAppaRao Puli }; 524b52664e2SAppaRao Puli 525b52664e2SAppaRao Puli class EventServiceManager 526b52664e2SAppaRao Puli { 527b52664e2SAppaRao Puli private: 528d3a9e084SEd Tanous bool serviceEnabled = false; 529d3a9e084SEd Tanous uint32_t retryAttempts = 0; 530d3a9e084SEd Tanous uint32_t retryTimeoutInterval = 0; 5317d1cc387SAppaRao Puli 5322558979cSP Dheeraj Srujan Kumar std::streampos redfishLogFilePosition{0}; 5339f616dd1SEd Tanous size_t noOfEventLogSubscribers{0}; 5349f616dd1SEd Tanous size_t noOfMetricReportSubscribers{0}; 53559d494eeSPatrick Williams std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor; 536b52664e2SAppaRao Puli boost::container::flat_map<std::string, std::shared_ptr<Subscription>> 537b52664e2SAppaRao Puli subscriptionsMap; 538b52664e2SAppaRao Puli 5399f616dd1SEd Tanous uint64_t eventId{1}; 54096330b99SSunitha Harish 541f80a87f2SEd Tanous struct Event 542f80a87f2SEd Tanous { 543f80a87f2SEd Tanous std::string id; 544f80a87f2SEd Tanous nlohmann::json message; 545f80a87f2SEd Tanous }; 546f80a87f2SEd Tanous 547f80a87f2SEd Tanous constexpr static size_t maxMessages = 200; 548f80a87f2SEd Tanous boost::circular_buffer<Event> messages{maxMessages}; 549f80a87f2SEd Tanous 550f8ca6d79SEd Tanous boost::asio::io_context& ioc; 551f8ca6d79SEd Tanous 552b52664e2SAppaRao Puli public: 5539f616dd1SEd Tanous EventServiceManager(const EventServiceManager&) = delete; 5549f616dd1SEd Tanous EventServiceManager& operator=(const EventServiceManager&) = delete; 5559f616dd1SEd Tanous EventServiceManager(EventServiceManager&&) = delete; 5569f616dd1SEd Tanous EventServiceManager& operator=(EventServiceManager&&) = delete; 557ecd6a3a2SEd Tanous ~EventServiceManager() = default; 5589f616dd1SEd Tanous 559f8ca6d79SEd Tanous explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn) 560b52664e2SAppaRao Puli { 561f8ca6d79SEd Tanous // Load config from persist store. 562f8ca6d79SEd Tanous initConfig(); 563f8ca6d79SEd Tanous } 564f8ca6d79SEd Tanous 565f8ca6d79SEd Tanous static EventServiceManager& 566f8ca6d79SEd Tanous getInstance(boost::asio::io_context* ioc = nullptr) 567f8ca6d79SEd Tanous { 568f8ca6d79SEd Tanous static EventServiceManager handler(*ioc); 569b52664e2SAppaRao Puli return handler; 570b52664e2SAppaRao Puli } 571b52664e2SAppaRao Puli 5721bf712bcSAyushi Smriti void initConfig() 5731bf712bcSAyushi Smriti { 57428afb49cSJunLin Chen loadOldBehavior(); 5751bf712bcSAyushi Smriti 57628afb49cSJunLin Chen persistent_data::EventServiceConfig eventServiceConfig = 57728afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 57828afb49cSJunLin Chen .getEventServiceConfig(); 5791bf712bcSAyushi Smriti 58028afb49cSJunLin Chen serviceEnabled = eventServiceConfig.enabled; 58128afb49cSJunLin Chen retryAttempts = eventServiceConfig.retryAttempts; 58228afb49cSJunLin Chen retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval; 5831bf712bcSAyushi Smriti 58428afb49cSJunLin Chen for (const auto& it : persistent_data::EventServiceStore::getInstance() 58528afb49cSJunLin Chen .subscriptionsConfigMap) 5861bf712bcSAyushi Smriti { 5874b712a29SEd Tanous const persistent_data::UserSubscription& newSub = it.second; 5884bbf237fSAppaRao Puli 5896fd29553SEd Tanous boost::system::result<boost::urls::url> url = 5904b712a29SEd Tanous boost::urls::parse_absolute_uri(newSub.destinationUrl); 5911bf712bcSAyushi Smriti 592a716aa74SEd Tanous if (!url) 5931bf712bcSAyushi Smriti { 59462598e31SEd Tanous BMCWEB_LOG_ERROR( 59562598e31SEd Tanous "Failed to validate and split destination url"); 5961bf712bcSAyushi Smriti continue; 5971bf712bcSAyushi Smriti } 5981bf712bcSAyushi Smriti std::shared_ptr<Subscription> subValue = 59921a94d5cSMyung Bae std::make_shared<Subscription>(newSub, *url, ioc); 600*a0969c70SMyung Bae std::string id = subValue->userSub.id; 601*a0969c70SMyung Bae subValue->deleter = [id]() { 602*a0969c70SMyung Bae EventServiceManager::getInstance().deleteSubscription(id); 603*a0969c70SMyung Bae }; 6041bf712bcSAyushi Smriti 605*a0969c70SMyung Bae subscriptionsMap.emplace(id, subValue); 60628afb49cSJunLin Chen 60728afb49cSJunLin Chen updateNoOfSubscribersCount(); 60828afb49cSJunLin Chen 60983328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 61083328316SEd Tanous { 6112558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 61283328316SEd Tanous } 6132558979cSP Dheeraj Srujan Kumar 61428afb49cSJunLin Chen // Update retry configuration. 61528afb49cSJunLin Chen subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 6161bf712bcSAyushi Smriti } 6171bf712bcSAyushi Smriti } 6181bf712bcSAyushi Smriti 61956d2396dSEd Tanous static void loadOldBehavior() 620b52664e2SAppaRao Puli { 62128afb49cSJunLin Chen std::ifstream eventConfigFile(eventServiceFile); 62228afb49cSJunLin Chen if (!eventConfigFile.good()) 6231bf712bcSAyushi Smriti { 62462598e31SEd Tanous BMCWEB_LOG_DEBUG("Old eventService config not exist"); 62528afb49cSJunLin Chen return; 62628afb49cSJunLin Chen } 62728afb49cSJunLin Chen auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false); 62828afb49cSJunLin Chen if (jsonData.is_discarded()) 6294bbf237fSAppaRao Puli { 63062598e31SEd Tanous BMCWEB_LOG_ERROR("Old eventService config parse error."); 63128afb49cSJunLin Chen return; 63228afb49cSJunLin Chen } 63328afb49cSJunLin Chen 6340bdda665SEd Tanous const nlohmann::json::object_t* obj = 6350bdda665SEd Tanous jsonData.get_ptr<const nlohmann::json::object_t*>(); 6360bdda665SEd Tanous for (const auto& item : *obj) 63728afb49cSJunLin Chen { 6380bdda665SEd Tanous if (item.first == "Configuration") 63928afb49cSJunLin Chen { 64028afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 64128afb49cSJunLin Chen .getEventServiceConfig() 6420bdda665SEd Tanous .fromJson(item.second); 64328afb49cSJunLin Chen } 6440bdda665SEd Tanous else if (item.first == "Subscriptions") 64528afb49cSJunLin Chen { 6460bdda665SEd Tanous for (const auto& elem : item.second) 64728afb49cSJunLin Chen { 6484b712a29SEd Tanous std::optional<persistent_data::UserSubscription> 64928afb49cSJunLin Chen newSubscription = 65028afb49cSJunLin Chen persistent_data::UserSubscription::fromJson(elem, 65128afb49cSJunLin Chen true); 6524b712a29SEd Tanous if (!newSubscription) 65328afb49cSJunLin Chen { 65462598e31SEd Tanous BMCWEB_LOG_ERROR("Problem reading subscription " 65562598e31SEd Tanous "from old persistent store"); 6564bbf237fSAppaRao Puli continue; 6574bbf237fSAppaRao Puli } 6584b712a29SEd Tanous persistent_data::UserSubscription& newSub = 6594b712a29SEd Tanous *newSubscription; 6601bf712bcSAyushi Smriti 66128afb49cSJunLin Chen std::uniform_int_distribution<uint32_t> dist(0); 66228afb49cSJunLin Chen bmcweb::OpenSSLGenerator gen; 6631bf712bcSAyushi Smriti 66428afb49cSJunLin Chen std::string id; 6651bf712bcSAyushi Smriti 66628afb49cSJunLin Chen int retry = 3; 667e662eae8SEd Tanous while (retry != 0) 6681bf712bcSAyushi Smriti { 66928afb49cSJunLin Chen id = std::to_string(dist(gen)); 67028afb49cSJunLin Chen if (gen.error()) 6717d1cc387SAppaRao Puli { 67228afb49cSJunLin Chen retry = 0; 67328afb49cSJunLin Chen break; 67428afb49cSJunLin Chen } 6754b712a29SEd Tanous newSub.id = id; 67628afb49cSJunLin Chen auto inserted = 67728afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 67828afb49cSJunLin Chen .subscriptionsConfigMap.insert( 6794b712a29SEd Tanous std::pair(id, newSub)); 68028afb49cSJunLin Chen if (inserted.second) 68128afb49cSJunLin Chen { 68228afb49cSJunLin Chen break; 68328afb49cSJunLin Chen } 68428afb49cSJunLin Chen --retry; 6857d1cc387SAppaRao Puli } 6867d1cc387SAppaRao Puli 68728afb49cSJunLin Chen if (retry <= 0) 68828afb49cSJunLin Chen { 68962598e31SEd Tanous BMCWEB_LOG_ERROR( 69062598e31SEd Tanous "Failed to generate random number from old " 69162598e31SEd Tanous "persistent store"); 69228afb49cSJunLin Chen continue; 69328afb49cSJunLin Chen } 69428afb49cSJunLin Chen } 69528afb49cSJunLin Chen } 69628afb49cSJunLin Chen 69728afb49cSJunLin Chen persistent_data::getConfig().writeData(); 6984c521c3cSEd Tanous std::error_code ec; 6994c521c3cSEd Tanous std::filesystem::remove(eventServiceFile, ec); 7004c521c3cSEd Tanous if (ec) 7014c521c3cSEd Tanous { 7024c521c3cSEd Tanous BMCWEB_LOG_DEBUG( 7034c521c3cSEd Tanous "Failed to remove old event service file. Ignoring"); 7044c521c3cSEd Tanous } 7054c521c3cSEd Tanous else 7064c521c3cSEd Tanous { 70762598e31SEd Tanous BMCWEB_LOG_DEBUG("Remove old eventservice config"); 70828afb49cSJunLin Chen } 70928afb49cSJunLin Chen } 7104c521c3cSEd Tanous } 71128afb49cSJunLin Chen 7129eb808c1SEd Tanous void updateSubscriptionData() const 71328afb49cSJunLin Chen { 71428afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 71528afb49cSJunLin Chen .eventServiceConfig.enabled = serviceEnabled; 71628afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 71728afb49cSJunLin Chen .eventServiceConfig.retryAttempts = retryAttempts; 71828afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 71928afb49cSJunLin Chen .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval; 72028afb49cSJunLin Chen 72128afb49cSJunLin Chen persistent_data::getConfig().writeData(); 72228afb49cSJunLin Chen } 72328afb49cSJunLin Chen 72428afb49cSJunLin Chen void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg) 7257d1cc387SAppaRao Puli { 7267d1cc387SAppaRao Puli bool updateConfig = false; 727fe44eb0bSAyushi Smriti bool updateRetryCfg = false; 7287d1cc387SAppaRao Puli 72928afb49cSJunLin Chen if (serviceEnabled != cfg.enabled) 7307d1cc387SAppaRao Puli { 73128afb49cSJunLin Chen serviceEnabled = cfg.enabled; 732e662eae8SEd Tanous if (serviceEnabled && noOfMetricReportSubscribers != 0U) 7337d1cc387SAppaRao Puli { 7347d1cc387SAppaRao Puli registerMetricReportSignal(); 7357d1cc387SAppaRao Puli } 7367d1cc387SAppaRao Puli else 7377d1cc387SAppaRao Puli { 7387d1cc387SAppaRao Puli unregisterMetricReportSignal(); 7397d1cc387SAppaRao Puli } 7407d1cc387SAppaRao Puli updateConfig = true; 7417d1cc387SAppaRao Puli } 7427d1cc387SAppaRao Puli 74328afb49cSJunLin Chen if (retryAttempts != cfg.retryAttempts) 7447d1cc387SAppaRao Puli { 74528afb49cSJunLin Chen retryAttempts = cfg.retryAttempts; 7467d1cc387SAppaRao Puli updateConfig = true; 747fe44eb0bSAyushi Smriti updateRetryCfg = true; 7487d1cc387SAppaRao Puli } 7497d1cc387SAppaRao Puli 75028afb49cSJunLin Chen if (retryTimeoutInterval != cfg.retryTimeoutInterval) 7517d1cc387SAppaRao Puli { 75228afb49cSJunLin Chen retryTimeoutInterval = cfg.retryTimeoutInterval; 7537d1cc387SAppaRao Puli updateConfig = true; 754fe44eb0bSAyushi Smriti updateRetryCfg = true; 7557d1cc387SAppaRao Puli } 7567d1cc387SAppaRao Puli 7577d1cc387SAppaRao Puli if (updateConfig) 7587d1cc387SAppaRao Puli { 7597d1cc387SAppaRao Puli updateSubscriptionData(); 7607d1cc387SAppaRao Puli } 761fe44eb0bSAyushi Smriti 762fe44eb0bSAyushi Smriti if (updateRetryCfg) 763fe44eb0bSAyushi Smriti { 764fe44eb0bSAyushi Smriti // Update the changed retry config to all subscriptions 765fe44eb0bSAyushi Smriti for (const auto& it : 766fe44eb0bSAyushi Smriti EventServiceManager::getInstance().subscriptionsMap) 767fe44eb0bSAyushi Smriti { 7685e44e3d8SAppaRao Puli Subscription& entry = *it.second; 7695e44e3d8SAppaRao Puli entry.updateRetryConfig(retryAttempts, retryTimeoutInterval); 770fe44eb0bSAyushi Smriti } 771fe44eb0bSAyushi Smriti } 7727d1cc387SAppaRao Puli } 7737d1cc387SAppaRao Puli 7747d1cc387SAppaRao Puli void updateNoOfSubscribersCount() 7757d1cc387SAppaRao Puli { 7767d1cc387SAppaRao Puli size_t eventLogSubCount = 0; 7777d1cc387SAppaRao Puli size_t metricReportSubCount = 0; 7787d1cc387SAppaRao Puli for (const auto& it : subscriptionsMap) 7797d1cc387SAppaRao Puli { 7807d1cc387SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 7814b712a29SEd Tanous if (entry->userSub.eventFormatType == eventFormatType) 7827d1cc387SAppaRao Puli { 7837d1cc387SAppaRao Puli eventLogSubCount++; 7847d1cc387SAppaRao Puli } 7854b712a29SEd Tanous else if (entry->userSub.eventFormatType == metricReportFormatType) 7867d1cc387SAppaRao Puli { 7877d1cc387SAppaRao Puli metricReportSubCount++; 7887d1cc387SAppaRao Puli } 7897d1cc387SAppaRao Puli } 7907d1cc387SAppaRao Puli 7917d1cc387SAppaRao Puli noOfEventLogSubscribers = eventLogSubCount; 7927d1cc387SAppaRao Puli if (noOfMetricReportSubscribers != metricReportSubCount) 7937d1cc387SAppaRao Puli { 7947d1cc387SAppaRao Puli noOfMetricReportSubscribers = metricReportSubCount; 795e662eae8SEd Tanous if (noOfMetricReportSubscribers != 0U) 7967d1cc387SAppaRao Puli { 7977d1cc387SAppaRao Puli registerMetricReportSignal(); 7987d1cc387SAppaRao Puli } 7997d1cc387SAppaRao Puli else 8007d1cc387SAppaRao Puli { 8017d1cc387SAppaRao Puli unregisterMetricReportSignal(); 8027d1cc387SAppaRao Puli } 8037d1cc387SAppaRao Puli } 8047d1cc387SAppaRao Puli } 8057d1cc387SAppaRao Puli 806b52664e2SAppaRao Puli std::shared_ptr<Subscription> getSubscription(const std::string& id) 807b52664e2SAppaRao Puli { 808b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 809b52664e2SAppaRao Puli if (obj == subscriptionsMap.end()) 810b52664e2SAppaRao Puli { 81162598e31SEd Tanous BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id); 812b52664e2SAppaRao Puli return nullptr; 813b52664e2SAppaRao Puli } 814b52664e2SAppaRao Puli std::shared_ptr<Subscription> subValue = obj->second; 815b52664e2SAppaRao Puli return subValue; 816b52664e2SAppaRao Puli } 817b52664e2SAppaRao Puli 818f80a87f2SEd Tanous std::string 819f80a87f2SEd Tanous addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue) 820b52664e2SAppaRao Puli { 821fc76b8acSEd Tanous std::uniform_int_distribution<uint32_t> dist(0); 822fc76b8acSEd Tanous bmcweb::OpenSSLGenerator gen; 823fc76b8acSEd Tanous 824b52664e2SAppaRao Puli std::string id; 825b52664e2SAppaRao Puli 826b52664e2SAppaRao Puli int retry = 3; 827e662eae8SEd Tanous while (retry != 0) 828b52664e2SAppaRao Puli { 829fc76b8acSEd Tanous id = std::to_string(dist(gen)); 830fc76b8acSEd Tanous if (gen.error()) 831fc76b8acSEd Tanous { 832fc76b8acSEd Tanous retry = 0; 833fc76b8acSEd Tanous break; 834fc76b8acSEd Tanous } 835b52664e2SAppaRao Puli auto inserted = subscriptionsMap.insert(std::pair(id, subValue)); 836b52664e2SAppaRao Puli if (inserted.second) 837b52664e2SAppaRao Puli { 838b52664e2SAppaRao Puli break; 839b52664e2SAppaRao Puli } 840b52664e2SAppaRao Puli --retry; 84123a21a1cSEd Tanous } 842b52664e2SAppaRao Puli 843b52664e2SAppaRao Puli if (retry <= 0) 844b52664e2SAppaRao Puli { 84562598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to generate random number"); 846abb93cddSEd Tanous return ""; 847b52664e2SAppaRao Puli } 848b52664e2SAppaRao Puli 84956ba386dSMyung Bae // Set Subscription ID for back trace 85056ba386dSMyung Bae subValue->setSubscriptionId(id); 85156ba386dSMyung Bae 8524b712a29SEd Tanous persistent_data::UserSubscription newSub(subValue->userSub); 853a14c9113SEd Tanous 85428afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 8554b712a29SEd Tanous .subscriptionsConfigMap.emplace(newSub.id, newSub); 85628afb49cSJunLin Chen 8577d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 8581bf712bcSAyushi Smriti 85983328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 86083328316SEd Tanous { 8612558979cSP Dheeraj Srujan Kumar if (redfishLogFilePosition != 0) 8627f4eb588SAppaRao Puli { 8632558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 8647f4eb588SAppaRao Puli } 86583328316SEd Tanous } 866fe44eb0bSAyushi Smriti // Update retry configuration. 867fe44eb0bSAyushi Smriti subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 868fe44eb0bSAyushi Smriti 869f80a87f2SEd Tanous return id; 870f80a87f2SEd Tanous } 871f80a87f2SEd Tanous 872f80a87f2SEd Tanous std::string 873f80a87f2SEd Tanous addSSESubscription(const std::shared_ptr<Subscription>& subValue, 874f80a87f2SEd Tanous std::string_view lastEventId) 875f80a87f2SEd Tanous { 876f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 877f80a87f2SEd Tanous 878f80a87f2SEd Tanous if (!lastEventId.empty()) 879f80a87f2SEd Tanous { 880f80a87f2SEd Tanous BMCWEB_LOG_INFO("Attempting to find message for last id {}", 881f80a87f2SEd Tanous lastEventId); 882f80a87f2SEd Tanous boost::circular_buffer<Event>::iterator lastEvent = 883f80a87f2SEd Tanous std::find_if(messages.begin(), messages.end(), 884f80a87f2SEd Tanous [&lastEventId](const Event& event) { 885f80a87f2SEd Tanous return event.id == lastEventId; 886f80a87f2SEd Tanous }); 887f80a87f2SEd Tanous // Can't find a matching ID 888f80a87f2SEd Tanous if (lastEvent == messages.end()) 889f80a87f2SEd Tanous { 890f80a87f2SEd Tanous nlohmann::json msg = messages::eventBufferExceeded(); 891f80a87f2SEd Tanous // If the buffer overloaded, send all messages. 8926d799e14SEd Tanous subValue->sendEventToSubscriber(msg); 893f80a87f2SEd Tanous lastEvent = messages.begin(); 894f80a87f2SEd Tanous } 895f80a87f2SEd Tanous else 896f80a87f2SEd Tanous { 897f80a87f2SEd Tanous // Skip the last event the user already has 898f80a87f2SEd Tanous lastEvent++; 899f80a87f2SEd Tanous } 900f80a87f2SEd Tanous 901f80a87f2SEd Tanous for (boost::circular_buffer<Event>::const_iterator event = 902f80a87f2SEd Tanous lastEvent; 903f80a87f2SEd Tanous lastEvent != messages.end(); lastEvent++) 904f80a87f2SEd Tanous { 9056d799e14SEd Tanous subValue->sendEventToSubscriber(event->message); 906f80a87f2SEd Tanous } 907f80a87f2SEd Tanous } 908f80a87f2SEd Tanous return id; 909f80a87f2SEd Tanous } 910f80a87f2SEd Tanous 911f80a87f2SEd Tanous std::string 912f80a87f2SEd Tanous addPushSubscription(const std::shared_ptr<Subscription>& subValue) 913f80a87f2SEd Tanous { 914f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 915*a0969c70SMyung Bae subValue->deleter = [id]() { 916*a0969c70SMyung Bae EventServiceManager::getInstance().deleteSubscription(id); 917*a0969c70SMyung Bae }; 918f80a87f2SEd Tanous updateSubscriptionData(); 919b52664e2SAppaRao Puli return id; 920b52664e2SAppaRao Puli } 921b52664e2SAppaRao Puli 922b52664e2SAppaRao Puli bool isSubscriptionExist(const std::string& id) 923b52664e2SAppaRao Puli { 924b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 92555f79e6fSEd Tanous return obj != subscriptionsMap.end(); 926b52664e2SAppaRao Puli } 927b52664e2SAppaRao Puli 9284b712a29SEd Tanous bool deleteSubscription(const std::string& id) 929b52664e2SAppaRao Puli { 930b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 9314b712a29SEd Tanous if (obj == subscriptionsMap.end()) 932b52664e2SAppaRao Puli { 9334b712a29SEd Tanous BMCWEB_LOG_WARNING("Could not find subscription with id {}", id); 9344b712a29SEd Tanous return false; 9354b712a29SEd Tanous } 936b52664e2SAppaRao Puli subscriptionsMap.erase(obj); 9374b712a29SEd Tanous auto& event = persistent_data::EventServiceStore::getInstance(); 9384b712a29SEd Tanous auto persistentObj = event.subscriptionsConfigMap.find(id); 9394b712a29SEd Tanous if (persistentObj == event.subscriptionsConfigMap.end()) 9404b712a29SEd Tanous { 9414b712a29SEd Tanous BMCWEB_LOG_ERROR("Subscription wasn't in persistent data"); 9424b712a29SEd Tanous return true; 9434b712a29SEd Tanous } 94428afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 9454b712a29SEd Tanous .subscriptionsConfigMap.erase(persistentObj); 9467d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 947b52664e2SAppaRao Puli updateSubscriptionData(); 9484b712a29SEd Tanous 9494b712a29SEd Tanous return true; 950b52664e2SAppaRao Puli } 951b52664e2SAppaRao Puli 9525e44e3d8SAppaRao Puli void deleteSseSubscription(const crow::sse_socket::Connection& thisConn) 9535e44e3d8SAppaRao Puli { 954bdbfae2aSEd Tanous for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();) 9555e44e3d8SAppaRao Puli { 956bdbfae2aSEd Tanous std::shared_ptr<Subscription> entry = it->second; 9575e44e3d8SAppaRao Puli bool entryIsThisConn = entry->matchSseId(thisConn); 9585e44e3d8SAppaRao Puli if (entryIsThisConn) 9595e44e3d8SAppaRao Puli { 9605e44e3d8SAppaRao Puli persistent_data::EventServiceStore::getInstance() 9615e44e3d8SAppaRao Puli .subscriptionsConfigMap.erase( 962bdbfae2aSEd Tanous it->second->getSubscriptionId()); 963bdbfae2aSEd Tanous it = subscriptionsMap.erase(it); 9645e44e3d8SAppaRao Puli return; 9655e44e3d8SAppaRao Puli } 966bdbfae2aSEd Tanous it++; 9675e44e3d8SAppaRao Puli } 9685e44e3d8SAppaRao Puli } 9695e44e3d8SAppaRao Puli 9705e44e3d8SAppaRao Puli size_t getNumberOfSubscriptions() const 971b52664e2SAppaRao Puli { 972b52664e2SAppaRao Puli return subscriptionsMap.size(); 973b52664e2SAppaRao Puli } 974b52664e2SAppaRao Puli 9755e44e3d8SAppaRao Puli size_t getNumberOfSSESubscriptions() const 9765e44e3d8SAppaRao Puli { 9773544d2a7SEd Tanous auto size = std::ranges::count_if( 9783544d2a7SEd Tanous subscriptionsMap, 9795e44e3d8SAppaRao Puli [](const std::pair<std::string, std::shared_ptr<Subscription>>& 9805e44e3d8SAppaRao Puli entry) { 9814b712a29SEd Tanous return (entry.second->userSub.subscriptionType == 9824b712a29SEd Tanous subscriptionTypeSSE); 9835e44e3d8SAppaRao Puli }); 9845e44e3d8SAppaRao Puli return static_cast<size_t>(size); 9855e44e3d8SAppaRao Puli } 9865e44e3d8SAppaRao Puli 987b52664e2SAppaRao Puli std::vector<std::string> getAllIDs() 988b52664e2SAppaRao Puli { 989b52664e2SAppaRao Puli std::vector<std::string> idList; 990b52664e2SAppaRao Puli for (const auto& it : subscriptionsMap) 991b52664e2SAppaRao Puli { 992b52664e2SAppaRao Puli idList.emplace_back(it.first); 993b52664e2SAppaRao Puli } 994b52664e2SAppaRao Puli return idList; 995b52664e2SAppaRao Puli } 996b52664e2SAppaRao Puli 9976ba8c82eSsunharis_in bool sendTestEventLog() 9980b4bdd93SAppaRao Puli { 9995e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 10000b4bdd93SAppaRao Puli { 10010b4bdd93SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 10026ba8c82eSsunharis_in if (!entry->sendTestEventLog()) 10036ba8c82eSsunharis_in { 10046ba8c82eSsunharis_in return false; 10050b4bdd93SAppaRao Puli } 10060b4bdd93SAppaRao Puli } 10076ba8c82eSsunharis_in return true; 10086ba8c82eSsunharis_in } 1009e9a14131SAppaRao Puli 1010f80a87f2SEd Tanous void sendEvent(nlohmann::json::object_t eventMessage, 1011f80a87f2SEd Tanous std::string_view origin, std::string_view resourceType) 101296330b99SSunitha Harish { 1013613dabeaSEd Tanous eventMessage["EventId"] = eventId; 1014f80a87f2SEd Tanous 1015613dabeaSEd Tanous eventMessage["EventTimestamp"] = 1016613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 1017613dabeaSEd Tanous eventMessage["OriginOfCondition"] = origin; 1018613dabeaSEd Tanous 1019f80a87f2SEd Tanous // MemberId is 0 : since we are sending one event record. 1020788b091bSIgor Kanyuka eventMessage["MemberId"] = "0"; 102196330b99SSunitha Harish 1022f80a87f2SEd Tanous messages.push_back(Event(std::to_string(eventId), eventMessage)); 1023f80a87f2SEd Tanous 1024f80a87f2SEd Tanous for (auto& it : subscriptionsMap) 102596330b99SSunitha Harish { 1026f80a87f2SEd Tanous std::shared_ptr<Subscription>& entry = it.second; 1027d3a48a14SEd Tanous if (!eventMatchesFilter(entry->userSub, eventMessage, resourceType)) 102896330b99SSunitha Harish { 1029f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 1030f80a87f2SEd Tanous continue; 103196330b99SSunitha Harish } 1032f80a87f2SEd Tanous 1033f80a87f2SEd Tanous nlohmann::json::array_t eventRecord; 1034f80a87f2SEd Tanous eventRecord.emplace_back(eventMessage); 1035f80a87f2SEd Tanous 1036613dabeaSEd Tanous nlohmann::json msgJson; 1037613dabeaSEd Tanous 1038613dabeaSEd Tanous msgJson["@odata.type"] = "#Event.v1_4_0.Event"; 1039613dabeaSEd Tanous msgJson["Name"] = "Event Log"; 1040613dabeaSEd Tanous msgJson["Id"] = eventId; 1041f80a87f2SEd Tanous msgJson["Events"] = std::move(eventRecord); 1042f52c03c1SCarson Labrado 1043f52c03c1SCarson Labrado std::string strMsg = msgJson.dump( 1044f52c03c1SCarson Labrado 2, ' ', true, nlohmann::json::error_handler_t::replace); 10456d799e14SEd Tanous entry->sendEventToSubscriber(std::move(strMsg)); 10468ece0e45SEd Tanous eventId++; // increment the eventId 104796330b99SSunitha Harish } 104896330b99SSunitha Harish } 104996330b99SSunitha Harish 10502558979cSP Dheeraj Srujan Kumar void resetRedfishFilePosition() 10517f4eb588SAppaRao Puli { 10522558979cSP Dheeraj Srujan Kumar // Control would be here when Redfish file is created. 10532558979cSP Dheeraj Srujan Kumar // Reset File Position as new file is created 10542558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = 0; 10552558979cSP Dheeraj Srujan Kumar } 10562558979cSP Dheeraj Srujan Kumar 10572558979cSP Dheeraj Srujan Kumar void cacheRedfishLogFile() 10582558979cSP Dheeraj Srujan Kumar { 10592558979cSP Dheeraj Srujan Kumar // Open the redfish file and read till the last record. 10602558979cSP Dheeraj Srujan Kumar 10617f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 10627f4eb588SAppaRao Puli if (!logStream.good()) 10637f4eb588SAppaRao Puli { 106462598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed "); 10657f4eb588SAppaRao Puli return; 10667f4eb588SAppaRao Puli } 10677f4eb588SAppaRao Puli std::string logEntry; 10687f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 10697f4eb588SAppaRao Puli { 10702558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 10717f4eb588SAppaRao Puli } 10727f4eb588SAppaRao Puli } 10737f4eb588SAppaRao Puli 10747f4eb588SAppaRao Puli void readEventLogsFromFile() 10757f4eb588SAppaRao Puli { 10767f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 10777f4eb588SAppaRao Puli if (!logStream.good()) 10787f4eb588SAppaRao Puli { 107962598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed"); 10807f4eb588SAppaRao Puli return; 10817f4eb588SAppaRao Puli } 10827f4eb588SAppaRao Puli 10837f4eb588SAppaRao Puli std::vector<EventLogObjectsType> eventRecords; 10847f4eb588SAppaRao Puli 10857f4eb588SAppaRao Puli std::string logEntry; 10862558979cSP Dheeraj Srujan Kumar 108703d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: seek to {}", 108803d4d37cSAlexander Hansen static_cast<int>(redfishLogFilePosition)); 108903d4d37cSAlexander Hansen 10902558979cSP Dheeraj Srujan Kumar // Get the read pointer to the next log to be read. 10912558979cSP Dheeraj Srujan Kumar logStream.seekg(redfishLogFilePosition); 10922558979cSP Dheeraj Srujan Kumar 10937f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 10947f4eb588SAppaRao Puli { 109503d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry"); 10962558979cSP Dheeraj Srujan Kumar // Update Pointer position 10972558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 10982558979cSP Dheeraj Srujan Kumar 10992558979cSP Dheeraj Srujan Kumar std::string idStr; 11002558979cSP Dheeraj Srujan Kumar if (!event_log::getUniqueEntryID(logEntry, idStr)) 11017f4eb588SAppaRao Puli { 110203d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 110303d4d37cSAlexander Hansen "Redfish log file: could not get unique entry id for {}", 110403d4d37cSAlexander Hansen logEntry); 11057f4eb588SAppaRao Puli continue; 11067f4eb588SAppaRao Puli } 11077f4eb588SAppaRao Puli 1108e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 11097f4eb588SAppaRao Puli { 11102558979cSP Dheeraj Srujan Kumar // If Service is not enabled, no need to compute 11112558979cSP Dheeraj Srujan Kumar // the remaining items below. 11122558979cSP Dheeraj Srujan Kumar // But, Loop must continue to keep track of Timestamp 111303d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 111403d4d37cSAlexander Hansen "Redfish log file: no subscribers / event service not enabled"); 11157f4eb588SAppaRao Puli continue; 11167f4eb588SAppaRao Puli } 11177f4eb588SAppaRao Puli 11187f4eb588SAppaRao Puli std::string timestamp; 11197f4eb588SAppaRao Puli std::string messageID; 11205e715de6SAppaRao Puli std::vector<std::string> messageArgs; 11217f4eb588SAppaRao Puli if (event_log::getEventLogParams(logEntry, timestamp, messageID, 11227f4eb588SAppaRao Puli messageArgs) != 0) 11237f4eb588SAppaRao Puli { 112403d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}", 112503d4d37cSAlexander Hansen logEntry); 11267f4eb588SAppaRao Puli continue; 11277f4eb588SAppaRao Puli } 11287f4eb588SAppaRao Puli 1129f80a87f2SEd Tanous eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs); 11307f4eb588SAppaRao Puli } 11317f4eb588SAppaRao Puli 1132e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 11332558979cSP Dheeraj Srujan Kumar { 113462598e31SEd Tanous BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions."); 11352558979cSP Dheeraj Srujan Kumar return; 11362558979cSP Dheeraj Srujan Kumar } 11372558979cSP Dheeraj Srujan Kumar 11382558979cSP Dheeraj Srujan Kumar if (eventRecords.empty()) 11392558979cSP Dheeraj Srujan Kumar { 11402558979cSP Dheeraj Srujan Kumar // No Records to send 114162598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 11422558979cSP Dheeraj Srujan Kumar return; 11432558979cSP Dheeraj Srujan Kumar } 11442558979cSP Dheeraj Srujan Kumar 11455e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 11467f4eb588SAppaRao Puli { 11477f4eb588SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 11484b712a29SEd Tanous if (entry->userSub.eventFormatType == "Event") 11497f4eb588SAppaRao Puli { 11507f4eb588SAppaRao Puli entry->filterAndSendEventLogs(eventRecords); 11517f4eb588SAppaRao Puli } 11527f4eb588SAppaRao Puli } 11537f4eb588SAppaRao Puli } 11547f4eb588SAppaRao Puli 11557f4eb588SAppaRao Puli static void watchRedfishEventLogFile() 11567f4eb588SAppaRao Puli { 11576a9f85f9SAppaRao Puli if (!inotifyConn) 11587f4eb588SAppaRao Puli { 115903d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("inotify Connection is not present"); 11607f4eb588SAppaRao Puli return; 11617f4eb588SAppaRao Puli } 11627f4eb588SAppaRao Puli 11637f4eb588SAppaRao Puli static std::array<char, 1024> readBuffer; 11647f4eb588SAppaRao Puli 1165bd79bce8SPatrick Williams inotifyConn->async_read_some( 1166bd79bce8SPatrick Williams boost::asio::buffer(readBuffer), 11677f4eb588SAppaRao Puli [&](const boost::system::error_code& ec, 11687f4eb588SAppaRao Puli const std::size_t& bytesTransferred) { 11699ed3f90aSEd Tanous if (ec == boost::asio::error::operation_aborted) 11709ed3f90aSEd Tanous { 11719ed3f90aSEd Tanous BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)"); 11729ed3f90aSEd Tanous return; 11739ed3f90aSEd Tanous } 11747f4eb588SAppaRao Puli if (ec) 11757f4eb588SAppaRao Puli { 117662598e31SEd Tanous BMCWEB_LOG_ERROR("Callback Error: {}", ec.message()); 11777f4eb588SAppaRao Puli return; 11787f4eb588SAppaRao Puli } 117903d4d37cSAlexander Hansen 118003d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred); 118103d4d37cSAlexander Hansen 11827f4eb588SAppaRao Puli std::size_t index = 0; 1183b792cc56SAppaRao Puli while ((index + iEventSize) <= bytesTransferred) 11847f4eb588SAppaRao Puli { 1185d3a9e084SEd Tanous struct inotify_event event 1186d3a9e084SEd Tanous {}; 1187b792cc56SAppaRao Puli std::memcpy(&event, &readBuffer[index], iEventSize); 1188b792cc56SAppaRao Puli if (event.wd == dirWatchDesc) 1189b792cc56SAppaRao Puli { 1190b792cc56SAppaRao Puli if ((event.len == 0) || 1191b792cc56SAppaRao Puli (index + iEventSize + event.len > bytesTransferred)) 1192b792cc56SAppaRao Puli { 1193b792cc56SAppaRao Puli index += (iEventSize + event.len); 1194b792cc56SAppaRao Puli continue; 1195b792cc56SAppaRao Puli } 1196b792cc56SAppaRao Puli 11974f568f74SJiaqing Zhao std::string fileName(&readBuffer[index + iEventSize]); 11984f568f74SJiaqing Zhao if (fileName != "redfish") 1199b792cc56SAppaRao Puli { 1200b792cc56SAppaRao Puli index += (iEventSize + event.len); 1201b792cc56SAppaRao Puli continue; 1202b792cc56SAppaRao Puli } 1203b792cc56SAppaRao Puli 120462598e31SEd Tanous BMCWEB_LOG_DEBUG( 120562598e31SEd Tanous "Redfish log file created/deleted. event.name: {}", 120662598e31SEd Tanous fileName); 1207b792cc56SAppaRao Puli if (event.mask == IN_CREATE) 1208b792cc56SAppaRao Puli { 1209b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1210b792cc56SAppaRao Puli { 121162598e31SEd Tanous BMCWEB_LOG_DEBUG( 121262598e31SEd Tanous "Remove and Add inotify watcher on " 121362598e31SEd Tanous "redfish event log file"); 1214016761afSAppaRao Puli // Remove existing inotify watcher and add 1215016761afSAppaRao Puli // with new redfish event log file. 1216016761afSAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1217016761afSAppaRao Puli fileWatchDesc = -1; 1218b792cc56SAppaRao Puli } 1219b792cc56SAppaRao Puli 1220b792cc56SAppaRao Puli fileWatchDesc = inotify_add_watch( 1221b792cc56SAppaRao Puli inotifyFd, redfishEventLogFile, IN_MODIFY); 1222b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1223b792cc56SAppaRao Puli { 122462598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for " 122562598e31SEd Tanous "redfish log file."); 1226b792cc56SAppaRao Puli return; 1227b792cc56SAppaRao Puli } 1228b792cc56SAppaRao Puli 1229b792cc56SAppaRao Puli EventServiceManager::getInstance() 12302558979cSP Dheeraj Srujan Kumar .resetRedfishFilePosition(); 1231b792cc56SAppaRao Puli EventServiceManager::getInstance() 1232b792cc56SAppaRao Puli .readEventLogsFromFile(); 1233b792cc56SAppaRao Puli } 1234b792cc56SAppaRao Puli else if ((event.mask == IN_DELETE) || 1235b792cc56SAppaRao Puli (event.mask == IN_MOVED_TO)) 1236b792cc56SAppaRao Puli { 1237b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1238b792cc56SAppaRao Puli { 1239b792cc56SAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1240b792cc56SAppaRao Puli fileWatchDesc = -1; 1241b792cc56SAppaRao Puli } 1242b792cc56SAppaRao Puli } 1243b792cc56SAppaRao Puli } 1244b792cc56SAppaRao Puli else if (event.wd == fileWatchDesc) 1245b792cc56SAppaRao Puli { 1246b792cc56SAppaRao Puli if (event.mask == IN_MODIFY) 12477f4eb588SAppaRao Puli { 12487f4eb588SAppaRao Puli EventServiceManager::getInstance() 12497f4eb588SAppaRao Puli .readEventLogsFromFile(); 12507f4eb588SAppaRao Puli } 1251b792cc56SAppaRao Puli } 1252b792cc56SAppaRao Puli index += (iEventSize + event.len); 12537f4eb588SAppaRao Puli } 12547f4eb588SAppaRao Puli 12557f4eb588SAppaRao Puli watchRedfishEventLogFile(); 12567f4eb588SAppaRao Puli }); 12577f4eb588SAppaRao Puli } 12587f4eb588SAppaRao Puli 12597f4eb588SAppaRao Puli static int startEventLogMonitor(boost::asio::io_context& ioc) 12607f4eb588SAppaRao Puli { 126103d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 126203d4d37cSAlexander Hansen 126323a21a1cSEd Tanous inotifyConn.emplace(ioc); 1264b792cc56SAppaRao Puli inotifyFd = inotify_init1(IN_NONBLOCK); 1265b792cc56SAppaRao Puli if (inotifyFd == -1) 12667f4eb588SAppaRao Puli { 126762598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_init1 failed."); 12687f4eb588SAppaRao Puli return -1; 12697f4eb588SAppaRao Puli } 1270b792cc56SAppaRao Puli 1271b792cc56SAppaRao Puli // Add watch on directory to handle redfish event log file 1272b792cc56SAppaRao Puli // create/delete. 1273b792cc56SAppaRao Puli dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 1274b792cc56SAppaRao Puli IN_CREATE | IN_MOVED_TO | IN_DELETE); 1275b792cc56SAppaRao Puli if (dirWatchDesc == -1) 12767f4eb588SAppaRao Puli { 127762598e31SEd Tanous BMCWEB_LOG_ERROR( 127862598e31SEd Tanous "inotify_add_watch failed for event log directory."); 12797f4eb588SAppaRao Puli return -1; 12807f4eb588SAppaRao Puli } 12817f4eb588SAppaRao Puli 1282b792cc56SAppaRao Puli // Watch redfish event log file for modifications. 1283bd79bce8SPatrick Williams fileWatchDesc = 1284bd79bce8SPatrick Williams inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 1285b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1286b792cc56SAppaRao Puli { 128762598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 1288b792cc56SAppaRao Puli // Don't return error if file not exist. 1289b792cc56SAppaRao Puli // Watch on directory will handle create/delete of file. 1290b792cc56SAppaRao Puli } 1291b792cc56SAppaRao Puli 12927f4eb588SAppaRao Puli // monitor redfish event log file 1293b792cc56SAppaRao Puli inotifyConn->assign(inotifyFd); 12947f4eb588SAppaRao Puli watchRedfishEventLogFile(); 12957f4eb588SAppaRao Puli 12967f4eb588SAppaRao Puli return 0; 12977f4eb588SAppaRao Puli } 12987f4eb588SAppaRao Puli 12999ed3f90aSEd Tanous static void stopEventLogMonitor() 13009ed3f90aSEd Tanous { 13019ed3f90aSEd Tanous inotifyConn.reset(); 13029ed3f90aSEd Tanous } 13039ed3f90aSEd Tanous 130459d494eeSPatrick Williams static void getReadingsForReport(sdbusplus::message_t& msg) 1305156d6b00SAppaRao Puli { 130656d2396dSEd Tanous if (msg.is_method_error()) 130756d2396dSEd Tanous { 130862598e31SEd Tanous BMCWEB_LOG_ERROR("TelemetryMonitor Signal error"); 130956d2396dSEd Tanous return; 131056d2396dSEd Tanous } 131156d2396dSEd Tanous 1312c0353249SWludzik, Jozef sdbusplus::message::object_path path(msg.get_path()); 1313c0353249SWludzik, Jozef std::string id = path.filename(); 1314c0353249SWludzik, Jozef if (id.empty()) 1315156d6b00SAppaRao Puli { 131662598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to get Id from path"); 1317156d6b00SAppaRao Puli return; 1318156d6b00SAppaRao Puli } 1319156d6b00SAppaRao Puli 1320c0353249SWludzik, Jozef std::string interface; 1321b9d36b47SEd Tanous dbus::utility::DBusPropertiesMap props; 1322c0353249SWludzik, Jozef std::vector<std::string> invalidProps; 1323c0353249SWludzik, Jozef msg.read(interface, props, invalidProps); 1324c0353249SWludzik, Jozef 1325bd79bce8SPatrick Williams auto found = std::ranges::find_if(props, [](const auto& x) { 1326bd79bce8SPatrick Williams return x.first == "Readings"; 1327bd79bce8SPatrick Williams }); 1328c0353249SWludzik, Jozef if (found == props.end()) 1329156d6b00SAppaRao Puli { 133062598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 1331156d6b00SAppaRao Puli return; 1332156d6b00SAppaRao Puli } 1333156d6b00SAppaRao Puli 13341e1e598dSJonathan Doman const telemetry::TimestampReadings* readings = 13351e1e598dSJonathan Doman std::get_if<telemetry::TimestampReadings>(&found->second); 1336e662eae8SEd Tanous if (readings == nullptr) 13371e1e598dSJonathan Doman { 133862598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 13391e1e598dSJonathan Doman return; 13401e1e598dSJonathan Doman } 13411e1e598dSJonathan Doman 1342156d6b00SAppaRao Puli for (const auto& it : 1343156d6b00SAppaRao Puli EventServiceManager::getInstance().subscriptionsMap) 1344156d6b00SAppaRao Puli { 1345e05aec50SEd Tanous Subscription& entry = *it.second; 13464b712a29SEd Tanous if (entry.userSub.eventFormatType == metricReportFormatType) 1347156d6b00SAppaRao Puli { 13481e1e598dSJonathan Doman entry.filterAndSendReports(id, *readings); 1349156d6b00SAppaRao Puli } 1350156d6b00SAppaRao Puli } 1351156d6b00SAppaRao Puli } 1352156d6b00SAppaRao Puli 1353156d6b00SAppaRao Puli void unregisterMetricReportSignal() 1354156d6b00SAppaRao Puli { 13557d1cc387SAppaRao Puli if (matchTelemetryMonitor) 13567d1cc387SAppaRao Puli { 135762598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Unregister"); 1358156d6b00SAppaRao Puli matchTelemetryMonitor.reset(); 1359156d6b00SAppaRao Puli matchTelemetryMonitor = nullptr; 1360156d6b00SAppaRao Puli } 13617d1cc387SAppaRao Puli } 1362156d6b00SAppaRao Puli 1363156d6b00SAppaRao Puli void registerMetricReportSignal() 1364156d6b00SAppaRao Puli { 13657d1cc387SAppaRao Puli if (!serviceEnabled || matchTelemetryMonitor) 1366156d6b00SAppaRao Puli { 136762598e31SEd Tanous BMCWEB_LOG_DEBUG("Not registering metric report signal."); 1368156d6b00SAppaRao Puli return; 1369156d6b00SAppaRao Puli } 1370156d6b00SAppaRao Puli 137162598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Register"); 1372c0353249SWludzik, Jozef std::string matchStr = "type='signal',member='PropertiesChanged'," 1373c0353249SWludzik, Jozef "interface='org.freedesktop.DBus.Properties'," 1374c0353249SWludzik, Jozef "arg0=xyz.openbmc_project.Telemetry.Report"; 1375156d6b00SAppaRao Puli 137659d494eeSPatrick Williams matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>( 137756d2396dSEd Tanous *crow::connections::systemBus, matchStr, getReadingsForReport); 1378156d6b00SAppaRao Puli } 137923a21a1cSEd Tanous }; 1380b52664e2SAppaRao Puli 1381b52664e2SAppaRao Puli } // namespace redfish 1382