xref: /openbmc/bmcweb/features/redfish/include/event_service_manager.hpp (revision 5fe4ef35821f946c91d9c20cee01e632d6d3ffd4)
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