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> 54*5fe4ef35SMyung Bae #include <utility> 55b52664e2SAppaRao Puli 56b52664e2SAppaRao Puli namespace redfish 57b52664e2SAppaRao Puli { 58156d6b00SAppaRao Puli 59156d6b00SAppaRao Puli static constexpr const char* eventFormatType = "Event"; 60156d6b00SAppaRao Puli static constexpr const char* metricReportFormatType = "MetricReport"; 61156d6b00SAppaRao Puli 625e44e3d8SAppaRao Puli static constexpr const char* subscriptionTypeSSE = "SSE"; 631bf712bcSAyushi Smriti static constexpr const char* eventServiceFile = 641bf712bcSAyushi Smriti "/var/lib/bmcweb/eventservice_config.json"; 651bf712bcSAyushi Smriti 665e44e3d8SAppaRao Puli static constexpr const uint8_t maxNoOfSubscriptions = 20; 675e44e3d8SAppaRao Puli static constexpr const uint8_t maxNoOfSSESubscriptions = 10; 685e44e3d8SAppaRao Puli 69cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 704642bf8fSGeorge Liu static std::optional<boost::asio::posix::stream_descriptor> inotifyConn; 714642bf8fSGeorge Liu static constexpr const char* redfishEventLogDir = "/var/log"; 724642bf8fSGeorge Liu static constexpr const char* redfishEventLogFile = "/var/log/redfish"; 734642bf8fSGeorge Liu static constexpr const size_t iEventSize = sizeof(inotify_event); 74cf9e417dSEd Tanous 75cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 764642bf8fSGeorge Liu static int inotifyFd = -1; 77cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 784642bf8fSGeorge Liu static int dirWatchDesc = -1; 79cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 804642bf8fSGeorge Liu static int fileWatchDesc = -1; 81f80a87f2SEd Tanous struct EventLogObjectsType 82f80a87f2SEd Tanous { 83f80a87f2SEd Tanous std::string id; 84f80a87f2SEd Tanous std::string timestamp; 85f80a87f2SEd Tanous std::string messageId; 86f80a87f2SEd Tanous std::vector<std::string> messageArgs; 87f80a87f2SEd Tanous }; 884642bf8fSGeorge Liu 89fffb8c1fSEd Tanous namespace registries 904642bf8fSGeorge Liu { 917f4eb588SAppaRao Puli static const Message* 927f4eb588SAppaRao Puli getMsgFromRegistry(const std::string& messageKey, 9326702d01SEd Tanous const std::span<const MessageEntry>& registry) 947f4eb588SAppaRao Puli { 953544d2a7SEd Tanous std::span<const MessageEntry>::iterator messageIt = std::ranges::find_if( 963544d2a7SEd Tanous registry, [&messageKey](const MessageEntry& messageEntry) { 9755f79e6fSEd Tanous return messageKey == messageEntry.first; 987f4eb588SAppaRao Puli }); 9926702d01SEd Tanous if (messageIt != registry.end()) 1007f4eb588SAppaRao Puli { 1017f4eb588SAppaRao Puli return &messageIt->second; 1027f4eb588SAppaRao Puli } 1037f4eb588SAppaRao Puli 1047f4eb588SAppaRao Puli return nullptr; 1057f4eb588SAppaRao Puli } 1067f4eb588SAppaRao Puli 10726ccae32SEd Tanous static const Message* formatMessage(std::string_view messageID) 1087f4eb588SAppaRao Puli { 1097f4eb588SAppaRao Puli // Redfish MessageIds are in the form 1107f4eb588SAppaRao Puli // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 1117f4eb588SAppaRao Puli // the right Message 1127f4eb588SAppaRao Puli std::vector<std::string> fields; 1137f4eb588SAppaRao Puli fields.reserve(4); 11450ebd4afSEd Tanous 11550ebd4afSEd Tanous bmcweb::split(fields, messageID, '.'); 1167f4eb588SAppaRao Puli if (fields.size() != 4) 1177f4eb588SAppaRao Puli { 1187f4eb588SAppaRao Puli return nullptr; 1197f4eb588SAppaRao Puli } 12002cad96eSEd Tanous const std::string& registryName = fields[0]; 12102cad96eSEd Tanous const std::string& messageKey = fields[3]; 1227f4eb588SAppaRao Puli 1237f4eb588SAppaRao Puli // Find the right registry and check it for the MessageKey 124b304bd79SP Dheeraj Srujan Kumar return getMsgFromRegistry(messageKey, getRegistryFromPrefix(registryName)); 1257f4eb588SAppaRao Puli } 126fffb8c1fSEd Tanous } // namespace registries 1277f4eb588SAppaRao Puli 1287f4eb588SAppaRao Puli namespace event_log 1297f4eb588SAppaRao Puli { 1302558979cSP Dheeraj Srujan Kumar inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) 1317f4eb588SAppaRao Puli { 1327f4eb588SAppaRao Puli static time_t prevTs = 0; 1337f4eb588SAppaRao Puli static int index = 0; 1347f4eb588SAppaRao Puli 1357f4eb588SAppaRao Puli // Get the entry timestamp 1367f4eb588SAppaRao Puli std::time_t curTs = 0; 1377f4eb588SAppaRao Puli std::tm timeStruct = {}; 1387f4eb588SAppaRao Puli std::istringstream entryStream(logEntry); 1397f4eb588SAppaRao Puli if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 1407f4eb588SAppaRao Puli { 1417f4eb588SAppaRao Puli curTs = std::mktime(&timeStruct); 1427f4eb588SAppaRao Puli if (curTs == -1) 1437f4eb588SAppaRao Puli { 1447f4eb588SAppaRao Puli return false; 1457f4eb588SAppaRao Puli } 1467f4eb588SAppaRao Puli } 1477f4eb588SAppaRao Puli // If the timestamp isn't unique, increment the index 1487f4eb588SAppaRao Puli index = (curTs == prevTs) ? index + 1 : 0; 1497f4eb588SAppaRao Puli 1507f4eb588SAppaRao Puli // Save the timestamp 1517f4eb588SAppaRao Puli prevTs = curTs; 1527f4eb588SAppaRao Puli 1537f4eb588SAppaRao Puli entryID = std::to_string(curTs); 1547f4eb588SAppaRao Puli if (index > 0) 1557f4eb588SAppaRao Puli { 1567f4eb588SAppaRao Puli entryID += "_" + std::to_string(index); 1577f4eb588SAppaRao Puli } 1587f4eb588SAppaRao Puli return true; 1597f4eb588SAppaRao Puli } 1607f4eb588SAppaRao Puli 16123a21a1cSEd Tanous inline int getEventLogParams(const std::string& logEntry, 16223a21a1cSEd Tanous std::string& timestamp, std::string& messageID, 1635e715de6SAppaRao Puli std::vector<std::string>& messageArgs) 1647f4eb588SAppaRao Puli { 1657f4eb588SAppaRao Puli // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 1667f4eb588SAppaRao Puli // First get the Timestamp 167f23b7296SEd Tanous size_t space = logEntry.find_first_of(' '); 1687f4eb588SAppaRao Puli if (space == std::string::npos) 1697f4eb588SAppaRao Puli { 17003d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", 17103d4d37cSAlexander Hansen logEntry); 1727f4eb588SAppaRao Puli return -EINVAL; 1737f4eb588SAppaRao Puli } 1747f4eb588SAppaRao Puli timestamp = logEntry.substr(0, space); 1757f4eb588SAppaRao Puli // Then get the log contents 176f23b7296SEd Tanous size_t entryStart = logEntry.find_first_not_of(' ', space); 1777f4eb588SAppaRao Puli if (entryStart == std::string::npos) 1787f4eb588SAppaRao Puli { 17903d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", 18003d4d37cSAlexander Hansen logEntry); 1817f4eb588SAppaRao Puli return -EINVAL; 1827f4eb588SAppaRao Puli } 1837f4eb588SAppaRao Puli std::string_view entry(logEntry); 1847f4eb588SAppaRao Puli entry.remove_prefix(entryStart); 1857f4eb588SAppaRao Puli // Use split to separate the entry into its fields 1867f4eb588SAppaRao Puli std::vector<std::string> logEntryFields; 18750ebd4afSEd Tanous bmcweb::split(logEntryFields, entry, ','); 1887f4eb588SAppaRao Puli // We need at least a MessageId to be valid 18926f6976fSEd Tanous if (logEntryFields.empty()) 1907f4eb588SAppaRao Puli { 19103d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", 19203d4d37cSAlexander Hansen logEntry); 1937f4eb588SAppaRao Puli return -EINVAL; 1947f4eb588SAppaRao Puli } 1957f4eb588SAppaRao Puli messageID = logEntryFields[0]; 1967f4eb588SAppaRao Puli 1977f4eb588SAppaRao Puli // Get the MessageArgs from the log if there are any 1987f4eb588SAppaRao Puli if (logEntryFields.size() > 1) 1997f4eb588SAppaRao Puli { 20002cad96eSEd Tanous const std::string& messageArgsStart = logEntryFields[1]; 2017f4eb588SAppaRao Puli // If the first string is empty, assume there are no MessageArgs 2027f4eb588SAppaRao Puli if (!messageArgsStart.empty()) 2037f4eb588SAppaRao Puli { 2045e715de6SAppaRao Puli messageArgs.assign(logEntryFields.begin() + 1, 2055e715de6SAppaRao Puli logEntryFields.end()); 2067f4eb588SAppaRao Puli } 2077f4eb588SAppaRao Puli } 2087f4eb588SAppaRao Puli 2097f4eb588SAppaRao Puli return 0; 2107f4eb588SAppaRao Puli } 2117f4eb588SAppaRao Puli 212bd79bce8SPatrick Williams inline int formatEventLogEntry( 213bd79bce8SPatrick Williams const std::string& logEntryID, const std::string& messageID, 214bd79bce8SPatrick Williams const std::span<std::string_view> messageArgs, std::string timestamp, 215bd79bce8SPatrick Williams const std::string& customText, nlohmann::json::object_t& logEntryJson) 2167f4eb588SAppaRao Puli { 2177f4eb588SAppaRao Puli // Get the Message from the MessageRegistry 218fffb8c1fSEd Tanous const registries::Message* message = registries::formatMessage(messageID); 2197f4eb588SAppaRao Puli 22080f595e7SEd Tanous if (message == nullptr) 2217f4eb588SAppaRao Puli { 22280f595e7SEd Tanous return -1; 2237f4eb588SAppaRao Puli } 2247f4eb588SAppaRao Puli 225bd79bce8SPatrick Williams std::string msg = 226bd79bce8SPatrick Williams redfish::registries::fillMessageArgs(messageArgs, message->message); 22780f595e7SEd Tanous if (msg.empty()) 22880f595e7SEd Tanous { 22980f595e7SEd Tanous return -1; 23080f595e7SEd Tanous } 2317f4eb588SAppaRao Puli 2327f4eb588SAppaRao Puli // Get the Created time from the timestamp. The log timestamp is in 2337f4eb588SAppaRao Puli // RFC3339 format which matches the Redfish format except for the 2347f4eb588SAppaRao Puli // fractional seconds between the '.' and the '+', so just remove them. 235f23b7296SEd Tanous std::size_t dot = timestamp.find_first_of('.'); 236b2f7609bSEd Tanous std::size_t plus = timestamp.find_first_of('+', dot); 2377f4eb588SAppaRao Puli if (dot != std::string::npos && plus != std::string::npos) 2387f4eb588SAppaRao Puli { 2397f4eb588SAppaRao Puli timestamp.erase(dot, plus - dot); 2407f4eb588SAppaRao Puli } 2417f4eb588SAppaRao Puli 2427f4eb588SAppaRao Puli // Fill in the log entry with the gathered data 2431476687dSEd Tanous logEntryJson["EventId"] = logEntryID; 244539d8c6bSEd Tanous 24580f595e7SEd Tanous logEntryJson["Severity"] = message->messageSeverity; 2461476687dSEd Tanous logEntryJson["Message"] = std::move(msg); 2471476687dSEd Tanous logEntryJson["MessageId"] = messageID; 2481476687dSEd Tanous logEntryJson["MessageArgs"] = messageArgs; 2491476687dSEd Tanous logEntryJson["EventTimestamp"] = std::move(timestamp); 2501476687dSEd Tanous logEntryJson["Context"] = customText; 2517f4eb588SAppaRao Puli return 0; 2527f4eb588SAppaRao Puli } 2537f4eb588SAppaRao Puli 2547f4eb588SAppaRao Puli } // namespace event_log 2557f4eb588SAppaRao Puli 256a0969c70SMyung Bae class Subscription : public std::enable_shared_from_this<Subscription> 257b52664e2SAppaRao Puli { 258b52664e2SAppaRao Puli public: 259b52664e2SAppaRao Puli Subscription(const Subscription&) = delete; 260b52664e2SAppaRao Puli Subscription& operator=(const Subscription&) = delete; 261b52664e2SAppaRao Puli Subscription(Subscription&&) = delete; 262b52664e2SAppaRao Puli Subscription& operator=(Subscription&&) = delete; 263b52664e2SAppaRao Puli 264*5fe4ef35SMyung Bae Subscription(std::shared_ptr<persistent_data::UserSubscription> userSubIn, 26521a94d5cSMyung Bae const boost::urls::url_view_base& url, 2664a7fbefdSEd Tanous boost::asio::io_context& ioc) : 267*5fe4ef35SMyung Bae userSub{std::move(userSubIn)}, 268*5fe4ef35SMyung Bae policy(std::make_shared<crow::ConnectionPolicy>()) 269b52664e2SAppaRao Puli { 270*5fe4ef35SMyung Bae userSub->destinationUrl = url; 2715e44e3d8SAppaRao Puli client.emplace(ioc, policy); 2727adb85acSSunitha Harish // Subscription constructor 273d14a48ffSCarson Labrado policy->invalidResp = retryRespHandler; 274b52664e2SAppaRao Puli } 2754bbf237fSAppaRao Puli 2765e44e3d8SAppaRao Puli explicit Subscription(crow::sse_socket::Connection& connIn) : 277*5fe4ef35SMyung Bae userSub{std::make_shared<persistent_data::UserSubscription>()}, 2785e44e3d8SAppaRao Puli sseConn(&connIn) 2795e44e3d8SAppaRao Puli {} 2805e44e3d8SAppaRao Puli 2819f616dd1SEd Tanous ~Subscription() = default; 282b52664e2SAppaRao Puli 283a0969c70SMyung Bae // callback for subscription sendData 284a0969c70SMyung Bae void resHandler(const std::shared_ptr<Subscription>& /*unused*/, 285a0969c70SMyung Bae const crow::Response& res) 286a0969c70SMyung Bae { 287a0969c70SMyung Bae BMCWEB_LOG_DEBUG("Response handled with return code: {}", 288a0969c70SMyung Bae res.resultInt()); 289a0969c70SMyung Bae 290a0969c70SMyung Bae if (!client) 291a0969c70SMyung Bae { 292a0969c70SMyung Bae BMCWEB_LOG_ERROR( 293a0969c70SMyung Bae "Http client wasn't filled but http client callback was called."); 294a0969c70SMyung Bae return; 295a0969c70SMyung Bae } 296a0969c70SMyung Bae 297*5fe4ef35SMyung Bae if (userSub->retryPolicy != "TerminateAfterRetries") 298a0969c70SMyung Bae { 299a0969c70SMyung Bae return; 300a0969c70SMyung Bae } 301a0969c70SMyung Bae if (client->isTerminated()) 302a0969c70SMyung Bae { 303a0969c70SMyung Bae if (deleter) 304a0969c70SMyung Bae { 305a0969c70SMyung Bae BMCWEB_LOG_INFO( 306a0969c70SMyung Bae "Subscription {} is deleted after MaxRetryAttempts", 307*5fe4ef35SMyung Bae userSub->id); 308a0969c70SMyung Bae deleter(); 309a0969c70SMyung Bae } 310a0969c70SMyung Bae } 311a0969c70SMyung Bae } 312a0969c70SMyung Bae 3136d799e14SEd Tanous bool sendEventToSubscriber(std::string&& msg) 314b52664e2SAppaRao Puli { 3156ba8c82eSsunharis_in persistent_data::EventServiceConfig eventServiceConfig = 3166ba8c82eSsunharis_in persistent_data::EventServiceStore::getInstance() 3176ba8c82eSsunharis_in .getEventServiceConfig(); 3186ba8c82eSsunharis_in if (!eventServiceConfig.enabled) 3196ba8c82eSsunharis_in { 3206ba8c82eSsunharis_in return false; 3216ba8c82eSsunharis_in } 3226ba8c82eSsunharis_in 3235e44e3d8SAppaRao Puli if (client) 3245e44e3d8SAppaRao Puli { 325a0969c70SMyung Bae client->sendDataWithCallback( 326*5fe4ef35SMyung Bae std::move(msg), userSub->destinationUrl, 3274b712a29SEd Tanous static_cast<ensuressl::VerifyCertificate>( 328*5fe4ef35SMyung Bae userSub->verifyCertificate), 329*5fe4ef35SMyung Bae userSub->httpHeaders, boost::beast::http::verb::post, 330a0969c70SMyung Bae std::bind_front(&Subscription::resHandler, this, 331a0969c70SMyung Bae shared_from_this())); 3325e44e3d8SAppaRao Puli return true; 3335e44e3d8SAppaRao Puli } 3347adb85acSSunitha Harish 3354bbf237fSAppaRao Puli if (sseConn != nullptr) 3364bbf237fSAppaRao Puli { 3375e44e3d8SAppaRao Puli eventSeqNum++; 3386d799e14SEd Tanous sseConn->sendSseEvent(std::to_string(eventSeqNum), msg); 3394bbf237fSAppaRao Puli } 3406ba8c82eSsunharis_in return true; 3414bbf237fSAppaRao Puli } 3424bbf237fSAppaRao Puli 3436ba8c82eSsunharis_in bool sendTestEventLog() 3440b4bdd93SAppaRao Puli { 345f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 346f80a87f2SEd Tanous nlohmann::json& logEntryJson = logEntryArray.emplace_back(); 3470b4bdd93SAppaRao Puli 348613dabeaSEd Tanous logEntryJson["EventId"] = "TestID"; 349539d8c6bSEd Tanous logEntryJson["Severity"] = log_entry::EventSeverity::OK; 350613dabeaSEd Tanous logEntryJson["Message"] = "Generated test event"; 351613dabeaSEd Tanous logEntryJson["MessageId"] = "OpenBMC.0.2.TestEventLog"; 352d2cdd478SChandra Harkude // MemberId is 0 : since we are sending one event record. 353788b091bSIgor Kanyuka logEntryJson["MemberId"] = "0"; 354613dabeaSEd Tanous logEntryJson["MessageArgs"] = nlohmann::json::array(); 355613dabeaSEd Tanous logEntryJson["EventTimestamp"] = 356613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 357*5fe4ef35SMyung Bae logEntryJson["Context"] = userSub->customText; 3580b4bdd93SAppaRao Puli 3591476687dSEd Tanous nlohmann::json msg; 3601476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 3611476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 3621476687dSEd Tanous msg["Name"] = "Event Log"; 3631476687dSEd Tanous msg["Events"] = logEntryArray; 3640b4bdd93SAppaRao Puli 365bd79bce8SPatrick Williams std::string strMsg = 366bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 3676d799e14SEd Tanous return sendEventToSubscriber(std::move(strMsg)); 3680b4bdd93SAppaRao Puli } 3690b4bdd93SAppaRao Puli 3707f4eb588SAppaRao Puli void filterAndSendEventLogs( 3717f4eb588SAppaRao Puli const std::vector<EventLogObjectsType>& eventRecords) 3727f4eb588SAppaRao Puli { 373f80a87f2SEd Tanous nlohmann::json::array_t logEntryArray; 3747f4eb588SAppaRao Puli for (const EventLogObjectsType& logEntry : eventRecords) 3757f4eb588SAppaRao Puli { 376f80a87f2SEd Tanous std::vector<std::string_view> messageArgsView( 377f80a87f2SEd Tanous logEntry.messageArgs.begin(), logEntry.messageArgs.end()); 3787f4eb588SAppaRao Puli 379f80a87f2SEd Tanous nlohmann::json::object_t bmcLogEntry; 380f80a87f2SEd Tanous if (event_log::formatEventLogEntry( 381f80a87f2SEd Tanous logEntry.id, logEntry.messageId, messageArgsView, 382*5fe4ef35SMyung Bae logEntry.timestamp, userSub->customText, bmcLogEntry) != 0) 3837f4eb588SAppaRao Puli { 38462598e31SEd Tanous BMCWEB_LOG_DEBUG("Read eventLog entry failed"); 3857f4eb588SAppaRao Puli continue; 3867f4eb588SAppaRao Puli } 387f80a87f2SEd Tanous 388*5fe4ef35SMyung Bae if (!eventMatchesFilter(*userSub, bmcLogEntry, "")) 389f80a87f2SEd Tanous { 39003d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Event {} did not match the filter", 39103d4d37cSAlexander Hansen nlohmann::json(bmcLogEntry).dump()); 392f80a87f2SEd Tanous continue; 393f80a87f2SEd Tanous } 394f80a87f2SEd Tanous 395d3a48a14SEd Tanous if (filter) 396d3a48a14SEd Tanous { 397d3a48a14SEd Tanous if (!memberMatches(bmcLogEntry, *filter)) 398d3a48a14SEd Tanous { 399d3a48a14SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 400d3a48a14SEd Tanous continue; 401d3a48a14SEd Tanous } 402d3a48a14SEd Tanous } 403d3a48a14SEd Tanous 404f80a87f2SEd Tanous logEntryArray.emplace_back(std::move(bmcLogEntry)); 4057f4eb588SAppaRao Puli } 4067f4eb588SAppaRao Puli 40726f6976fSEd Tanous if (logEntryArray.empty()) 4087f4eb588SAppaRao Puli { 40962598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 4107f4eb588SAppaRao Puli return; 4117f4eb588SAppaRao Puli } 4127f4eb588SAppaRao Puli 4131476687dSEd Tanous nlohmann::json msg; 4141476687dSEd Tanous msg["@odata.type"] = "#Event.v1_4_0.Event"; 4151476687dSEd Tanous msg["Id"] = std::to_string(eventSeqNum); 4161476687dSEd Tanous msg["Name"] = "Event Log"; 417f80a87f2SEd Tanous msg["Events"] = std::move(logEntryArray); 418bd79bce8SPatrick Williams std::string strMsg = 419bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 4206d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 4215e44e3d8SAppaRao Puli eventSeqNum++; 4227f4eb588SAppaRao Puli } 4237f4eb588SAppaRao Puli 424248d0230SEd Tanous void filterAndSendReports(const std::string& reportId, 4251e1e598dSJonathan Doman const telemetry::TimestampReadings& var) 426156d6b00SAppaRao Puli { 427ef4c65b7SEd Tanous boost::urls::url mrdUri = boost::urls::format( 428ef4c65b7SEd Tanous "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", 429ef4c65b7SEd Tanous reportId); 430156d6b00SAppaRao Puli 431156d6b00SAppaRao Puli // Empty list means no filter. Send everything. 432*5fe4ef35SMyung Bae if (!userSub->metricReportDefinitions.empty()) 433156d6b00SAppaRao Puli { 434*5fe4ef35SMyung Bae if (std::ranges::find(userSub->metricReportDefinitions, 4354b712a29SEd Tanous mrdUri.buffer()) == 436*5fe4ef35SMyung Bae userSub->metricReportDefinitions.end()) 437156d6b00SAppaRao Puli { 438156d6b00SAppaRao Puli return; 439156d6b00SAppaRao Puli } 440156d6b00SAppaRao Puli } 441156d6b00SAppaRao Puli 442c0353249SWludzik, Jozef nlohmann::json msg; 443248d0230SEd Tanous if (!telemetry::fillReport(msg, reportId, var)) 444156d6b00SAppaRao Puli { 44562598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to fill the MetricReport for DBus " 44662598e31SEd Tanous "Report with id {}", 44762598e31SEd Tanous reportId); 448c0353249SWludzik, Jozef return; 449156d6b00SAppaRao Puli } 450156d6b00SAppaRao Puli 45122daffd7SAppaRao Puli // Context is set by user during Event subscription and it must be 45222daffd7SAppaRao Puli // set for MetricReport response. 453*5fe4ef35SMyung Bae if (!userSub->customText.empty()) 45422daffd7SAppaRao Puli { 455*5fe4ef35SMyung Bae msg["Context"] = userSub->customText; 45622daffd7SAppaRao Puli } 45722daffd7SAppaRao Puli 458bd79bce8SPatrick Williams std::string strMsg = 459bd79bce8SPatrick Williams msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 4606d799e14SEd Tanous sendEventToSubscriber(std::move(strMsg)); 461156d6b00SAppaRao Puli } 462156d6b00SAppaRao Puli 463d14a48ffSCarson Labrado void updateRetryConfig(uint32_t retryAttempts, 464d14a48ffSCarson Labrado uint32_t retryTimeoutInterval) 465fe44eb0bSAyushi Smriti { 46693cf0ac2SEd Tanous if (policy == nullptr) 46793cf0ac2SEd Tanous { 46893cf0ac2SEd Tanous BMCWEB_LOG_DEBUG("Retry policy was nullptr, ignoring set"); 46993cf0ac2SEd Tanous return; 47093cf0ac2SEd Tanous } 471d14a48ffSCarson Labrado policy->maxRetryAttempts = retryAttempts; 472d14a48ffSCarson Labrado policy->retryIntervalSecs = std::chrono::seconds(retryTimeoutInterval); 47362de0c68SAppaRao Puli } 474fe44eb0bSAyushi Smriti 4759eb808c1SEd Tanous uint64_t getEventSeqNum() const 47696330b99SSunitha Harish { 47796330b99SSunitha Harish return eventSeqNum; 47896330b99SSunitha Harish } 47996330b99SSunitha Harish 4805e44e3d8SAppaRao Puli bool matchSseId(const crow::sse_socket::Connection& thisConn) 4815e44e3d8SAppaRao Puli { 4825e44e3d8SAppaRao Puli return &thisConn == sseConn; 4835e44e3d8SAppaRao Puli } 4845e44e3d8SAppaRao Puli 485a7a80296SCarson Labrado // Check used to indicate what response codes are valid as part of our retry 486a7a80296SCarson Labrado // policy. 2XX is considered acceptable 487a7a80296SCarson Labrado static boost::system::error_code retryRespHandler(unsigned int respCode) 488a7a80296SCarson Labrado { 48962598e31SEd Tanous BMCWEB_LOG_DEBUG( 49062598e31SEd Tanous "Checking response code validity for SubscriptionEvent"); 491a7a80296SCarson Labrado if ((respCode < 200) || (respCode >= 300)) 492a7a80296SCarson Labrado { 493a7a80296SCarson Labrado return boost::system::errc::make_error_code( 494a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 495a7a80296SCarson Labrado } 496a7a80296SCarson Labrado 497a7a80296SCarson Labrado // Return 0 if the response code is valid 498a7a80296SCarson Labrado return boost::system::errc::make_error_code( 499a7a80296SCarson Labrado boost::system::errc::success); 5009fa6d147SNan Zhou } 501f80a87f2SEd Tanous 502*5fe4ef35SMyung Bae std::shared_ptr<persistent_data::UserSubscription> userSub; 503a0969c70SMyung Bae std::function<void()> deleter; 5044b712a29SEd Tanous 505f80a87f2SEd Tanous private: 506f80a87f2SEd Tanous uint64_t eventSeqNum = 1; 507f80a87f2SEd Tanous boost::urls::url host; 508f80a87f2SEd Tanous std::shared_ptr<crow::ConnectionPolicy> policy; 509f80a87f2SEd Tanous crow::sse_socket::Connection* sseConn = nullptr; 510f80a87f2SEd Tanous 511f80a87f2SEd Tanous std::optional<crow::HttpClient> client; 512f80a87f2SEd Tanous 513f80a87f2SEd Tanous public: 514f80a87f2SEd Tanous std::optional<filter_ast::LogicalAnd> filter; 515b52664e2SAppaRao Puli }; 516b52664e2SAppaRao Puli 517b52664e2SAppaRao Puli class EventServiceManager 518b52664e2SAppaRao Puli { 519b52664e2SAppaRao Puli private: 520d3a9e084SEd Tanous bool serviceEnabled = false; 521d3a9e084SEd Tanous uint32_t retryAttempts = 0; 522d3a9e084SEd Tanous uint32_t retryTimeoutInterval = 0; 5237d1cc387SAppaRao Puli 5242558979cSP Dheeraj Srujan Kumar std::streampos redfishLogFilePosition{0}; 5259f616dd1SEd Tanous size_t noOfEventLogSubscribers{0}; 5269f616dd1SEd Tanous size_t noOfMetricReportSubscribers{0}; 52759d494eeSPatrick Williams std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor; 528b52664e2SAppaRao Puli boost::container::flat_map<std::string, std::shared_ptr<Subscription>> 529b52664e2SAppaRao Puli subscriptionsMap; 530b52664e2SAppaRao Puli 5319f616dd1SEd Tanous uint64_t eventId{1}; 53296330b99SSunitha Harish 533f80a87f2SEd Tanous struct Event 534f80a87f2SEd Tanous { 535f80a87f2SEd Tanous std::string id; 536f80a87f2SEd Tanous nlohmann::json message; 537f80a87f2SEd Tanous }; 538f80a87f2SEd Tanous 539f80a87f2SEd Tanous constexpr static size_t maxMessages = 200; 540f80a87f2SEd Tanous boost::circular_buffer<Event> messages{maxMessages}; 541f80a87f2SEd Tanous 542f8ca6d79SEd Tanous boost::asio::io_context& ioc; 543f8ca6d79SEd Tanous 544b52664e2SAppaRao Puli public: 5459f616dd1SEd Tanous EventServiceManager(const EventServiceManager&) = delete; 5469f616dd1SEd Tanous EventServiceManager& operator=(const EventServiceManager&) = delete; 5479f616dd1SEd Tanous EventServiceManager(EventServiceManager&&) = delete; 5489f616dd1SEd Tanous EventServiceManager& operator=(EventServiceManager&&) = delete; 549ecd6a3a2SEd Tanous ~EventServiceManager() = default; 5509f616dd1SEd Tanous 551f8ca6d79SEd Tanous explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn) 552b52664e2SAppaRao Puli { 553f8ca6d79SEd Tanous // Load config from persist store. 554f8ca6d79SEd Tanous initConfig(); 555f8ca6d79SEd Tanous } 556f8ca6d79SEd Tanous 557f8ca6d79SEd Tanous static EventServiceManager& 558f8ca6d79SEd Tanous getInstance(boost::asio::io_context* ioc = nullptr) 559f8ca6d79SEd Tanous { 560f8ca6d79SEd Tanous static EventServiceManager handler(*ioc); 561b52664e2SAppaRao Puli return handler; 562b52664e2SAppaRao Puli } 563b52664e2SAppaRao Puli 5641bf712bcSAyushi Smriti void initConfig() 5651bf712bcSAyushi Smriti { 56628afb49cSJunLin Chen loadOldBehavior(); 5671bf712bcSAyushi Smriti 56828afb49cSJunLin Chen persistent_data::EventServiceConfig eventServiceConfig = 56928afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 57028afb49cSJunLin Chen .getEventServiceConfig(); 5711bf712bcSAyushi Smriti 57228afb49cSJunLin Chen serviceEnabled = eventServiceConfig.enabled; 57328afb49cSJunLin Chen retryAttempts = eventServiceConfig.retryAttempts; 57428afb49cSJunLin Chen retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval; 5751bf712bcSAyushi Smriti 57628afb49cSJunLin Chen for (const auto& it : persistent_data::EventServiceStore::getInstance() 57728afb49cSJunLin Chen .subscriptionsConfigMap) 5781bf712bcSAyushi Smriti { 579*5fe4ef35SMyung Bae std::shared_ptr<persistent_data::UserSubscription> newSub = 580*5fe4ef35SMyung Bae it.second; 5814bbf237fSAppaRao Puli 5826fd29553SEd Tanous boost::system::result<boost::urls::url> url = 583*5fe4ef35SMyung Bae boost::urls::parse_absolute_uri(newSub->destinationUrl); 5841bf712bcSAyushi Smriti 585a716aa74SEd Tanous if (!url) 5861bf712bcSAyushi Smriti { 58762598e31SEd Tanous BMCWEB_LOG_ERROR( 58862598e31SEd Tanous "Failed to validate and split destination url"); 5891bf712bcSAyushi Smriti continue; 5901bf712bcSAyushi Smriti } 5911bf712bcSAyushi Smriti std::shared_ptr<Subscription> subValue = 59221a94d5cSMyung Bae std::make_shared<Subscription>(newSub, *url, ioc); 593*5fe4ef35SMyung Bae std::string id = subValue->userSub->id; 594a0969c70SMyung Bae subValue->deleter = [id]() { 595a0969c70SMyung Bae EventServiceManager::getInstance().deleteSubscription(id); 596a0969c70SMyung Bae }; 5971bf712bcSAyushi Smriti 598a0969c70SMyung Bae subscriptionsMap.emplace(id, subValue); 59928afb49cSJunLin Chen 60028afb49cSJunLin Chen updateNoOfSubscribersCount(); 60128afb49cSJunLin Chen 60283328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 60383328316SEd Tanous { 6042558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 60583328316SEd Tanous } 6062558979cSP Dheeraj Srujan Kumar 60728afb49cSJunLin Chen // Update retry configuration. 60828afb49cSJunLin Chen subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 6091bf712bcSAyushi Smriti } 6101bf712bcSAyushi Smriti } 6111bf712bcSAyushi Smriti 61256d2396dSEd Tanous static void loadOldBehavior() 613b52664e2SAppaRao Puli { 61428afb49cSJunLin Chen std::ifstream eventConfigFile(eventServiceFile); 61528afb49cSJunLin Chen if (!eventConfigFile.good()) 6161bf712bcSAyushi Smriti { 61762598e31SEd Tanous BMCWEB_LOG_DEBUG("Old eventService config not exist"); 61828afb49cSJunLin Chen return; 61928afb49cSJunLin Chen } 62028afb49cSJunLin Chen auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false); 62128afb49cSJunLin Chen if (jsonData.is_discarded()) 6224bbf237fSAppaRao Puli { 62362598e31SEd Tanous BMCWEB_LOG_ERROR("Old eventService config parse error."); 62428afb49cSJunLin Chen return; 62528afb49cSJunLin Chen } 62628afb49cSJunLin Chen 6270bdda665SEd Tanous const nlohmann::json::object_t* obj = 6280bdda665SEd Tanous jsonData.get_ptr<const nlohmann::json::object_t*>(); 6290bdda665SEd Tanous for (const auto& item : *obj) 63028afb49cSJunLin Chen { 6310bdda665SEd Tanous if (item.first == "Configuration") 63228afb49cSJunLin Chen { 63328afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 63428afb49cSJunLin Chen .getEventServiceConfig() 6350bdda665SEd Tanous .fromJson(item.second); 63628afb49cSJunLin Chen } 6370bdda665SEd Tanous else if (item.first == "Subscriptions") 63828afb49cSJunLin Chen { 6390bdda665SEd Tanous for (const auto& elem : item.second) 64028afb49cSJunLin Chen { 6414b712a29SEd Tanous std::optional<persistent_data::UserSubscription> 64228afb49cSJunLin Chen newSubscription = 64328afb49cSJunLin Chen persistent_data::UserSubscription::fromJson(elem, 64428afb49cSJunLin Chen true); 6454b712a29SEd Tanous if (!newSubscription) 64628afb49cSJunLin Chen { 64762598e31SEd Tanous BMCWEB_LOG_ERROR("Problem reading subscription " 64862598e31SEd Tanous "from old persistent store"); 6494bbf237fSAppaRao Puli continue; 6504bbf237fSAppaRao Puli } 6514b712a29SEd Tanous persistent_data::UserSubscription& newSub = 6524b712a29SEd Tanous *newSubscription; 6531bf712bcSAyushi Smriti 65428afb49cSJunLin Chen std::uniform_int_distribution<uint32_t> dist(0); 65528afb49cSJunLin Chen bmcweb::OpenSSLGenerator gen; 6561bf712bcSAyushi Smriti 65728afb49cSJunLin Chen std::string id; 6581bf712bcSAyushi Smriti 65928afb49cSJunLin Chen int retry = 3; 660e662eae8SEd Tanous while (retry != 0) 6611bf712bcSAyushi Smriti { 66228afb49cSJunLin Chen id = std::to_string(dist(gen)); 66328afb49cSJunLin Chen if (gen.error()) 6647d1cc387SAppaRao Puli { 66528afb49cSJunLin Chen retry = 0; 66628afb49cSJunLin Chen break; 66728afb49cSJunLin Chen } 6684b712a29SEd Tanous newSub.id = id; 66928afb49cSJunLin Chen auto inserted = 67028afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 671*5fe4ef35SMyung Bae .subscriptionsConfigMap.insert(std::pair( 672*5fe4ef35SMyung Bae id, std::make_shared< 673*5fe4ef35SMyung Bae persistent_data::UserSubscription>( 674*5fe4ef35SMyung Bae newSub))); 67528afb49cSJunLin Chen if (inserted.second) 67628afb49cSJunLin Chen { 67728afb49cSJunLin Chen break; 67828afb49cSJunLin Chen } 67928afb49cSJunLin Chen --retry; 6807d1cc387SAppaRao Puli } 6817d1cc387SAppaRao Puli 68228afb49cSJunLin Chen if (retry <= 0) 68328afb49cSJunLin Chen { 68462598e31SEd Tanous BMCWEB_LOG_ERROR( 68562598e31SEd Tanous "Failed to generate random number from old " 68662598e31SEd Tanous "persistent store"); 68728afb49cSJunLin Chen continue; 68828afb49cSJunLin Chen } 68928afb49cSJunLin Chen } 69028afb49cSJunLin Chen } 69128afb49cSJunLin Chen 69228afb49cSJunLin Chen persistent_data::getConfig().writeData(); 6934c521c3cSEd Tanous std::error_code ec; 6944c521c3cSEd Tanous std::filesystem::remove(eventServiceFile, ec); 6954c521c3cSEd Tanous if (ec) 6964c521c3cSEd Tanous { 6974c521c3cSEd Tanous BMCWEB_LOG_DEBUG( 6984c521c3cSEd Tanous "Failed to remove old event service file. Ignoring"); 6994c521c3cSEd Tanous } 7004c521c3cSEd Tanous else 7014c521c3cSEd Tanous { 70262598e31SEd Tanous BMCWEB_LOG_DEBUG("Remove old eventservice config"); 70328afb49cSJunLin Chen } 70428afb49cSJunLin Chen } 7054c521c3cSEd Tanous } 70628afb49cSJunLin Chen 7079eb808c1SEd Tanous void updateSubscriptionData() const 70828afb49cSJunLin Chen { 70928afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 71028afb49cSJunLin Chen .eventServiceConfig.enabled = serviceEnabled; 71128afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 71228afb49cSJunLin Chen .eventServiceConfig.retryAttempts = retryAttempts; 71328afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 71428afb49cSJunLin Chen .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval; 71528afb49cSJunLin Chen 71628afb49cSJunLin Chen persistent_data::getConfig().writeData(); 71728afb49cSJunLin Chen } 71828afb49cSJunLin Chen 71928afb49cSJunLin Chen void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg) 7207d1cc387SAppaRao Puli { 7217d1cc387SAppaRao Puli bool updateConfig = false; 722fe44eb0bSAyushi Smriti bool updateRetryCfg = false; 7237d1cc387SAppaRao Puli 72428afb49cSJunLin Chen if (serviceEnabled != cfg.enabled) 7257d1cc387SAppaRao Puli { 72628afb49cSJunLin Chen serviceEnabled = cfg.enabled; 727e662eae8SEd Tanous if (serviceEnabled && noOfMetricReportSubscribers != 0U) 7287d1cc387SAppaRao Puli { 7297d1cc387SAppaRao Puli registerMetricReportSignal(); 7307d1cc387SAppaRao Puli } 7317d1cc387SAppaRao Puli else 7327d1cc387SAppaRao Puli { 7337d1cc387SAppaRao Puli unregisterMetricReportSignal(); 7347d1cc387SAppaRao Puli } 7357d1cc387SAppaRao Puli updateConfig = true; 7367d1cc387SAppaRao Puli } 7377d1cc387SAppaRao Puli 73828afb49cSJunLin Chen if (retryAttempts != cfg.retryAttempts) 7397d1cc387SAppaRao Puli { 74028afb49cSJunLin Chen retryAttempts = cfg.retryAttempts; 7417d1cc387SAppaRao Puli updateConfig = true; 742fe44eb0bSAyushi Smriti updateRetryCfg = true; 7437d1cc387SAppaRao Puli } 7447d1cc387SAppaRao Puli 74528afb49cSJunLin Chen if (retryTimeoutInterval != cfg.retryTimeoutInterval) 7467d1cc387SAppaRao Puli { 74728afb49cSJunLin Chen retryTimeoutInterval = cfg.retryTimeoutInterval; 7487d1cc387SAppaRao Puli updateConfig = true; 749fe44eb0bSAyushi Smriti updateRetryCfg = true; 7507d1cc387SAppaRao Puli } 7517d1cc387SAppaRao Puli 7527d1cc387SAppaRao Puli if (updateConfig) 7537d1cc387SAppaRao Puli { 7547d1cc387SAppaRao Puli updateSubscriptionData(); 7557d1cc387SAppaRao Puli } 756fe44eb0bSAyushi Smriti 757fe44eb0bSAyushi Smriti if (updateRetryCfg) 758fe44eb0bSAyushi Smriti { 759fe44eb0bSAyushi Smriti // Update the changed retry config to all subscriptions 760fe44eb0bSAyushi Smriti for (const auto& it : 761fe44eb0bSAyushi Smriti EventServiceManager::getInstance().subscriptionsMap) 762fe44eb0bSAyushi Smriti { 7635e44e3d8SAppaRao Puli Subscription& entry = *it.second; 7645e44e3d8SAppaRao Puli entry.updateRetryConfig(retryAttempts, retryTimeoutInterval); 765fe44eb0bSAyushi Smriti } 766fe44eb0bSAyushi Smriti } 7677d1cc387SAppaRao Puli } 7687d1cc387SAppaRao Puli 7697d1cc387SAppaRao Puli void updateNoOfSubscribersCount() 7707d1cc387SAppaRao Puli { 7717d1cc387SAppaRao Puli size_t eventLogSubCount = 0; 7727d1cc387SAppaRao Puli size_t metricReportSubCount = 0; 7737d1cc387SAppaRao Puli for (const auto& it : subscriptionsMap) 7747d1cc387SAppaRao Puli { 7757d1cc387SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 776*5fe4ef35SMyung Bae if (entry->userSub->eventFormatType == eventFormatType) 7777d1cc387SAppaRao Puli { 7787d1cc387SAppaRao Puli eventLogSubCount++; 7797d1cc387SAppaRao Puli } 780*5fe4ef35SMyung Bae else if (entry->userSub->eventFormatType == metricReportFormatType) 7817d1cc387SAppaRao Puli { 7827d1cc387SAppaRao Puli metricReportSubCount++; 7837d1cc387SAppaRao Puli } 7847d1cc387SAppaRao Puli } 7857d1cc387SAppaRao Puli 7867d1cc387SAppaRao Puli noOfEventLogSubscribers = eventLogSubCount; 7877d1cc387SAppaRao Puli if (noOfMetricReportSubscribers != metricReportSubCount) 7887d1cc387SAppaRao Puli { 7897d1cc387SAppaRao Puli noOfMetricReportSubscribers = metricReportSubCount; 790e662eae8SEd Tanous if (noOfMetricReportSubscribers != 0U) 7917d1cc387SAppaRao Puli { 7927d1cc387SAppaRao Puli registerMetricReportSignal(); 7937d1cc387SAppaRao Puli } 7947d1cc387SAppaRao Puli else 7957d1cc387SAppaRao Puli { 7967d1cc387SAppaRao Puli unregisterMetricReportSignal(); 7977d1cc387SAppaRao Puli } 7987d1cc387SAppaRao Puli } 7997d1cc387SAppaRao Puli } 8007d1cc387SAppaRao Puli 801b52664e2SAppaRao Puli std::shared_ptr<Subscription> getSubscription(const std::string& id) 802b52664e2SAppaRao Puli { 803b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 804b52664e2SAppaRao Puli if (obj == subscriptionsMap.end()) 805b52664e2SAppaRao Puli { 80662598e31SEd Tanous BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id); 807b52664e2SAppaRao Puli return nullptr; 808b52664e2SAppaRao Puli } 809b52664e2SAppaRao Puli std::shared_ptr<Subscription> subValue = obj->second; 810b52664e2SAppaRao Puli return subValue; 811b52664e2SAppaRao Puli } 812b52664e2SAppaRao Puli 813f80a87f2SEd Tanous std::string 814f80a87f2SEd Tanous addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue) 815b52664e2SAppaRao Puli { 816fc76b8acSEd Tanous std::uniform_int_distribution<uint32_t> dist(0); 817fc76b8acSEd Tanous bmcweb::OpenSSLGenerator gen; 818fc76b8acSEd Tanous 819b52664e2SAppaRao Puli std::string id; 820b52664e2SAppaRao Puli 821b52664e2SAppaRao Puli int retry = 3; 822e662eae8SEd Tanous while (retry != 0) 823b52664e2SAppaRao Puli { 824fc76b8acSEd Tanous id = std::to_string(dist(gen)); 825fc76b8acSEd Tanous if (gen.error()) 826fc76b8acSEd Tanous { 827fc76b8acSEd Tanous retry = 0; 828fc76b8acSEd Tanous break; 829fc76b8acSEd Tanous } 830b52664e2SAppaRao Puli auto inserted = subscriptionsMap.insert(std::pair(id, subValue)); 831b52664e2SAppaRao Puli if (inserted.second) 832b52664e2SAppaRao Puli { 833b52664e2SAppaRao Puli break; 834b52664e2SAppaRao Puli } 835b52664e2SAppaRao Puli --retry; 83623a21a1cSEd Tanous } 837b52664e2SAppaRao Puli 838b52664e2SAppaRao Puli if (retry <= 0) 839b52664e2SAppaRao Puli { 84062598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to generate random number"); 841abb93cddSEd Tanous return ""; 842b52664e2SAppaRao Puli } 843b52664e2SAppaRao Puli 84456ba386dSMyung Bae // Set Subscription ID for back trace 845*5fe4ef35SMyung Bae subValue->userSub->id = id; 846a14c9113SEd Tanous 84728afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 848*5fe4ef35SMyung Bae .subscriptionsConfigMap.emplace(id, subValue->userSub); 84928afb49cSJunLin Chen 8507d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 8511bf712bcSAyushi Smriti 85283328316SEd Tanous if constexpr (!BMCWEB_REDFISH_DBUS_LOG) 85383328316SEd Tanous { 8542558979cSP Dheeraj Srujan Kumar if (redfishLogFilePosition != 0) 8557f4eb588SAppaRao Puli { 8562558979cSP Dheeraj Srujan Kumar cacheRedfishLogFile(); 8577f4eb588SAppaRao Puli } 85883328316SEd Tanous } 859fe44eb0bSAyushi Smriti // Update retry configuration. 860fe44eb0bSAyushi Smriti subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); 861fe44eb0bSAyushi Smriti 862f80a87f2SEd Tanous return id; 863f80a87f2SEd Tanous } 864f80a87f2SEd Tanous 865f80a87f2SEd Tanous std::string 866f80a87f2SEd Tanous addSSESubscription(const std::shared_ptr<Subscription>& subValue, 867f80a87f2SEd Tanous std::string_view lastEventId) 868f80a87f2SEd Tanous { 869f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 870f80a87f2SEd Tanous 871f80a87f2SEd Tanous if (!lastEventId.empty()) 872f80a87f2SEd Tanous { 873f80a87f2SEd Tanous BMCWEB_LOG_INFO("Attempting to find message for last id {}", 874f80a87f2SEd Tanous lastEventId); 875f80a87f2SEd Tanous boost::circular_buffer<Event>::iterator lastEvent = 876f80a87f2SEd Tanous std::find_if(messages.begin(), messages.end(), 877f80a87f2SEd Tanous [&lastEventId](const Event& event) { 878f80a87f2SEd Tanous return event.id == lastEventId; 879f80a87f2SEd Tanous }); 880f80a87f2SEd Tanous // Can't find a matching ID 881f80a87f2SEd Tanous if (lastEvent == messages.end()) 882f80a87f2SEd Tanous { 883f80a87f2SEd Tanous nlohmann::json msg = messages::eventBufferExceeded(); 884f80a87f2SEd Tanous // If the buffer overloaded, send all messages. 8856d799e14SEd Tanous subValue->sendEventToSubscriber(msg); 886f80a87f2SEd Tanous lastEvent = messages.begin(); 887f80a87f2SEd Tanous } 888f80a87f2SEd Tanous else 889f80a87f2SEd Tanous { 890f80a87f2SEd Tanous // Skip the last event the user already has 891f80a87f2SEd Tanous lastEvent++; 892f80a87f2SEd Tanous } 893f80a87f2SEd Tanous 894f80a87f2SEd Tanous for (boost::circular_buffer<Event>::const_iterator event = 895f80a87f2SEd Tanous lastEvent; 896f80a87f2SEd Tanous lastEvent != messages.end(); lastEvent++) 897f80a87f2SEd Tanous { 8986d799e14SEd Tanous subValue->sendEventToSubscriber(event->message); 899f80a87f2SEd Tanous } 900f80a87f2SEd Tanous } 901f80a87f2SEd Tanous return id; 902f80a87f2SEd Tanous } 903f80a87f2SEd Tanous 904f80a87f2SEd Tanous std::string 905f80a87f2SEd Tanous addPushSubscription(const std::shared_ptr<Subscription>& subValue) 906f80a87f2SEd Tanous { 907f80a87f2SEd Tanous std::string id = addSubscriptionInternal(subValue); 908a0969c70SMyung Bae subValue->deleter = [id]() { 909a0969c70SMyung Bae EventServiceManager::getInstance().deleteSubscription(id); 910a0969c70SMyung Bae }; 911f80a87f2SEd Tanous updateSubscriptionData(); 912b52664e2SAppaRao Puli return id; 913b52664e2SAppaRao Puli } 914b52664e2SAppaRao Puli 915b52664e2SAppaRao Puli bool isSubscriptionExist(const std::string& id) 916b52664e2SAppaRao Puli { 917b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 91855f79e6fSEd Tanous return obj != subscriptionsMap.end(); 919b52664e2SAppaRao Puli } 920b52664e2SAppaRao Puli 9214b712a29SEd Tanous bool deleteSubscription(const std::string& id) 922b52664e2SAppaRao Puli { 923b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 9244b712a29SEd Tanous if (obj == subscriptionsMap.end()) 925b52664e2SAppaRao Puli { 9264b712a29SEd Tanous BMCWEB_LOG_WARNING("Could not find subscription with id {}", id); 9274b712a29SEd Tanous return false; 9284b712a29SEd Tanous } 929b52664e2SAppaRao Puli subscriptionsMap.erase(obj); 9304b712a29SEd Tanous auto& event = persistent_data::EventServiceStore::getInstance(); 9314b712a29SEd Tanous auto persistentObj = event.subscriptionsConfigMap.find(id); 9324b712a29SEd Tanous if (persistentObj == event.subscriptionsConfigMap.end()) 9334b712a29SEd Tanous { 9344b712a29SEd Tanous BMCWEB_LOG_ERROR("Subscription wasn't in persistent data"); 9354b712a29SEd Tanous return true; 9364b712a29SEd Tanous } 93728afb49cSJunLin Chen persistent_data::EventServiceStore::getInstance() 9384b712a29SEd Tanous .subscriptionsConfigMap.erase(persistentObj); 9397d1cc387SAppaRao Puli updateNoOfSubscribersCount(); 940b52664e2SAppaRao Puli updateSubscriptionData(); 9414b712a29SEd Tanous 9424b712a29SEd Tanous return true; 943b52664e2SAppaRao Puli } 944b52664e2SAppaRao Puli 9455e44e3d8SAppaRao Puli void deleteSseSubscription(const crow::sse_socket::Connection& thisConn) 9465e44e3d8SAppaRao Puli { 947bdbfae2aSEd Tanous for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();) 9485e44e3d8SAppaRao Puli { 949bdbfae2aSEd Tanous std::shared_ptr<Subscription> entry = it->second; 9505e44e3d8SAppaRao Puli bool entryIsThisConn = entry->matchSseId(thisConn); 9515e44e3d8SAppaRao Puli if (entryIsThisConn) 9525e44e3d8SAppaRao Puli { 9535e44e3d8SAppaRao Puli persistent_data::EventServiceStore::getInstance() 954*5fe4ef35SMyung Bae .subscriptionsConfigMap.erase(entry->userSub->id); 955bdbfae2aSEd Tanous it = subscriptionsMap.erase(it); 9565e44e3d8SAppaRao Puli return; 9575e44e3d8SAppaRao Puli } 958bdbfae2aSEd Tanous it++; 9595e44e3d8SAppaRao Puli } 9605e44e3d8SAppaRao Puli } 9615e44e3d8SAppaRao Puli 9625e44e3d8SAppaRao Puli size_t getNumberOfSubscriptions() const 963b52664e2SAppaRao Puli { 964b52664e2SAppaRao Puli return subscriptionsMap.size(); 965b52664e2SAppaRao Puli } 966b52664e2SAppaRao Puli 9675e44e3d8SAppaRao Puli size_t getNumberOfSSESubscriptions() const 9685e44e3d8SAppaRao Puli { 9693544d2a7SEd Tanous auto size = std::ranges::count_if( 9703544d2a7SEd Tanous subscriptionsMap, 9715e44e3d8SAppaRao Puli [](const std::pair<std::string, std::shared_ptr<Subscription>>& 9725e44e3d8SAppaRao Puli entry) { 973*5fe4ef35SMyung Bae return (entry.second->userSub->subscriptionType == 9744b712a29SEd Tanous subscriptionTypeSSE); 9755e44e3d8SAppaRao Puli }); 9765e44e3d8SAppaRao Puli return static_cast<size_t>(size); 9775e44e3d8SAppaRao Puli } 9785e44e3d8SAppaRao Puli 979b52664e2SAppaRao Puli std::vector<std::string> getAllIDs() 980b52664e2SAppaRao Puli { 981b52664e2SAppaRao Puli std::vector<std::string> idList; 982b52664e2SAppaRao Puli for (const auto& it : subscriptionsMap) 983b52664e2SAppaRao Puli { 984b52664e2SAppaRao Puli idList.emplace_back(it.first); 985b52664e2SAppaRao Puli } 986b52664e2SAppaRao Puli return idList; 987b52664e2SAppaRao Puli } 988b52664e2SAppaRao Puli 9896ba8c82eSsunharis_in bool sendTestEventLog() 9900b4bdd93SAppaRao Puli { 9915e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 9920b4bdd93SAppaRao Puli { 9930b4bdd93SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 9946ba8c82eSsunharis_in if (!entry->sendTestEventLog()) 9956ba8c82eSsunharis_in { 9966ba8c82eSsunharis_in return false; 9970b4bdd93SAppaRao Puli } 9980b4bdd93SAppaRao Puli } 9996ba8c82eSsunharis_in return true; 10006ba8c82eSsunharis_in } 1001e9a14131SAppaRao Puli 1002f80a87f2SEd Tanous void sendEvent(nlohmann::json::object_t eventMessage, 1003f80a87f2SEd Tanous std::string_view origin, std::string_view resourceType) 100496330b99SSunitha Harish { 1005613dabeaSEd Tanous eventMessage["EventId"] = eventId; 1006f80a87f2SEd Tanous 1007613dabeaSEd Tanous eventMessage["EventTimestamp"] = 1008613dabeaSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 1009613dabeaSEd Tanous eventMessage["OriginOfCondition"] = origin; 1010613dabeaSEd Tanous 1011f80a87f2SEd Tanous // MemberId is 0 : since we are sending one event record. 1012788b091bSIgor Kanyuka eventMessage["MemberId"] = "0"; 101396330b99SSunitha Harish 1014f80a87f2SEd Tanous messages.push_back(Event(std::to_string(eventId), eventMessage)); 1015f80a87f2SEd Tanous 1016f80a87f2SEd Tanous for (auto& it : subscriptionsMap) 101796330b99SSunitha Harish { 1018f80a87f2SEd Tanous std::shared_ptr<Subscription>& entry = it.second; 1019*5fe4ef35SMyung Bae if (!eventMatchesFilter(*entry->userSub, eventMessage, 1020*5fe4ef35SMyung Bae resourceType)) 102196330b99SSunitha Harish { 1022f80a87f2SEd Tanous BMCWEB_LOG_DEBUG("Filter didn't match"); 1023f80a87f2SEd Tanous continue; 102496330b99SSunitha Harish } 1025f80a87f2SEd Tanous 1026f80a87f2SEd Tanous nlohmann::json::array_t eventRecord; 1027f80a87f2SEd Tanous eventRecord.emplace_back(eventMessage); 1028f80a87f2SEd Tanous 1029613dabeaSEd Tanous nlohmann::json msgJson; 1030613dabeaSEd Tanous 1031613dabeaSEd Tanous msgJson["@odata.type"] = "#Event.v1_4_0.Event"; 1032613dabeaSEd Tanous msgJson["Name"] = "Event Log"; 1033613dabeaSEd Tanous msgJson["Id"] = eventId; 1034f80a87f2SEd Tanous msgJson["Events"] = std::move(eventRecord); 1035f52c03c1SCarson Labrado 1036f52c03c1SCarson Labrado std::string strMsg = msgJson.dump( 1037f52c03c1SCarson Labrado 2, ' ', true, nlohmann::json::error_handler_t::replace); 10386d799e14SEd Tanous entry->sendEventToSubscriber(std::move(strMsg)); 10398ece0e45SEd Tanous eventId++; // increment the eventId 104096330b99SSunitha Harish } 104196330b99SSunitha Harish } 104296330b99SSunitha Harish 10432558979cSP Dheeraj Srujan Kumar void resetRedfishFilePosition() 10447f4eb588SAppaRao Puli { 10452558979cSP Dheeraj Srujan Kumar // Control would be here when Redfish file is created. 10462558979cSP Dheeraj Srujan Kumar // Reset File Position as new file is created 10472558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = 0; 10482558979cSP Dheeraj Srujan Kumar } 10492558979cSP Dheeraj Srujan Kumar 10502558979cSP Dheeraj Srujan Kumar void cacheRedfishLogFile() 10512558979cSP Dheeraj Srujan Kumar { 10522558979cSP Dheeraj Srujan Kumar // Open the redfish file and read till the last record. 10532558979cSP Dheeraj Srujan Kumar 10547f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 10557f4eb588SAppaRao Puli if (!logStream.good()) 10567f4eb588SAppaRao Puli { 105762598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed "); 10587f4eb588SAppaRao Puli return; 10597f4eb588SAppaRao Puli } 10607f4eb588SAppaRao Puli std::string logEntry; 10617f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 10627f4eb588SAppaRao Puli { 10632558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 10647f4eb588SAppaRao Puli } 10657f4eb588SAppaRao Puli } 10667f4eb588SAppaRao Puli 10677f4eb588SAppaRao Puli void readEventLogsFromFile() 10687f4eb588SAppaRao Puli { 10697f4eb588SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 10707f4eb588SAppaRao Puli if (!logStream.good()) 10717f4eb588SAppaRao Puli { 107262598e31SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed"); 10737f4eb588SAppaRao Puli return; 10747f4eb588SAppaRao Puli } 10757f4eb588SAppaRao Puli 10767f4eb588SAppaRao Puli std::vector<EventLogObjectsType> eventRecords; 10777f4eb588SAppaRao Puli 10787f4eb588SAppaRao Puli std::string logEntry; 10792558979cSP Dheeraj Srujan Kumar 108003d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: seek to {}", 108103d4d37cSAlexander Hansen static_cast<int>(redfishLogFilePosition)); 108203d4d37cSAlexander Hansen 10832558979cSP Dheeraj Srujan Kumar // Get the read pointer to the next log to be read. 10842558979cSP Dheeraj Srujan Kumar logStream.seekg(redfishLogFilePosition); 10852558979cSP Dheeraj Srujan Kumar 10867f4eb588SAppaRao Puli while (std::getline(logStream, logEntry)) 10877f4eb588SAppaRao Puli { 108803d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry"); 10892558979cSP Dheeraj Srujan Kumar // Update Pointer position 10902558979cSP Dheeraj Srujan Kumar redfishLogFilePosition = logStream.tellg(); 10912558979cSP Dheeraj Srujan Kumar 10922558979cSP Dheeraj Srujan Kumar std::string idStr; 10932558979cSP Dheeraj Srujan Kumar if (!event_log::getUniqueEntryID(logEntry, idStr)) 10947f4eb588SAppaRao Puli { 109503d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 109603d4d37cSAlexander Hansen "Redfish log file: could not get unique entry id for {}", 109703d4d37cSAlexander Hansen logEntry); 10987f4eb588SAppaRao Puli continue; 10997f4eb588SAppaRao Puli } 11007f4eb588SAppaRao Puli 1101e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 11027f4eb588SAppaRao Puli { 11032558979cSP Dheeraj Srujan Kumar // If Service is not enabled, no need to compute 11042558979cSP Dheeraj Srujan Kumar // the remaining items below. 11052558979cSP Dheeraj Srujan Kumar // But, Loop must continue to keep track of Timestamp 110603d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG( 110703d4d37cSAlexander Hansen "Redfish log file: no subscribers / event service not enabled"); 11087f4eb588SAppaRao Puli continue; 11097f4eb588SAppaRao Puli } 11107f4eb588SAppaRao Puli 11117f4eb588SAppaRao Puli std::string timestamp; 11127f4eb588SAppaRao Puli std::string messageID; 11135e715de6SAppaRao Puli std::vector<std::string> messageArgs; 11147f4eb588SAppaRao Puli if (event_log::getEventLogParams(logEntry, timestamp, messageID, 11157f4eb588SAppaRao Puli messageArgs) != 0) 11167f4eb588SAppaRao Puli { 111703d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}", 111803d4d37cSAlexander Hansen logEntry); 11197f4eb588SAppaRao Puli continue; 11207f4eb588SAppaRao Puli } 11217f4eb588SAppaRao Puli 1122f80a87f2SEd Tanous eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs); 11237f4eb588SAppaRao Puli } 11247f4eb588SAppaRao Puli 1125e662eae8SEd Tanous if (!serviceEnabled || noOfEventLogSubscribers == 0) 11262558979cSP Dheeraj Srujan Kumar { 112762598e31SEd Tanous BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions."); 11282558979cSP Dheeraj Srujan Kumar return; 11292558979cSP Dheeraj Srujan Kumar } 11302558979cSP Dheeraj Srujan Kumar 11312558979cSP Dheeraj Srujan Kumar if (eventRecords.empty()) 11322558979cSP Dheeraj Srujan Kumar { 11332558979cSP Dheeraj Srujan Kumar // No Records to send 113462598e31SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 11352558979cSP Dheeraj Srujan Kumar return; 11362558979cSP Dheeraj Srujan Kumar } 11372558979cSP Dheeraj Srujan Kumar 11385e44e3d8SAppaRao Puli for (const auto& it : subscriptionsMap) 11397f4eb588SAppaRao Puli { 11407f4eb588SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 1141*5fe4ef35SMyung Bae if (entry->userSub->eventFormatType == "Event") 11427f4eb588SAppaRao Puli { 11437f4eb588SAppaRao Puli entry->filterAndSendEventLogs(eventRecords); 11447f4eb588SAppaRao Puli } 11457f4eb588SAppaRao Puli } 11467f4eb588SAppaRao Puli } 11477f4eb588SAppaRao Puli 11487f4eb588SAppaRao Puli static void watchRedfishEventLogFile() 11497f4eb588SAppaRao Puli { 11506a9f85f9SAppaRao Puli if (!inotifyConn) 11517f4eb588SAppaRao Puli { 115203d4d37cSAlexander Hansen BMCWEB_LOG_ERROR("inotify Connection is not present"); 11537f4eb588SAppaRao Puli return; 11547f4eb588SAppaRao Puli } 11557f4eb588SAppaRao Puli 11567f4eb588SAppaRao Puli static std::array<char, 1024> readBuffer; 11577f4eb588SAppaRao Puli 1158bd79bce8SPatrick Williams inotifyConn->async_read_some( 1159bd79bce8SPatrick Williams boost::asio::buffer(readBuffer), 11607f4eb588SAppaRao Puli [&](const boost::system::error_code& ec, 11617f4eb588SAppaRao Puli const std::size_t& bytesTransferred) { 11629ed3f90aSEd Tanous if (ec == boost::asio::error::operation_aborted) 11639ed3f90aSEd Tanous { 11649ed3f90aSEd Tanous BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)"); 11659ed3f90aSEd Tanous return; 11669ed3f90aSEd Tanous } 11677f4eb588SAppaRao Puli if (ec) 11687f4eb588SAppaRao Puli { 116962598e31SEd Tanous BMCWEB_LOG_ERROR("Callback Error: {}", ec.message()); 11707f4eb588SAppaRao Puli return; 11717f4eb588SAppaRao Puli } 117203d4d37cSAlexander Hansen 117303d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred); 117403d4d37cSAlexander Hansen 11757f4eb588SAppaRao Puli std::size_t index = 0; 1176b792cc56SAppaRao Puli while ((index + iEventSize) <= bytesTransferred) 11777f4eb588SAppaRao Puli { 1178d3a9e084SEd Tanous struct inotify_event event 1179d3a9e084SEd Tanous {}; 1180b792cc56SAppaRao Puli std::memcpy(&event, &readBuffer[index], iEventSize); 1181b792cc56SAppaRao Puli if (event.wd == dirWatchDesc) 1182b792cc56SAppaRao Puli { 1183b792cc56SAppaRao Puli if ((event.len == 0) || 1184b792cc56SAppaRao Puli (index + iEventSize + event.len > bytesTransferred)) 1185b792cc56SAppaRao Puli { 1186b792cc56SAppaRao Puli index += (iEventSize + event.len); 1187b792cc56SAppaRao Puli continue; 1188b792cc56SAppaRao Puli } 1189b792cc56SAppaRao Puli 11904f568f74SJiaqing Zhao std::string fileName(&readBuffer[index + iEventSize]); 11914f568f74SJiaqing Zhao if (fileName != "redfish") 1192b792cc56SAppaRao Puli { 1193b792cc56SAppaRao Puli index += (iEventSize + event.len); 1194b792cc56SAppaRao Puli continue; 1195b792cc56SAppaRao Puli } 1196b792cc56SAppaRao Puli 119762598e31SEd Tanous BMCWEB_LOG_DEBUG( 119862598e31SEd Tanous "Redfish log file created/deleted. event.name: {}", 119962598e31SEd Tanous fileName); 1200b792cc56SAppaRao Puli if (event.mask == IN_CREATE) 1201b792cc56SAppaRao Puli { 1202b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1203b792cc56SAppaRao Puli { 120462598e31SEd Tanous BMCWEB_LOG_DEBUG( 120562598e31SEd Tanous "Remove and Add inotify watcher on " 120662598e31SEd Tanous "redfish event log file"); 1207016761afSAppaRao Puli // Remove existing inotify watcher and add 1208016761afSAppaRao Puli // with new redfish event log file. 1209016761afSAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1210016761afSAppaRao Puli fileWatchDesc = -1; 1211b792cc56SAppaRao Puli } 1212b792cc56SAppaRao Puli 1213b792cc56SAppaRao Puli fileWatchDesc = inotify_add_watch( 1214b792cc56SAppaRao Puli inotifyFd, redfishEventLogFile, IN_MODIFY); 1215b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1216b792cc56SAppaRao Puli { 121762598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for " 121862598e31SEd Tanous "redfish log file."); 1219b792cc56SAppaRao Puli return; 1220b792cc56SAppaRao Puli } 1221b792cc56SAppaRao Puli 1222b792cc56SAppaRao Puli EventServiceManager::getInstance() 12232558979cSP Dheeraj Srujan Kumar .resetRedfishFilePosition(); 1224b792cc56SAppaRao Puli EventServiceManager::getInstance() 1225b792cc56SAppaRao Puli .readEventLogsFromFile(); 1226b792cc56SAppaRao Puli } 1227b792cc56SAppaRao Puli else if ((event.mask == IN_DELETE) || 1228b792cc56SAppaRao Puli (event.mask == IN_MOVED_TO)) 1229b792cc56SAppaRao Puli { 1230b792cc56SAppaRao Puli if (fileWatchDesc != -1) 1231b792cc56SAppaRao Puli { 1232b792cc56SAppaRao Puli inotify_rm_watch(inotifyFd, fileWatchDesc); 1233b792cc56SAppaRao Puli fileWatchDesc = -1; 1234b792cc56SAppaRao Puli } 1235b792cc56SAppaRao Puli } 1236b792cc56SAppaRao Puli } 1237b792cc56SAppaRao Puli else if (event.wd == fileWatchDesc) 1238b792cc56SAppaRao Puli { 1239b792cc56SAppaRao Puli if (event.mask == IN_MODIFY) 12407f4eb588SAppaRao Puli { 12417f4eb588SAppaRao Puli EventServiceManager::getInstance() 12427f4eb588SAppaRao Puli .readEventLogsFromFile(); 12437f4eb588SAppaRao Puli } 1244b792cc56SAppaRao Puli } 1245b792cc56SAppaRao Puli index += (iEventSize + event.len); 12467f4eb588SAppaRao Puli } 12477f4eb588SAppaRao Puli 12487f4eb588SAppaRao Puli watchRedfishEventLogFile(); 12497f4eb588SAppaRao Puli }); 12507f4eb588SAppaRao Puli } 12517f4eb588SAppaRao Puli 12527f4eb588SAppaRao Puli static int startEventLogMonitor(boost::asio::io_context& ioc) 12537f4eb588SAppaRao Puli { 125403d4d37cSAlexander Hansen BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 125503d4d37cSAlexander Hansen 125623a21a1cSEd Tanous inotifyConn.emplace(ioc); 1257b792cc56SAppaRao Puli inotifyFd = inotify_init1(IN_NONBLOCK); 1258b792cc56SAppaRao Puli if (inotifyFd == -1) 12597f4eb588SAppaRao Puli { 126062598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_init1 failed."); 12617f4eb588SAppaRao Puli return -1; 12627f4eb588SAppaRao Puli } 1263b792cc56SAppaRao Puli 1264b792cc56SAppaRao Puli // Add watch on directory to handle redfish event log file 1265b792cc56SAppaRao Puli // create/delete. 1266b792cc56SAppaRao Puli dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 1267b792cc56SAppaRao Puli IN_CREATE | IN_MOVED_TO | IN_DELETE); 1268b792cc56SAppaRao Puli if (dirWatchDesc == -1) 12697f4eb588SAppaRao Puli { 127062598e31SEd Tanous BMCWEB_LOG_ERROR( 127162598e31SEd Tanous "inotify_add_watch failed for event log directory."); 12727f4eb588SAppaRao Puli return -1; 12737f4eb588SAppaRao Puli } 12747f4eb588SAppaRao Puli 1275b792cc56SAppaRao Puli // Watch redfish event log file for modifications. 1276bd79bce8SPatrick Williams fileWatchDesc = 1277bd79bce8SPatrick Williams inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 1278b792cc56SAppaRao Puli if (fileWatchDesc == -1) 1279b792cc56SAppaRao Puli { 128062598e31SEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 1281b792cc56SAppaRao Puli // Don't return error if file not exist. 1282b792cc56SAppaRao Puli // Watch on directory will handle create/delete of file. 1283b792cc56SAppaRao Puli } 1284b792cc56SAppaRao Puli 12857f4eb588SAppaRao Puli // monitor redfish event log file 1286b792cc56SAppaRao Puli inotifyConn->assign(inotifyFd); 12877f4eb588SAppaRao Puli watchRedfishEventLogFile(); 12887f4eb588SAppaRao Puli 12897f4eb588SAppaRao Puli return 0; 12907f4eb588SAppaRao Puli } 12917f4eb588SAppaRao Puli 12929ed3f90aSEd Tanous static void stopEventLogMonitor() 12939ed3f90aSEd Tanous { 12949ed3f90aSEd Tanous inotifyConn.reset(); 12959ed3f90aSEd Tanous } 12969ed3f90aSEd Tanous 129759d494eeSPatrick Williams static void getReadingsForReport(sdbusplus::message_t& msg) 1298156d6b00SAppaRao Puli { 129956d2396dSEd Tanous if (msg.is_method_error()) 130056d2396dSEd Tanous { 130162598e31SEd Tanous BMCWEB_LOG_ERROR("TelemetryMonitor Signal error"); 130256d2396dSEd Tanous return; 130356d2396dSEd Tanous } 130456d2396dSEd Tanous 1305c0353249SWludzik, Jozef sdbusplus::message::object_path path(msg.get_path()); 1306c0353249SWludzik, Jozef std::string id = path.filename(); 1307c0353249SWludzik, Jozef if (id.empty()) 1308156d6b00SAppaRao Puli { 130962598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to get Id from path"); 1310156d6b00SAppaRao Puli return; 1311156d6b00SAppaRao Puli } 1312156d6b00SAppaRao Puli 1313c0353249SWludzik, Jozef std::string interface; 1314b9d36b47SEd Tanous dbus::utility::DBusPropertiesMap props; 1315c0353249SWludzik, Jozef std::vector<std::string> invalidProps; 1316c0353249SWludzik, Jozef msg.read(interface, props, invalidProps); 1317c0353249SWludzik, Jozef 1318bd79bce8SPatrick Williams auto found = std::ranges::find_if(props, [](const auto& x) { 1319bd79bce8SPatrick Williams return x.first == "Readings"; 1320bd79bce8SPatrick Williams }); 1321c0353249SWludzik, Jozef if (found == props.end()) 1322156d6b00SAppaRao Puli { 132362598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 1324156d6b00SAppaRao Puli return; 1325156d6b00SAppaRao Puli } 1326156d6b00SAppaRao Puli 13271e1e598dSJonathan Doman const telemetry::TimestampReadings* readings = 13281e1e598dSJonathan Doman std::get_if<telemetry::TimestampReadings>(&found->second); 1329e662eae8SEd Tanous if (readings == nullptr) 13301e1e598dSJonathan Doman { 133162598e31SEd Tanous BMCWEB_LOG_INFO("Failed to get Readings from Report properties"); 13321e1e598dSJonathan Doman return; 13331e1e598dSJonathan Doman } 13341e1e598dSJonathan Doman 1335156d6b00SAppaRao Puli for (const auto& it : 1336156d6b00SAppaRao Puli EventServiceManager::getInstance().subscriptionsMap) 1337156d6b00SAppaRao Puli { 1338e05aec50SEd Tanous Subscription& entry = *it.second; 1339*5fe4ef35SMyung Bae if (entry.userSub->eventFormatType == metricReportFormatType) 1340156d6b00SAppaRao Puli { 13411e1e598dSJonathan Doman entry.filterAndSendReports(id, *readings); 1342156d6b00SAppaRao Puli } 1343156d6b00SAppaRao Puli } 1344156d6b00SAppaRao Puli } 1345156d6b00SAppaRao Puli 1346156d6b00SAppaRao Puli void unregisterMetricReportSignal() 1347156d6b00SAppaRao Puli { 13487d1cc387SAppaRao Puli if (matchTelemetryMonitor) 13497d1cc387SAppaRao Puli { 135062598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Unregister"); 1351156d6b00SAppaRao Puli matchTelemetryMonitor.reset(); 1352156d6b00SAppaRao Puli matchTelemetryMonitor = nullptr; 1353156d6b00SAppaRao Puli } 13547d1cc387SAppaRao Puli } 1355156d6b00SAppaRao Puli 1356156d6b00SAppaRao Puli void registerMetricReportSignal() 1357156d6b00SAppaRao Puli { 13587d1cc387SAppaRao Puli if (!serviceEnabled || matchTelemetryMonitor) 1359156d6b00SAppaRao Puli { 136062598e31SEd Tanous BMCWEB_LOG_DEBUG("Not registering metric report signal."); 1361156d6b00SAppaRao Puli return; 1362156d6b00SAppaRao Puli } 1363156d6b00SAppaRao Puli 136462598e31SEd Tanous BMCWEB_LOG_DEBUG("Metrics report signal - Register"); 1365c0353249SWludzik, Jozef std::string matchStr = "type='signal',member='PropertiesChanged'," 1366c0353249SWludzik, Jozef "interface='org.freedesktop.DBus.Properties'," 1367c0353249SWludzik, Jozef "arg0=xyz.openbmc_project.Telemetry.Report"; 1368156d6b00SAppaRao Puli 136959d494eeSPatrick Williams matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>( 137056d2396dSEd Tanous *crow::connections::systemBus, matchStr, getReadingsForReport); 1371156d6b00SAppaRao Puli } 137223a21a1cSEd Tanous }; 1373b52664e2SAppaRao Puli 1374b52664e2SAppaRao Puli } // namespace redfish 1375