xref: /openbmc/bmcweb/features/redfish/include/event_service_manager.hpp (revision 02c1e29fceae14aa5cb7a1b3d6bcaa4c0a256f2e)
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"
21c0353249SWludzik, Jozef #include "metric_report.hpp"
222c6ffdb0SEd Tanous #include "ossl_random.hpp"
233ccb3adbSEd Tanous #include "persistent_data.hpp"
247f4eb588SAppaRao Puli #include "registries.hpp"
258dab0f58SEd Tanous #include "registries_selector.hpp"
2650ebd4afSEd Tanous #include "str_utility.hpp"
27*02c1e29fSAlexander Hansen #include "subscription.hpp"
285b90429aSEd Tanous #include "utils/time_utils.hpp"
297f4eb588SAppaRao Puli 
307f4eb588SAppaRao Puli #include <sys/inotify.h>
31b52664e2SAppaRao Puli 
32fb4fd5d4SZhenfei Tai #include <boost/asio/io_context.hpp>
33f80a87f2SEd Tanous #include <boost/circular_buffer.hpp>
34b52664e2SAppaRao Puli #include <boost/container/flat_map.hpp>
35ef4c65b7SEd Tanous #include <boost/url/format.hpp>
364a7fbefdSEd Tanous #include <boost/url/url_view_base.hpp>
37b5b40605Snitroglycerine #include <sdbusplus/bus/match.hpp>
381214b7e7SGunnar Mills 
395e44e3d8SAppaRao Puli #include <algorithm>
40b52664e2SAppaRao Puli #include <cstdlib>
41b52664e2SAppaRao Puli #include <ctime>
42a14c9113SEd Tanous #include <format>
431bf712bcSAyushi Smriti #include <fstream>
44b52664e2SAppaRao Puli #include <memory>
4526702d01SEd Tanous #include <span>
46a14c9113SEd Tanous #include <string>
4756ba386dSMyung Bae #include <string_view>
485fe4ef35SMyung Bae #include <utility>
49b52664e2SAppaRao Puli 
50b52664e2SAppaRao Puli namespace redfish
51b52664e2SAppaRao Puli {
52156d6b00SAppaRao Puli 
53156d6b00SAppaRao Puli static constexpr const char* eventFormatType = "Event";
54156d6b00SAppaRao Puli static constexpr const char* metricReportFormatType = "MetricReport";
55156d6b00SAppaRao Puli 
561bf712bcSAyushi Smriti static constexpr const char* eventServiceFile =
571bf712bcSAyushi Smriti     "/var/lib/bmcweb/eventservice_config.json";
581bf712bcSAyushi Smriti 
59cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
604642bf8fSGeorge Liu static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
614642bf8fSGeorge Liu static constexpr const char* redfishEventLogDir = "/var/log";
624642bf8fSGeorge Liu static constexpr const char* redfishEventLogFile = "/var/log/redfish";
634642bf8fSGeorge Liu static constexpr const size_t iEventSize = sizeof(inotify_event);
64cf9e417dSEd Tanous 
65cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
664642bf8fSGeorge Liu static int inotifyFd = -1;
67cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
684642bf8fSGeorge Liu static int dirWatchDesc = -1;
69cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
704642bf8fSGeorge Liu static int fileWatchDesc = -1;
714642bf8fSGeorge Liu 
72fffb8c1fSEd Tanous namespace registries
734642bf8fSGeorge Liu {
747f4eb588SAppaRao Puli static const Message*
757f4eb588SAppaRao Puli     getMsgFromRegistry(const std::string& messageKey,
7626702d01SEd Tanous                        const std::span<const MessageEntry>& registry)
777f4eb588SAppaRao Puli {
783544d2a7SEd Tanous     std::span<const MessageEntry>::iterator messageIt = std::ranges::find_if(
793544d2a7SEd Tanous         registry, [&messageKey](const MessageEntry& messageEntry) {
8055f79e6fSEd Tanous             return messageKey == messageEntry.first;
817f4eb588SAppaRao Puli         });
8226702d01SEd Tanous     if (messageIt != registry.end())
837f4eb588SAppaRao Puli     {
847f4eb588SAppaRao Puli         return &messageIt->second;
857f4eb588SAppaRao Puli     }
867f4eb588SAppaRao Puli 
877f4eb588SAppaRao Puli     return nullptr;
887f4eb588SAppaRao Puli }
897f4eb588SAppaRao Puli 
9026ccae32SEd Tanous static const Message* formatMessage(std::string_view messageID)
917f4eb588SAppaRao Puli {
927f4eb588SAppaRao Puli     // Redfish MessageIds are in the form
937f4eb588SAppaRao Puli     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
947f4eb588SAppaRao Puli     // the right Message
957f4eb588SAppaRao Puli     std::vector<std::string> fields;
967f4eb588SAppaRao Puli     fields.reserve(4);
9750ebd4afSEd Tanous 
9850ebd4afSEd Tanous     bmcweb::split(fields, messageID, '.');
997f4eb588SAppaRao Puli     if (fields.size() != 4)
1007f4eb588SAppaRao Puli     {
1017f4eb588SAppaRao Puli         return nullptr;
1027f4eb588SAppaRao Puli     }
10302cad96eSEd Tanous     const std::string& registryName = fields[0];
10402cad96eSEd Tanous     const std::string& messageKey = fields[3];
1057f4eb588SAppaRao Puli 
1067f4eb588SAppaRao Puli     // Find the right registry and check it for the MessageKey
107b304bd79SP Dheeraj Srujan Kumar     return getMsgFromRegistry(messageKey, getRegistryFromPrefix(registryName));
1087f4eb588SAppaRao Puli }
109fffb8c1fSEd Tanous } // namespace registries
1107f4eb588SAppaRao Puli 
1117f4eb588SAppaRao Puli namespace event_log
1127f4eb588SAppaRao Puli {
1132558979cSP Dheeraj Srujan Kumar inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID)
1147f4eb588SAppaRao Puli {
1157f4eb588SAppaRao Puli     static time_t prevTs = 0;
1167f4eb588SAppaRao Puli     static int index = 0;
1177f4eb588SAppaRao Puli 
1187f4eb588SAppaRao Puli     // Get the entry timestamp
1197f4eb588SAppaRao Puli     std::time_t curTs = 0;
1207f4eb588SAppaRao Puli     std::tm timeStruct = {};
1217f4eb588SAppaRao Puli     std::istringstream entryStream(logEntry);
1227f4eb588SAppaRao Puli     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
1237f4eb588SAppaRao Puli     {
1247f4eb588SAppaRao Puli         curTs = std::mktime(&timeStruct);
1257f4eb588SAppaRao Puli         if (curTs == -1)
1267f4eb588SAppaRao Puli         {
1277f4eb588SAppaRao Puli             return false;
1287f4eb588SAppaRao Puli         }
1297f4eb588SAppaRao Puli     }
1307f4eb588SAppaRao Puli     // If the timestamp isn't unique, increment the index
1317f4eb588SAppaRao Puli     index = (curTs == prevTs) ? index + 1 : 0;
1327f4eb588SAppaRao Puli 
1337f4eb588SAppaRao Puli     // Save the timestamp
1347f4eb588SAppaRao Puli     prevTs = curTs;
1357f4eb588SAppaRao Puli 
1367f4eb588SAppaRao Puli     entryID = std::to_string(curTs);
1377f4eb588SAppaRao Puli     if (index > 0)
1387f4eb588SAppaRao Puli     {
1397f4eb588SAppaRao Puli         entryID += "_" + std::to_string(index);
1407f4eb588SAppaRao Puli     }
1417f4eb588SAppaRao Puli     return true;
1427f4eb588SAppaRao Puli }
1437f4eb588SAppaRao Puli 
14423a21a1cSEd Tanous inline int getEventLogParams(const std::string& logEntry,
14523a21a1cSEd Tanous                              std::string& timestamp, std::string& messageID,
1465e715de6SAppaRao Puli                              std::vector<std::string>& messageArgs)
1477f4eb588SAppaRao Puli {
1487f4eb588SAppaRao Puli     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
1497f4eb588SAppaRao Puli     // First get the Timestamp
150f23b7296SEd Tanous     size_t space = logEntry.find_first_of(' ');
1517f4eb588SAppaRao Puli     if (space == std::string::npos)
1527f4eb588SAppaRao Puli     {
15303d4d37cSAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}",
15403d4d37cSAlexander Hansen                          logEntry);
1557f4eb588SAppaRao Puli         return -EINVAL;
1567f4eb588SAppaRao Puli     }
1577f4eb588SAppaRao Puli     timestamp = logEntry.substr(0, space);
1587f4eb588SAppaRao Puli     // Then get the log contents
159f23b7296SEd Tanous     size_t entryStart = logEntry.find_first_not_of(' ', space);
1607f4eb588SAppaRao Puli     if (entryStart == std::string::npos)
1617f4eb588SAppaRao Puli     {
16203d4d37cSAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}",
16303d4d37cSAlexander Hansen                          logEntry);
1647f4eb588SAppaRao Puli         return -EINVAL;
1657f4eb588SAppaRao Puli     }
1667f4eb588SAppaRao Puli     std::string_view entry(logEntry);
1677f4eb588SAppaRao Puli     entry.remove_prefix(entryStart);
1687f4eb588SAppaRao Puli     // Use split to separate the entry into its fields
1697f4eb588SAppaRao Puli     std::vector<std::string> logEntryFields;
17050ebd4afSEd Tanous     bmcweb::split(logEntryFields, entry, ',');
1717f4eb588SAppaRao Puli     // We need at least a MessageId to be valid
17226f6976fSEd Tanous     if (logEntryFields.empty())
1737f4eb588SAppaRao Puli     {
17403d4d37cSAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}",
17503d4d37cSAlexander Hansen                          logEntry);
1767f4eb588SAppaRao Puli         return -EINVAL;
1777f4eb588SAppaRao Puli     }
1787f4eb588SAppaRao Puli     messageID = logEntryFields[0];
1797f4eb588SAppaRao Puli 
1807f4eb588SAppaRao Puli     // Get the MessageArgs from the log if there are any
1817f4eb588SAppaRao Puli     if (logEntryFields.size() > 1)
1827f4eb588SAppaRao Puli     {
18302cad96eSEd Tanous         const std::string& messageArgsStart = logEntryFields[1];
1847f4eb588SAppaRao Puli         // If the first string is empty, assume there are no MessageArgs
1857f4eb588SAppaRao Puli         if (!messageArgsStart.empty())
1867f4eb588SAppaRao Puli         {
1875e715de6SAppaRao Puli             messageArgs.assign(logEntryFields.begin() + 1,
1885e715de6SAppaRao Puli                                logEntryFields.end());
1897f4eb588SAppaRao Puli         }
1907f4eb588SAppaRao Puli     }
1917f4eb588SAppaRao Puli 
1927f4eb588SAppaRao Puli     return 0;
1937f4eb588SAppaRao Puli }
1947f4eb588SAppaRao Puli 
195bd79bce8SPatrick Williams inline int formatEventLogEntry(
196bd79bce8SPatrick Williams     const std::string& logEntryID, const std::string& messageID,
197bd79bce8SPatrick Williams     const std::span<std::string_view> messageArgs, std::string timestamp,
198bd79bce8SPatrick Williams     const std::string& customText, nlohmann::json::object_t& logEntryJson)
1997f4eb588SAppaRao Puli {
2007f4eb588SAppaRao Puli     // Get the Message from the MessageRegistry
201fffb8c1fSEd Tanous     const registries::Message* message = registries::formatMessage(messageID);
2027f4eb588SAppaRao Puli 
20380f595e7SEd Tanous     if (message == nullptr)
2047f4eb588SAppaRao Puli     {
20580f595e7SEd Tanous         return -1;
2067f4eb588SAppaRao Puli     }
2077f4eb588SAppaRao Puli 
208bd79bce8SPatrick Williams     std::string msg =
209bd79bce8SPatrick Williams         redfish::registries::fillMessageArgs(messageArgs, message->message);
21080f595e7SEd Tanous     if (msg.empty())
21180f595e7SEd Tanous     {
21280f595e7SEd Tanous         return -1;
21380f595e7SEd Tanous     }
2147f4eb588SAppaRao Puli 
2157f4eb588SAppaRao Puli     // Get the Created time from the timestamp. The log timestamp is in
2167f4eb588SAppaRao Puli     // RFC3339 format which matches the Redfish format except for the
2177f4eb588SAppaRao Puli     // fractional seconds between the '.' and the '+', so just remove them.
218f23b7296SEd Tanous     std::size_t dot = timestamp.find_first_of('.');
219b2f7609bSEd Tanous     std::size_t plus = timestamp.find_first_of('+', dot);
2207f4eb588SAppaRao Puli     if (dot != std::string::npos && plus != std::string::npos)
2217f4eb588SAppaRao Puli     {
2227f4eb588SAppaRao Puli         timestamp.erase(dot, plus - dot);
2237f4eb588SAppaRao Puli     }
2247f4eb588SAppaRao Puli 
2257f4eb588SAppaRao Puli     // Fill in the log entry with the gathered data
2261476687dSEd Tanous     logEntryJson["EventId"] = logEntryID;
227539d8c6bSEd Tanous 
22880f595e7SEd Tanous     logEntryJson["Severity"] = message->messageSeverity;
2291476687dSEd Tanous     logEntryJson["Message"] = std::move(msg);
2301476687dSEd Tanous     logEntryJson["MessageId"] = messageID;
2311476687dSEd Tanous     logEntryJson["MessageArgs"] = messageArgs;
2321476687dSEd Tanous     logEntryJson["EventTimestamp"] = std::move(timestamp);
2331476687dSEd Tanous     logEntryJson["Context"] = customText;
2347f4eb588SAppaRao Puli     return 0;
2357f4eb588SAppaRao Puli }
2367f4eb588SAppaRao Puli 
2377f4eb588SAppaRao Puli } // namespace event_log
2387f4eb588SAppaRao Puli 
239b52664e2SAppaRao Puli class EventServiceManager
240b52664e2SAppaRao Puli {
241b52664e2SAppaRao Puli   private:
242d3a9e084SEd Tanous     bool serviceEnabled = false;
243d3a9e084SEd Tanous     uint32_t retryAttempts = 0;
244d3a9e084SEd Tanous     uint32_t retryTimeoutInterval = 0;
2457d1cc387SAppaRao Puli 
2462558979cSP Dheeraj Srujan Kumar     std::streampos redfishLogFilePosition{0};
2479f616dd1SEd Tanous     size_t noOfEventLogSubscribers{0};
2489f616dd1SEd Tanous     size_t noOfMetricReportSubscribers{0};
24959d494eeSPatrick Williams     std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor;
250b52664e2SAppaRao Puli     boost::container::flat_map<std::string, std::shared_ptr<Subscription>>
251b52664e2SAppaRao Puli         subscriptionsMap;
252b52664e2SAppaRao Puli 
2539f616dd1SEd Tanous     uint64_t eventId{1};
25496330b99SSunitha Harish 
255f80a87f2SEd Tanous     struct Event
256f80a87f2SEd Tanous     {
257f80a87f2SEd Tanous         std::string id;
258f80a87f2SEd Tanous         nlohmann::json message;
259f80a87f2SEd Tanous     };
260f80a87f2SEd Tanous 
261f80a87f2SEd Tanous     constexpr static size_t maxMessages = 200;
262f80a87f2SEd Tanous     boost::circular_buffer<Event> messages{maxMessages};
263f80a87f2SEd Tanous 
264f8ca6d79SEd Tanous     boost::asio::io_context& ioc;
265f8ca6d79SEd Tanous 
266b52664e2SAppaRao Puli   public:
2679f616dd1SEd Tanous     EventServiceManager(const EventServiceManager&) = delete;
2689f616dd1SEd Tanous     EventServiceManager& operator=(const EventServiceManager&) = delete;
2699f616dd1SEd Tanous     EventServiceManager(EventServiceManager&&) = delete;
2709f616dd1SEd Tanous     EventServiceManager& operator=(EventServiceManager&&) = delete;
271ecd6a3a2SEd Tanous     ~EventServiceManager() = default;
2729f616dd1SEd Tanous 
273f8ca6d79SEd Tanous     explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn)
274b52664e2SAppaRao Puli     {
275f8ca6d79SEd Tanous         // Load config from persist store.
276f8ca6d79SEd Tanous         initConfig();
277f8ca6d79SEd Tanous     }
278f8ca6d79SEd Tanous 
279f8ca6d79SEd Tanous     static EventServiceManager&
280f8ca6d79SEd Tanous         getInstance(boost::asio::io_context* ioc = nullptr)
281f8ca6d79SEd Tanous     {
282f8ca6d79SEd Tanous         static EventServiceManager handler(*ioc);
283b52664e2SAppaRao Puli         return handler;
284b52664e2SAppaRao Puli     }
285b52664e2SAppaRao Puli 
2861bf712bcSAyushi Smriti     void initConfig()
2871bf712bcSAyushi Smriti     {
28828afb49cSJunLin Chen         loadOldBehavior();
2891bf712bcSAyushi Smriti 
29028afb49cSJunLin Chen         persistent_data::EventServiceConfig eventServiceConfig =
29128afb49cSJunLin Chen             persistent_data::EventServiceStore::getInstance()
29228afb49cSJunLin Chen                 .getEventServiceConfig();
2931bf712bcSAyushi Smriti 
29428afb49cSJunLin Chen         serviceEnabled = eventServiceConfig.enabled;
29528afb49cSJunLin Chen         retryAttempts = eventServiceConfig.retryAttempts;
29628afb49cSJunLin Chen         retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval;
2971bf712bcSAyushi Smriti 
29828afb49cSJunLin Chen         for (const auto& it : persistent_data::EventServiceStore::getInstance()
29928afb49cSJunLin Chen                                   .subscriptionsConfigMap)
3001bf712bcSAyushi Smriti         {
3015fe4ef35SMyung Bae             std::shared_ptr<persistent_data::UserSubscription> newSub =
3025fe4ef35SMyung Bae                 it.second;
3034bbf237fSAppaRao Puli 
3046fd29553SEd Tanous             boost::system::result<boost::urls::url> url =
3055fe4ef35SMyung Bae                 boost::urls::parse_absolute_uri(newSub->destinationUrl);
3061bf712bcSAyushi Smriti 
307a716aa74SEd Tanous             if (!url)
3081bf712bcSAyushi Smriti             {
30962598e31SEd Tanous                 BMCWEB_LOG_ERROR(
31062598e31SEd Tanous                     "Failed to validate and split destination url");
3111bf712bcSAyushi Smriti                 continue;
3121bf712bcSAyushi Smriti             }
3131bf712bcSAyushi Smriti             std::shared_ptr<Subscription> subValue =
31421a94d5cSMyung Bae                 std::make_shared<Subscription>(newSub, *url, ioc);
3155fe4ef35SMyung Bae             std::string id = subValue->userSub->id;
316a0969c70SMyung Bae             subValue->deleter = [id]() {
317a0969c70SMyung Bae                 EventServiceManager::getInstance().deleteSubscription(id);
318a0969c70SMyung Bae             };
3191bf712bcSAyushi Smriti 
320a0969c70SMyung Bae             subscriptionsMap.emplace(id, subValue);
32128afb49cSJunLin Chen 
32228afb49cSJunLin Chen             updateNoOfSubscribersCount();
32328afb49cSJunLin Chen 
32483328316SEd Tanous             if constexpr (!BMCWEB_REDFISH_DBUS_LOG)
32583328316SEd Tanous             {
3262558979cSP Dheeraj Srujan Kumar                 cacheRedfishLogFile();
32783328316SEd Tanous             }
3282558979cSP Dheeraj Srujan Kumar 
32928afb49cSJunLin Chen             // Update retry configuration.
33028afb49cSJunLin Chen             subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
3311bf712bcSAyushi Smriti         }
3321bf712bcSAyushi Smriti     }
3331bf712bcSAyushi Smriti 
33456d2396dSEd Tanous     static void loadOldBehavior()
335b52664e2SAppaRao Puli     {
33628afb49cSJunLin Chen         std::ifstream eventConfigFile(eventServiceFile);
33728afb49cSJunLin Chen         if (!eventConfigFile.good())
3381bf712bcSAyushi Smriti         {
33962598e31SEd Tanous             BMCWEB_LOG_DEBUG("Old eventService config not exist");
34028afb49cSJunLin Chen             return;
34128afb49cSJunLin Chen         }
34228afb49cSJunLin Chen         auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false);
34328afb49cSJunLin Chen         if (jsonData.is_discarded())
3444bbf237fSAppaRao Puli         {
34562598e31SEd Tanous             BMCWEB_LOG_ERROR("Old eventService config parse error.");
34628afb49cSJunLin Chen             return;
34728afb49cSJunLin Chen         }
34828afb49cSJunLin Chen 
3490bdda665SEd Tanous         const nlohmann::json::object_t* obj =
3500bdda665SEd Tanous             jsonData.get_ptr<const nlohmann::json::object_t*>();
3510bdda665SEd Tanous         for (const auto& item : *obj)
35228afb49cSJunLin Chen         {
3530bdda665SEd Tanous             if (item.first == "Configuration")
35428afb49cSJunLin Chen             {
35528afb49cSJunLin Chen                 persistent_data::EventServiceStore::getInstance()
35628afb49cSJunLin Chen                     .getEventServiceConfig()
3570bdda665SEd Tanous                     .fromJson(item.second);
35828afb49cSJunLin Chen             }
3590bdda665SEd Tanous             else if (item.first == "Subscriptions")
36028afb49cSJunLin Chen             {
3610bdda665SEd Tanous                 for (const auto& elem : item.second)
36228afb49cSJunLin Chen                 {
3634b712a29SEd Tanous                     std::optional<persistent_data::UserSubscription>
36428afb49cSJunLin Chen                         newSubscription =
36528afb49cSJunLin Chen                             persistent_data::UserSubscription::fromJson(elem,
36628afb49cSJunLin Chen                                                                         true);
3674b712a29SEd Tanous                     if (!newSubscription)
36828afb49cSJunLin Chen                     {
36962598e31SEd Tanous                         BMCWEB_LOG_ERROR("Problem reading subscription "
37062598e31SEd Tanous                                          "from old persistent store");
3714bbf237fSAppaRao Puli                         continue;
3724bbf237fSAppaRao Puli                     }
3734b712a29SEd Tanous                     persistent_data::UserSubscription& newSub =
3744b712a29SEd Tanous                         *newSubscription;
3751bf712bcSAyushi Smriti 
37628afb49cSJunLin Chen                     std::uniform_int_distribution<uint32_t> dist(0);
37728afb49cSJunLin Chen                     bmcweb::OpenSSLGenerator gen;
3781bf712bcSAyushi Smriti 
37928afb49cSJunLin Chen                     std::string id;
3801bf712bcSAyushi Smriti 
38128afb49cSJunLin Chen                     int retry = 3;
382e662eae8SEd Tanous                     while (retry != 0)
3831bf712bcSAyushi Smriti                     {
38428afb49cSJunLin Chen                         id = std::to_string(dist(gen));
38528afb49cSJunLin Chen                         if (gen.error())
3867d1cc387SAppaRao Puli                         {
38728afb49cSJunLin Chen                             retry = 0;
38828afb49cSJunLin Chen                             break;
38928afb49cSJunLin Chen                         }
3904b712a29SEd Tanous                         newSub.id = id;
39128afb49cSJunLin Chen                         auto inserted =
39228afb49cSJunLin Chen                             persistent_data::EventServiceStore::getInstance()
3935fe4ef35SMyung Bae                                 .subscriptionsConfigMap.insert(std::pair(
3945fe4ef35SMyung Bae                                     id, std::make_shared<
3955fe4ef35SMyung Bae                                             persistent_data::UserSubscription>(
3965fe4ef35SMyung Bae                                             newSub)));
39728afb49cSJunLin Chen                         if (inserted.second)
39828afb49cSJunLin Chen                         {
39928afb49cSJunLin Chen                             break;
40028afb49cSJunLin Chen                         }
40128afb49cSJunLin Chen                         --retry;
4027d1cc387SAppaRao Puli                     }
4037d1cc387SAppaRao Puli 
40428afb49cSJunLin Chen                     if (retry <= 0)
40528afb49cSJunLin Chen                     {
40662598e31SEd Tanous                         BMCWEB_LOG_ERROR(
40762598e31SEd Tanous                             "Failed to generate random number from old "
40862598e31SEd Tanous                             "persistent store");
40928afb49cSJunLin Chen                         continue;
41028afb49cSJunLin Chen                     }
41128afb49cSJunLin Chen                 }
41228afb49cSJunLin Chen             }
41328afb49cSJunLin Chen 
41428afb49cSJunLin Chen             persistent_data::getConfig().writeData();
4154c521c3cSEd Tanous             std::error_code ec;
4164c521c3cSEd Tanous             std::filesystem::remove(eventServiceFile, ec);
4174c521c3cSEd Tanous             if (ec)
4184c521c3cSEd Tanous             {
4194c521c3cSEd Tanous                 BMCWEB_LOG_DEBUG(
4204c521c3cSEd Tanous                     "Failed to remove old event service file.  Ignoring");
4214c521c3cSEd Tanous             }
4224c521c3cSEd Tanous             else
4234c521c3cSEd Tanous             {
42462598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Remove old eventservice config");
42528afb49cSJunLin Chen             }
42628afb49cSJunLin Chen         }
4274c521c3cSEd Tanous     }
42828afb49cSJunLin Chen 
4299eb808c1SEd Tanous     void updateSubscriptionData() const
43028afb49cSJunLin Chen     {
43128afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
43228afb49cSJunLin Chen             .eventServiceConfig.enabled = serviceEnabled;
43328afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
43428afb49cSJunLin Chen             .eventServiceConfig.retryAttempts = retryAttempts;
43528afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
43628afb49cSJunLin Chen             .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval;
43728afb49cSJunLin Chen 
43828afb49cSJunLin Chen         persistent_data::getConfig().writeData();
43928afb49cSJunLin Chen     }
44028afb49cSJunLin Chen 
44128afb49cSJunLin Chen     void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg)
4427d1cc387SAppaRao Puli     {
4437d1cc387SAppaRao Puli         bool updateConfig = false;
444fe44eb0bSAyushi Smriti         bool updateRetryCfg = false;
4457d1cc387SAppaRao Puli 
44628afb49cSJunLin Chen         if (serviceEnabled != cfg.enabled)
4477d1cc387SAppaRao Puli         {
44828afb49cSJunLin Chen             serviceEnabled = cfg.enabled;
449e662eae8SEd Tanous             if (serviceEnabled && noOfMetricReportSubscribers != 0U)
4507d1cc387SAppaRao Puli             {
4517d1cc387SAppaRao Puli                 registerMetricReportSignal();
4527d1cc387SAppaRao Puli             }
4537d1cc387SAppaRao Puli             else
4547d1cc387SAppaRao Puli             {
4557d1cc387SAppaRao Puli                 unregisterMetricReportSignal();
4567d1cc387SAppaRao Puli             }
4577d1cc387SAppaRao Puli             updateConfig = true;
4587d1cc387SAppaRao Puli         }
4597d1cc387SAppaRao Puli 
46028afb49cSJunLin Chen         if (retryAttempts != cfg.retryAttempts)
4617d1cc387SAppaRao Puli         {
46228afb49cSJunLin Chen             retryAttempts = cfg.retryAttempts;
4637d1cc387SAppaRao Puli             updateConfig = true;
464fe44eb0bSAyushi Smriti             updateRetryCfg = true;
4657d1cc387SAppaRao Puli         }
4667d1cc387SAppaRao Puli 
46728afb49cSJunLin Chen         if (retryTimeoutInterval != cfg.retryTimeoutInterval)
4687d1cc387SAppaRao Puli         {
46928afb49cSJunLin Chen             retryTimeoutInterval = cfg.retryTimeoutInterval;
4707d1cc387SAppaRao Puli             updateConfig = true;
471fe44eb0bSAyushi Smriti             updateRetryCfg = true;
4727d1cc387SAppaRao Puli         }
4737d1cc387SAppaRao Puli 
4747d1cc387SAppaRao Puli         if (updateConfig)
4757d1cc387SAppaRao Puli         {
4767d1cc387SAppaRao Puli             updateSubscriptionData();
4777d1cc387SAppaRao Puli         }
478fe44eb0bSAyushi Smriti 
479fe44eb0bSAyushi Smriti         if (updateRetryCfg)
480fe44eb0bSAyushi Smriti         {
481fe44eb0bSAyushi Smriti             // Update the changed retry config to all subscriptions
482fe44eb0bSAyushi Smriti             for (const auto& it :
483fe44eb0bSAyushi Smriti                  EventServiceManager::getInstance().subscriptionsMap)
484fe44eb0bSAyushi Smriti             {
4855e44e3d8SAppaRao Puli                 Subscription& entry = *it.second;
4865e44e3d8SAppaRao Puli                 entry.updateRetryConfig(retryAttempts, retryTimeoutInterval);
487fe44eb0bSAyushi Smriti             }
488fe44eb0bSAyushi Smriti         }
4897d1cc387SAppaRao Puli     }
4907d1cc387SAppaRao Puli 
4917d1cc387SAppaRao Puli     void updateNoOfSubscribersCount()
4927d1cc387SAppaRao Puli     {
4937d1cc387SAppaRao Puli         size_t eventLogSubCount = 0;
4947d1cc387SAppaRao Puli         size_t metricReportSubCount = 0;
4957d1cc387SAppaRao Puli         for (const auto& it : subscriptionsMap)
4967d1cc387SAppaRao Puli         {
4977d1cc387SAppaRao Puli             std::shared_ptr<Subscription> entry = it.second;
4985fe4ef35SMyung Bae             if (entry->userSub->eventFormatType == eventFormatType)
4997d1cc387SAppaRao Puli             {
5007d1cc387SAppaRao Puli                 eventLogSubCount++;
5017d1cc387SAppaRao Puli             }
5025fe4ef35SMyung Bae             else if (entry->userSub->eventFormatType == metricReportFormatType)
5037d1cc387SAppaRao Puli             {
5047d1cc387SAppaRao Puli                 metricReportSubCount++;
5057d1cc387SAppaRao Puli             }
5067d1cc387SAppaRao Puli         }
5077d1cc387SAppaRao Puli 
5087d1cc387SAppaRao Puli         noOfEventLogSubscribers = eventLogSubCount;
5097d1cc387SAppaRao Puli         if (noOfMetricReportSubscribers != metricReportSubCount)
5107d1cc387SAppaRao Puli         {
5117d1cc387SAppaRao Puli             noOfMetricReportSubscribers = metricReportSubCount;
512e662eae8SEd Tanous             if (noOfMetricReportSubscribers != 0U)
5137d1cc387SAppaRao Puli             {
5147d1cc387SAppaRao Puli                 registerMetricReportSignal();
5157d1cc387SAppaRao Puli             }
5167d1cc387SAppaRao Puli             else
5177d1cc387SAppaRao Puli             {
5187d1cc387SAppaRao Puli                 unregisterMetricReportSignal();
5197d1cc387SAppaRao Puli             }
5207d1cc387SAppaRao Puli         }
5217d1cc387SAppaRao Puli     }
5227d1cc387SAppaRao Puli 
523b52664e2SAppaRao Puli     std::shared_ptr<Subscription> getSubscription(const std::string& id)
524b52664e2SAppaRao Puli     {
525b52664e2SAppaRao Puli         auto obj = subscriptionsMap.find(id);
526b52664e2SAppaRao Puli         if (obj == subscriptionsMap.end())
527b52664e2SAppaRao Puli         {
52862598e31SEd Tanous             BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id);
529b52664e2SAppaRao Puli             return nullptr;
530b52664e2SAppaRao Puli         }
531b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue = obj->second;
532b52664e2SAppaRao Puli         return subValue;
533b52664e2SAppaRao Puli     }
534b52664e2SAppaRao Puli 
535f80a87f2SEd Tanous     std::string
536f80a87f2SEd Tanous         addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue)
537b52664e2SAppaRao Puli     {
538fc76b8acSEd Tanous         std::uniform_int_distribution<uint32_t> dist(0);
539fc76b8acSEd Tanous         bmcweb::OpenSSLGenerator gen;
540fc76b8acSEd Tanous 
541b52664e2SAppaRao Puli         std::string id;
542b52664e2SAppaRao Puli 
543b52664e2SAppaRao Puli         int retry = 3;
544e662eae8SEd Tanous         while (retry != 0)
545b52664e2SAppaRao Puli         {
546fc76b8acSEd Tanous             id = std::to_string(dist(gen));
547fc76b8acSEd Tanous             if (gen.error())
548fc76b8acSEd Tanous             {
549fc76b8acSEd Tanous                 retry = 0;
550fc76b8acSEd Tanous                 break;
551fc76b8acSEd Tanous             }
552b52664e2SAppaRao Puli             auto inserted = subscriptionsMap.insert(std::pair(id, subValue));
553b52664e2SAppaRao Puli             if (inserted.second)
554b52664e2SAppaRao Puli             {
555b52664e2SAppaRao Puli                 break;
556b52664e2SAppaRao Puli             }
557b52664e2SAppaRao Puli             --retry;
55823a21a1cSEd Tanous         }
559b52664e2SAppaRao Puli 
560b52664e2SAppaRao Puli         if (retry <= 0)
561b52664e2SAppaRao Puli         {
56262598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to generate random number");
563abb93cddSEd Tanous             return "";
564b52664e2SAppaRao Puli         }
565b52664e2SAppaRao Puli 
56656ba386dSMyung Bae         // Set Subscription ID for back trace
5675fe4ef35SMyung Bae         subValue->userSub->id = id;
568a14c9113SEd Tanous 
56928afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
5705fe4ef35SMyung Bae             .subscriptionsConfigMap.emplace(id, subValue->userSub);
57128afb49cSJunLin Chen 
5727d1cc387SAppaRao Puli         updateNoOfSubscribersCount();
5731bf712bcSAyushi Smriti 
57483328316SEd Tanous         if constexpr (!BMCWEB_REDFISH_DBUS_LOG)
57583328316SEd Tanous         {
5762558979cSP Dheeraj Srujan Kumar             if (redfishLogFilePosition != 0)
5777f4eb588SAppaRao Puli             {
5782558979cSP Dheeraj Srujan Kumar                 cacheRedfishLogFile();
5797f4eb588SAppaRao Puli             }
58083328316SEd Tanous         }
581fe44eb0bSAyushi Smriti         // Update retry configuration.
582fe44eb0bSAyushi Smriti         subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
583fe44eb0bSAyushi Smriti 
584f80a87f2SEd Tanous         return id;
585f80a87f2SEd Tanous     }
586f80a87f2SEd Tanous 
587f80a87f2SEd Tanous     std::string
588f80a87f2SEd Tanous         addSSESubscription(const std::shared_ptr<Subscription>& subValue,
589f80a87f2SEd Tanous                            std::string_view lastEventId)
590f80a87f2SEd Tanous     {
591f80a87f2SEd Tanous         std::string id = addSubscriptionInternal(subValue);
592f80a87f2SEd Tanous 
593f80a87f2SEd Tanous         if (!lastEventId.empty())
594f80a87f2SEd Tanous         {
595f80a87f2SEd Tanous             BMCWEB_LOG_INFO("Attempting to find message for last id {}",
596f80a87f2SEd Tanous                             lastEventId);
597f80a87f2SEd Tanous             boost::circular_buffer<Event>::iterator lastEvent =
598f80a87f2SEd Tanous                 std::find_if(messages.begin(), messages.end(),
599f80a87f2SEd Tanous                              [&lastEventId](const Event& event) {
600f80a87f2SEd Tanous                                  return event.id == lastEventId;
601f80a87f2SEd Tanous                              });
602f80a87f2SEd Tanous             // Can't find a matching ID
603f80a87f2SEd Tanous             if (lastEvent == messages.end())
604f80a87f2SEd Tanous             {
605f80a87f2SEd Tanous                 nlohmann::json msg = messages::eventBufferExceeded();
606f80a87f2SEd Tanous                 // If the buffer overloaded, send all messages.
6076d799e14SEd Tanous                 subValue->sendEventToSubscriber(msg);
608f80a87f2SEd Tanous                 lastEvent = messages.begin();
609f80a87f2SEd Tanous             }
610f80a87f2SEd Tanous             else
611f80a87f2SEd Tanous             {
612f80a87f2SEd Tanous                 // Skip the last event the user already has
613f80a87f2SEd Tanous                 lastEvent++;
614f80a87f2SEd Tanous             }
615f80a87f2SEd Tanous 
616f80a87f2SEd Tanous             for (boost::circular_buffer<Event>::const_iterator event =
617f80a87f2SEd Tanous                      lastEvent;
618f80a87f2SEd Tanous                  lastEvent != messages.end(); lastEvent++)
619f80a87f2SEd Tanous             {
6206d799e14SEd Tanous                 subValue->sendEventToSubscriber(event->message);
621f80a87f2SEd Tanous             }
622f80a87f2SEd Tanous         }
623f80a87f2SEd Tanous         return id;
624f80a87f2SEd Tanous     }
625f80a87f2SEd Tanous 
626f80a87f2SEd Tanous     std::string
627f80a87f2SEd Tanous         addPushSubscription(const std::shared_ptr<Subscription>& subValue)
628f80a87f2SEd Tanous     {
629f80a87f2SEd Tanous         std::string id = addSubscriptionInternal(subValue);
630a0969c70SMyung Bae         subValue->deleter = [id]() {
631a0969c70SMyung Bae             EventServiceManager::getInstance().deleteSubscription(id);
632a0969c70SMyung Bae         };
633f80a87f2SEd Tanous         updateSubscriptionData();
634b52664e2SAppaRao Puli         return id;
635b52664e2SAppaRao Puli     }
636b52664e2SAppaRao Puli 
637b52664e2SAppaRao Puli     bool isSubscriptionExist(const std::string& id)
638b52664e2SAppaRao Puli     {
639b52664e2SAppaRao Puli         auto obj = subscriptionsMap.find(id);
64055f79e6fSEd Tanous         return obj != subscriptionsMap.end();
641b52664e2SAppaRao Puli     }
642b52664e2SAppaRao Puli 
6434b712a29SEd Tanous     bool deleteSubscription(const std::string& id)
644b52664e2SAppaRao Puli     {
645b52664e2SAppaRao Puli         auto obj = subscriptionsMap.find(id);
6464b712a29SEd Tanous         if (obj == subscriptionsMap.end())
647b52664e2SAppaRao Puli         {
6484b712a29SEd Tanous             BMCWEB_LOG_WARNING("Could not find subscription with id {}", id);
6494b712a29SEd Tanous             return false;
6504b712a29SEd Tanous         }
651b52664e2SAppaRao Puli         subscriptionsMap.erase(obj);
6524b712a29SEd Tanous         auto& event = persistent_data::EventServiceStore::getInstance();
6534b712a29SEd Tanous         auto persistentObj = event.subscriptionsConfigMap.find(id);
6544b712a29SEd Tanous         if (persistentObj == event.subscriptionsConfigMap.end())
6554b712a29SEd Tanous         {
6564b712a29SEd Tanous             BMCWEB_LOG_ERROR("Subscription wasn't in persistent data");
6574b712a29SEd Tanous             return true;
6584b712a29SEd Tanous         }
65928afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
6604b712a29SEd Tanous             .subscriptionsConfigMap.erase(persistentObj);
6617d1cc387SAppaRao Puli         updateNoOfSubscribersCount();
662b52664e2SAppaRao Puli         updateSubscriptionData();
6634b712a29SEd Tanous 
6644b712a29SEd Tanous         return true;
665b52664e2SAppaRao Puli     }
666b52664e2SAppaRao Puli 
6675e44e3d8SAppaRao Puli     void deleteSseSubscription(const crow::sse_socket::Connection& thisConn)
6685e44e3d8SAppaRao Puli     {
669bdbfae2aSEd Tanous         for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();)
6705e44e3d8SAppaRao Puli         {
671bdbfae2aSEd Tanous             std::shared_ptr<Subscription> entry = it->second;
6725e44e3d8SAppaRao Puli             bool entryIsThisConn = entry->matchSseId(thisConn);
6735e44e3d8SAppaRao Puli             if (entryIsThisConn)
6745e44e3d8SAppaRao Puli             {
6755e44e3d8SAppaRao Puli                 persistent_data::EventServiceStore::getInstance()
6765fe4ef35SMyung Bae                     .subscriptionsConfigMap.erase(entry->userSub->id);
677bdbfae2aSEd Tanous                 it = subscriptionsMap.erase(it);
6785e44e3d8SAppaRao Puli                 return;
6795e44e3d8SAppaRao Puli             }
680bdbfae2aSEd Tanous             it++;
6815e44e3d8SAppaRao Puli         }
6825e44e3d8SAppaRao Puli     }
6835e44e3d8SAppaRao Puli 
6845e44e3d8SAppaRao Puli     size_t getNumberOfSubscriptions() const
685b52664e2SAppaRao Puli     {
686b52664e2SAppaRao Puli         return subscriptionsMap.size();
687b52664e2SAppaRao Puli     }
688b52664e2SAppaRao Puli 
6895e44e3d8SAppaRao Puli     size_t getNumberOfSSESubscriptions() const
6905e44e3d8SAppaRao Puli     {
6913544d2a7SEd Tanous         auto size = std::ranges::count_if(
6923544d2a7SEd Tanous             subscriptionsMap,
6935e44e3d8SAppaRao Puli             [](const std::pair<std::string, std::shared_ptr<Subscription>>&
6945e44e3d8SAppaRao Puli                    entry) {
6955fe4ef35SMyung Bae                 return (entry.second->userSub->subscriptionType ==
6964b712a29SEd Tanous                         subscriptionTypeSSE);
6975e44e3d8SAppaRao Puli             });
6985e44e3d8SAppaRao Puli         return static_cast<size_t>(size);
6995e44e3d8SAppaRao Puli     }
7005e44e3d8SAppaRao Puli 
701b52664e2SAppaRao Puli     std::vector<std::string> getAllIDs()
702b52664e2SAppaRao Puli     {
703b52664e2SAppaRao Puli         std::vector<std::string> idList;
704b52664e2SAppaRao Puli         for (const auto& it : subscriptionsMap)
705b52664e2SAppaRao Puli         {
706b52664e2SAppaRao Puli             idList.emplace_back(it.first);
707b52664e2SAppaRao Puli         }
708b52664e2SAppaRao Puli         return idList;
709b52664e2SAppaRao Puli     }
710b52664e2SAppaRao Puli 
7116ba8c82eSsunharis_in     bool sendTestEventLog()
7120b4bdd93SAppaRao Puli     {
7135e44e3d8SAppaRao Puli         for (const auto& it : subscriptionsMap)
7140b4bdd93SAppaRao Puli         {
7150b4bdd93SAppaRao Puli             std::shared_ptr<Subscription> entry = it.second;
7166ba8c82eSsunharis_in             if (!entry->sendTestEventLog())
7176ba8c82eSsunharis_in             {
7186ba8c82eSsunharis_in                 return false;
7190b4bdd93SAppaRao Puli             }
7200b4bdd93SAppaRao Puli         }
7216ba8c82eSsunharis_in         return true;
7226ba8c82eSsunharis_in     }
723e9a14131SAppaRao Puli 
724f80a87f2SEd Tanous     void sendEvent(nlohmann::json::object_t eventMessage,
725f80a87f2SEd Tanous                    std::string_view origin, std::string_view resourceType)
72696330b99SSunitha Harish     {
727613dabeaSEd Tanous         eventMessage["EventId"] = eventId;
728f80a87f2SEd Tanous 
729613dabeaSEd Tanous         eventMessage["EventTimestamp"] =
730613dabeaSEd Tanous             redfish::time_utils::getDateTimeOffsetNow().first;
731613dabeaSEd Tanous         eventMessage["OriginOfCondition"] = origin;
732613dabeaSEd Tanous 
733f80a87f2SEd Tanous         // MemberId is 0 : since we are sending one event record.
734788b091bSIgor Kanyuka         eventMessage["MemberId"] = "0";
73596330b99SSunitha Harish 
736f80a87f2SEd Tanous         messages.push_back(Event(std::to_string(eventId), eventMessage));
737f80a87f2SEd Tanous 
738f80a87f2SEd Tanous         for (auto& it : subscriptionsMap)
73996330b99SSunitha Harish         {
740f80a87f2SEd Tanous             std::shared_ptr<Subscription>& entry = it.second;
7415fe4ef35SMyung Bae             if (!eventMatchesFilter(*entry->userSub, eventMessage,
7425fe4ef35SMyung Bae                                     resourceType))
74396330b99SSunitha Harish             {
744f80a87f2SEd Tanous                 BMCWEB_LOG_DEBUG("Filter didn't match");
745f80a87f2SEd Tanous                 continue;
74696330b99SSunitha Harish             }
747f80a87f2SEd Tanous 
748f80a87f2SEd Tanous             nlohmann::json::array_t eventRecord;
749f80a87f2SEd Tanous             eventRecord.emplace_back(eventMessage);
750f80a87f2SEd Tanous 
751613dabeaSEd Tanous             nlohmann::json msgJson;
752613dabeaSEd Tanous 
753613dabeaSEd Tanous             msgJson["@odata.type"] = "#Event.v1_4_0.Event";
754613dabeaSEd Tanous             msgJson["Name"] = "Event Log";
755613dabeaSEd Tanous             msgJson["Id"] = eventId;
756f80a87f2SEd Tanous             msgJson["Events"] = std::move(eventRecord);
757f52c03c1SCarson Labrado 
758f52c03c1SCarson Labrado             std::string strMsg = msgJson.dump(
759f52c03c1SCarson Labrado                 2, ' ', true, nlohmann::json::error_handler_t::replace);
7606d799e14SEd Tanous             entry->sendEventToSubscriber(std::move(strMsg));
7618ece0e45SEd Tanous             eventId++; // increment the eventId
76296330b99SSunitha Harish         }
76396330b99SSunitha Harish     }
76496330b99SSunitha Harish 
7652558979cSP Dheeraj Srujan Kumar     void resetRedfishFilePosition()
7667f4eb588SAppaRao Puli     {
7672558979cSP Dheeraj Srujan Kumar         // Control would be here when Redfish file is created.
7682558979cSP Dheeraj Srujan Kumar         // Reset File Position as new file is created
7692558979cSP Dheeraj Srujan Kumar         redfishLogFilePosition = 0;
7702558979cSP Dheeraj Srujan Kumar     }
7712558979cSP Dheeraj Srujan Kumar 
7722558979cSP Dheeraj Srujan Kumar     void cacheRedfishLogFile()
7732558979cSP Dheeraj Srujan Kumar     {
7742558979cSP Dheeraj Srujan Kumar         // Open the redfish file and read till the last record.
7752558979cSP Dheeraj Srujan Kumar 
7767f4eb588SAppaRao Puli         std::ifstream logStream(redfishEventLogFile);
7777f4eb588SAppaRao Puli         if (!logStream.good())
7787f4eb588SAppaRao Puli         {
77962598e31SEd Tanous             BMCWEB_LOG_ERROR(" Redfish log file open failed ");
7807f4eb588SAppaRao Puli             return;
7817f4eb588SAppaRao Puli         }
7827f4eb588SAppaRao Puli         std::string logEntry;
7837f4eb588SAppaRao Puli         while (std::getline(logStream, logEntry))
7847f4eb588SAppaRao Puli         {
7852558979cSP Dheeraj Srujan Kumar             redfishLogFilePosition = logStream.tellg();
7867f4eb588SAppaRao Puli         }
7877f4eb588SAppaRao Puli     }
7887f4eb588SAppaRao Puli 
7897f4eb588SAppaRao Puli     void readEventLogsFromFile()
7907f4eb588SAppaRao Puli     {
7917f4eb588SAppaRao Puli         std::ifstream logStream(redfishEventLogFile);
7927f4eb588SAppaRao Puli         if (!logStream.good())
7937f4eb588SAppaRao Puli         {
79462598e31SEd Tanous             BMCWEB_LOG_ERROR(" Redfish log file open failed");
7957f4eb588SAppaRao Puli             return;
7967f4eb588SAppaRao Puli         }
7977f4eb588SAppaRao Puli 
7987f4eb588SAppaRao Puli         std::vector<EventLogObjectsType> eventRecords;
7997f4eb588SAppaRao Puli 
8007f4eb588SAppaRao Puli         std::string logEntry;
8012558979cSP Dheeraj Srujan Kumar 
80203d4d37cSAlexander Hansen         BMCWEB_LOG_DEBUG("Redfish log file: seek to {}",
80303d4d37cSAlexander Hansen                          static_cast<int>(redfishLogFilePosition));
80403d4d37cSAlexander Hansen 
8052558979cSP Dheeraj Srujan Kumar         // Get the read pointer to the next log to be read.
8062558979cSP Dheeraj Srujan Kumar         logStream.seekg(redfishLogFilePosition);
8072558979cSP Dheeraj Srujan Kumar 
8087f4eb588SAppaRao Puli         while (std::getline(logStream, logEntry))
8097f4eb588SAppaRao Puli         {
81003d4d37cSAlexander Hansen             BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry");
8112558979cSP Dheeraj Srujan Kumar             // Update Pointer position
8122558979cSP Dheeraj Srujan Kumar             redfishLogFilePosition = logStream.tellg();
8132558979cSP Dheeraj Srujan Kumar 
8142558979cSP Dheeraj Srujan Kumar             std::string idStr;
8152558979cSP Dheeraj Srujan Kumar             if (!event_log::getUniqueEntryID(logEntry, idStr))
8167f4eb588SAppaRao Puli             {
81703d4d37cSAlexander Hansen                 BMCWEB_LOG_DEBUG(
81803d4d37cSAlexander Hansen                     "Redfish log file: could not get unique entry id for {}",
81903d4d37cSAlexander Hansen                     logEntry);
8207f4eb588SAppaRao Puli                 continue;
8217f4eb588SAppaRao Puli             }
8227f4eb588SAppaRao Puli 
823e662eae8SEd Tanous             if (!serviceEnabled || noOfEventLogSubscribers == 0)
8247f4eb588SAppaRao Puli             {
8252558979cSP Dheeraj Srujan Kumar                 // If Service is not enabled, no need to compute
8262558979cSP Dheeraj Srujan Kumar                 // the remaining items below.
8272558979cSP Dheeraj Srujan Kumar                 // But, Loop must continue to keep track of Timestamp
82803d4d37cSAlexander Hansen                 BMCWEB_LOG_DEBUG(
82903d4d37cSAlexander Hansen                     "Redfish log file: no subscribers / event service not enabled");
8307f4eb588SAppaRao Puli                 continue;
8317f4eb588SAppaRao Puli             }
8327f4eb588SAppaRao Puli 
8337f4eb588SAppaRao Puli             std::string timestamp;
8347f4eb588SAppaRao Puli             std::string messageID;
8355e715de6SAppaRao Puli             std::vector<std::string> messageArgs;
8367f4eb588SAppaRao Puli             if (event_log::getEventLogParams(logEntry, timestamp, messageID,
8377f4eb588SAppaRao Puli                                              messageArgs) != 0)
8387f4eb588SAppaRao Puli             {
83903d4d37cSAlexander Hansen                 BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}",
84003d4d37cSAlexander Hansen                                  logEntry);
8417f4eb588SAppaRao Puli                 continue;
8427f4eb588SAppaRao Puli             }
8437f4eb588SAppaRao Puli 
844f80a87f2SEd Tanous             eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs);
8457f4eb588SAppaRao Puli         }
8467f4eb588SAppaRao Puli 
847e662eae8SEd Tanous         if (!serviceEnabled || noOfEventLogSubscribers == 0)
8482558979cSP Dheeraj Srujan Kumar         {
84962598e31SEd Tanous             BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions.");
8502558979cSP Dheeraj Srujan Kumar             return;
8512558979cSP Dheeraj Srujan Kumar         }
8522558979cSP Dheeraj Srujan Kumar 
8532558979cSP Dheeraj Srujan Kumar         if (eventRecords.empty())
8542558979cSP Dheeraj Srujan Kumar         {
8552558979cSP Dheeraj Srujan Kumar             // No Records to send
85662598e31SEd Tanous             BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
8572558979cSP Dheeraj Srujan Kumar             return;
8582558979cSP Dheeraj Srujan Kumar         }
8592558979cSP Dheeraj Srujan Kumar 
8605e44e3d8SAppaRao Puli         for (const auto& it : subscriptionsMap)
8617f4eb588SAppaRao Puli         {
8627f4eb588SAppaRao Puli             std::shared_ptr<Subscription> entry = it.second;
8635fe4ef35SMyung Bae             if (entry->userSub->eventFormatType == "Event")
8647f4eb588SAppaRao Puli             {
8657f4eb588SAppaRao Puli                 entry->filterAndSendEventLogs(eventRecords);
8667f4eb588SAppaRao Puli             }
8677f4eb588SAppaRao Puli         }
8687f4eb588SAppaRao Puli     }
8697f4eb588SAppaRao Puli 
8707f4eb588SAppaRao Puli     static void watchRedfishEventLogFile()
8717f4eb588SAppaRao Puli     {
8726a9f85f9SAppaRao Puli         if (!inotifyConn)
8737f4eb588SAppaRao Puli         {
87403d4d37cSAlexander Hansen             BMCWEB_LOG_ERROR("inotify Connection is not present");
8757f4eb588SAppaRao Puli             return;
8767f4eb588SAppaRao Puli         }
8777f4eb588SAppaRao Puli 
8787f4eb588SAppaRao Puli         static std::array<char, 1024> readBuffer;
8797f4eb588SAppaRao Puli 
880bd79bce8SPatrick Williams         inotifyConn->async_read_some(
881bd79bce8SPatrick Williams             boost::asio::buffer(readBuffer),
8827f4eb588SAppaRao Puli             [&](const boost::system::error_code& ec,
8837f4eb588SAppaRao Puli                 const std::size_t& bytesTransferred) {
8849ed3f90aSEd Tanous                 if (ec == boost::asio::error::operation_aborted)
8859ed3f90aSEd Tanous                 {
8869ed3f90aSEd Tanous                     BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
8879ed3f90aSEd Tanous                     return;
8889ed3f90aSEd Tanous                 }
8897f4eb588SAppaRao Puli                 if (ec)
8907f4eb588SAppaRao Puli                 {
89162598e31SEd Tanous                     BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
8927f4eb588SAppaRao Puli                     return;
8937f4eb588SAppaRao Puli                 }
89403d4d37cSAlexander Hansen 
89503d4d37cSAlexander Hansen                 BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
89603d4d37cSAlexander Hansen 
8977f4eb588SAppaRao Puli                 std::size_t index = 0;
898b792cc56SAppaRao Puli                 while ((index + iEventSize) <= bytesTransferred)
8997f4eb588SAppaRao Puli                 {
900d3a9e084SEd Tanous                     struct inotify_event event
901d3a9e084SEd Tanous                     {};
902b792cc56SAppaRao Puli                     std::memcpy(&event, &readBuffer[index], iEventSize);
903b792cc56SAppaRao Puli                     if (event.wd == dirWatchDesc)
904b792cc56SAppaRao Puli                     {
905b792cc56SAppaRao Puli                         if ((event.len == 0) ||
906b792cc56SAppaRao Puli                             (index + iEventSize + event.len > bytesTransferred))
907b792cc56SAppaRao Puli                         {
908b792cc56SAppaRao Puli                             index += (iEventSize + event.len);
909b792cc56SAppaRao Puli                             continue;
910b792cc56SAppaRao Puli                         }
911b792cc56SAppaRao Puli 
9124f568f74SJiaqing Zhao                         std::string fileName(&readBuffer[index + iEventSize]);
9134f568f74SJiaqing Zhao                         if (fileName != "redfish")
914b792cc56SAppaRao Puli                         {
915b792cc56SAppaRao Puli                             index += (iEventSize + event.len);
916b792cc56SAppaRao Puli                             continue;
917b792cc56SAppaRao Puli                         }
918b792cc56SAppaRao Puli 
91962598e31SEd Tanous                         BMCWEB_LOG_DEBUG(
92062598e31SEd Tanous                             "Redfish log file created/deleted. event.name: {}",
92162598e31SEd Tanous                             fileName);
922b792cc56SAppaRao Puli                         if (event.mask == IN_CREATE)
923b792cc56SAppaRao Puli                         {
924b792cc56SAppaRao Puli                             if (fileWatchDesc != -1)
925b792cc56SAppaRao Puli                             {
92662598e31SEd Tanous                                 BMCWEB_LOG_DEBUG(
92762598e31SEd Tanous                                     "Remove and Add inotify watcher on "
92862598e31SEd Tanous                                     "redfish event log file");
929016761afSAppaRao Puli                                 // Remove existing inotify watcher and add
930016761afSAppaRao Puli                                 // with new redfish event log file.
931016761afSAppaRao Puli                                 inotify_rm_watch(inotifyFd, fileWatchDesc);
932016761afSAppaRao Puli                                 fileWatchDesc = -1;
933b792cc56SAppaRao Puli                             }
934b792cc56SAppaRao Puli 
935b792cc56SAppaRao Puli                             fileWatchDesc = inotify_add_watch(
936b792cc56SAppaRao Puli                                 inotifyFd, redfishEventLogFile, IN_MODIFY);
937b792cc56SAppaRao Puli                             if (fileWatchDesc == -1)
938b792cc56SAppaRao Puli                             {
93962598e31SEd Tanous                                 BMCWEB_LOG_ERROR("inotify_add_watch failed for "
94062598e31SEd Tanous                                                  "redfish log file.");
941b792cc56SAppaRao Puli                                 return;
942b792cc56SAppaRao Puli                             }
943b792cc56SAppaRao Puli 
944b792cc56SAppaRao Puli                             EventServiceManager::getInstance()
9452558979cSP Dheeraj Srujan Kumar                                 .resetRedfishFilePosition();
946b792cc56SAppaRao Puli                             EventServiceManager::getInstance()
947b792cc56SAppaRao Puli                                 .readEventLogsFromFile();
948b792cc56SAppaRao Puli                         }
949b792cc56SAppaRao Puli                         else if ((event.mask == IN_DELETE) ||
950b792cc56SAppaRao Puli                                  (event.mask == IN_MOVED_TO))
951b792cc56SAppaRao Puli                         {
952b792cc56SAppaRao Puli                             if (fileWatchDesc != -1)
953b792cc56SAppaRao Puli                             {
954b792cc56SAppaRao Puli                                 inotify_rm_watch(inotifyFd, fileWatchDesc);
955b792cc56SAppaRao Puli                                 fileWatchDesc = -1;
956b792cc56SAppaRao Puli                             }
957b792cc56SAppaRao Puli                         }
958b792cc56SAppaRao Puli                     }
959b792cc56SAppaRao Puli                     else if (event.wd == fileWatchDesc)
960b792cc56SAppaRao Puli                     {
961b792cc56SAppaRao Puli                         if (event.mask == IN_MODIFY)
9627f4eb588SAppaRao Puli                         {
9637f4eb588SAppaRao Puli                             EventServiceManager::getInstance()
9647f4eb588SAppaRao Puli                                 .readEventLogsFromFile();
9657f4eb588SAppaRao Puli                         }
966b792cc56SAppaRao Puli                     }
967b792cc56SAppaRao Puli                     index += (iEventSize + event.len);
9687f4eb588SAppaRao Puli                 }
9697f4eb588SAppaRao Puli 
9707f4eb588SAppaRao Puli                 watchRedfishEventLogFile();
9717f4eb588SAppaRao Puli             });
9727f4eb588SAppaRao Puli     }
9737f4eb588SAppaRao Puli 
9747f4eb588SAppaRao Puli     static int startEventLogMonitor(boost::asio::io_context& ioc)
9757f4eb588SAppaRao Puli     {
97603d4d37cSAlexander Hansen         BMCWEB_LOG_DEBUG("starting Event Log Monitor");
97703d4d37cSAlexander Hansen 
97823a21a1cSEd Tanous         inotifyConn.emplace(ioc);
979b792cc56SAppaRao Puli         inotifyFd = inotify_init1(IN_NONBLOCK);
980b792cc56SAppaRao Puli         if (inotifyFd == -1)
9817f4eb588SAppaRao Puli         {
98262598e31SEd Tanous             BMCWEB_LOG_ERROR("inotify_init1 failed.");
9837f4eb588SAppaRao Puli             return -1;
9847f4eb588SAppaRao Puli         }
985b792cc56SAppaRao Puli 
986b792cc56SAppaRao Puli         // Add watch on directory to handle redfish event log file
987b792cc56SAppaRao Puli         // create/delete.
988b792cc56SAppaRao Puli         dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
989b792cc56SAppaRao Puli                                          IN_CREATE | IN_MOVED_TO | IN_DELETE);
990b792cc56SAppaRao Puli         if (dirWatchDesc == -1)
9917f4eb588SAppaRao Puli         {
99262598e31SEd Tanous             BMCWEB_LOG_ERROR(
99362598e31SEd Tanous                 "inotify_add_watch failed for event log directory.");
9947f4eb588SAppaRao Puli             return -1;
9957f4eb588SAppaRao Puli         }
9967f4eb588SAppaRao Puli 
997b792cc56SAppaRao Puli         // Watch redfish event log file for modifications.
998bd79bce8SPatrick Williams         fileWatchDesc =
999bd79bce8SPatrick Williams             inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
1000b792cc56SAppaRao Puli         if (fileWatchDesc == -1)
1001b792cc56SAppaRao Puli         {
100262598e31SEd Tanous             BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
1003b792cc56SAppaRao Puli             // Don't return error if file not exist.
1004b792cc56SAppaRao Puli             // Watch on directory will handle create/delete of file.
1005b792cc56SAppaRao Puli         }
1006b792cc56SAppaRao Puli 
10077f4eb588SAppaRao Puli         // monitor redfish event log file
1008b792cc56SAppaRao Puli         inotifyConn->assign(inotifyFd);
10097f4eb588SAppaRao Puli         watchRedfishEventLogFile();
10107f4eb588SAppaRao Puli 
10117f4eb588SAppaRao Puli         return 0;
10127f4eb588SAppaRao Puli     }
10137f4eb588SAppaRao Puli 
10149ed3f90aSEd Tanous     static void stopEventLogMonitor()
10159ed3f90aSEd Tanous     {
10169ed3f90aSEd Tanous         inotifyConn.reset();
10179ed3f90aSEd Tanous     }
10189ed3f90aSEd Tanous 
101959d494eeSPatrick Williams     static void getReadingsForReport(sdbusplus::message_t& msg)
1020156d6b00SAppaRao Puli     {
102156d2396dSEd Tanous         if (msg.is_method_error())
102256d2396dSEd Tanous         {
102362598e31SEd Tanous             BMCWEB_LOG_ERROR("TelemetryMonitor Signal error");
102456d2396dSEd Tanous             return;
102556d2396dSEd Tanous         }
102656d2396dSEd Tanous 
1027c0353249SWludzik, Jozef         sdbusplus::message::object_path path(msg.get_path());
1028c0353249SWludzik, Jozef         std::string id = path.filename();
1029c0353249SWludzik, Jozef         if (id.empty())
1030156d6b00SAppaRao Puli         {
103162598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to get Id from path");
1032156d6b00SAppaRao Puli             return;
1033156d6b00SAppaRao Puli         }
1034156d6b00SAppaRao Puli 
1035c0353249SWludzik, Jozef         std::string interface;
1036b9d36b47SEd Tanous         dbus::utility::DBusPropertiesMap props;
1037c0353249SWludzik, Jozef         std::vector<std::string> invalidProps;
1038c0353249SWludzik, Jozef         msg.read(interface, props, invalidProps);
1039c0353249SWludzik, Jozef 
1040bd79bce8SPatrick Williams         auto found = std::ranges::find_if(props, [](const auto& x) {
1041bd79bce8SPatrick Williams             return x.first == "Readings";
1042bd79bce8SPatrick Williams         });
1043c0353249SWludzik, Jozef         if (found == props.end())
1044156d6b00SAppaRao Puli         {
104562598e31SEd Tanous             BMCWEB_LOG_INFO("Failed to get Readings from Report properties");
1046156d6b00SAppaRao Puli             return;
1047156d6b00SAppaRao Puli         }
1048156d6b00SAppaRao Puli 
10491e1e598dSJonathan Doman         const telemetry::TimestampReadings* readings =
10501e1e598dSJonathan Doman             std::get_if<telemetry::TimestampReadings>(&found->second);
1051e662eae8SEd Tanous         if (readings == nullptr)
10521e1e598dSJonathan Doman         {
105362598e31SEd Tanous             BMCWEB_LOG_INFO("Failed to get Readings from Report properties");
10541e1e598dSJonathan Doman             return;
10551e1e598dSJonathan Doman         }
10561e1e598dSJonathan Doman 
1057156d6b00SAppaRao Puli         for (const auto& it :
1058156d6b00SAppaRao Puli              EventServiceManager::getInstance().subscriptionsMap)
1059156d6b00SAppaRao Puli         {
1060e05aec50SEd Tanous             Subscription& entry = *it.second;
10615fe4ef35SMyung Bae             if (entry.userSub->eventFormatType == metricReportFormatType)
1062156d6b00SAppaRao Puli             {
10631e1e598dSJonathan Doman                 entry.filterAndSendReports(id, *readings);
1064156d6b00SAppaRao Puli             }
1065156d6b00SAppaRao Puli         }
1066156d6b00SAppaRao Puli     }
1067156d6b00SAppaRao Puli 
1068156d6b00SAppaRao Puli     void unregisterMetricReportSignal()
1069156d6b00SAppaRao Puli     {
10707d1cc387SAppaRao Puli         if (matchTelemetryMonitor)
10717d1cc387SAppaRao Puli         {
107262598e31SEd Tanous             BMCWEB_LOG_DEBUG("Metrics report signal - Unregister");
1073156d6b00SAppaRao Puli             matchTelemetryMonitor.reset();
1074156d6b00SAppaRao Puli             matchTelemetryMonitor = nullptr;
1075156d6b00SAppaRao Puli         }
10767d1cc387SAppaRao Puli     }
1077156d6b00SAppaRao Puli 
1078156d6b00SAppaRao Puli     void registerMetricReportSignal()
1079156d6b00SAppaRao Puli     {
10807d1cc387SAppaRao Puli         if (!serviceEnabled || matchTelemetryMonitor)
1081156d6b00SAppaRao Puli         {
108262598e31SEd Tanous             BMCWEB_LOG_DEBUG("Not registering metric report signal.");
1083156d6b00SAppaRao Puli             return;
1084156d6b00SAppaRao Puli         }
1085156d6b00SAppaRao Puli 
108662598e31SEd Tanous         BMCWEB_LOG_DEBUG("Metrics report signal - Register");
1087c0353249SWludzik, Jozef         std::string matchStr = "type='signal',member='PropertiesChanged',"
1088c0353249SWludzik, Jozef                                "interface='org.freedesktop.DBus.Properties',"
1089c0353249SWludzik, Jozef                                "arg0=xyz.openbmc_project.Telemetry.Report";
1090156d6b00SAppaRao Puli 
109159d494eeSPatrick Williams         matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>(
109256d2396dSEd Tanous             *crow::connections::systemBus, matchStr, getReadingsForReport);
1093156d6b00SAppaRao Puli     }
109423a21a1cSEd Tanous };
1095b52664e2SAppaRao Puli 
1096b52664e2SAppaRao Puli } // namespace redfish
1097