xref: /openbmc/bmcweb/features/redfish/include/event_service_manager.hpp (revision 7d1cc387d312e2a8e4844f9d69ab39b042acd5ce)
1 /*
2 // Copyright (c) 2020 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 #include "node.hpp"
18 #include "registries.hpp"
19 #include "registries/base_message_registry.hpp"
20 #include "registries/openbmc_message_registry.hpp"
21 
22 #include <sys/inotify.h>
23 
24 #include <boost/container/flat_map.hpp>
25 #include <cstdlib>
26 #include <ctime>
27 #include <error_messages.hpp>
28 #include <http_client.hpp>
29 #include <memory>
30 #include <utils/json_utils.hpp>
31 #include <variant>
32 
33 namespace redfish
34 {
35 
36 using ReadingsObjType =
37     std::vector<std::tuple<std::string, std::string, double, std::string>>;
38 using EventServiceConfig = std::tuple<bool, uint32_t, uint32_t>;
39 
40 static constexpr const char* eventFormatType = "Event";
41 static constexpr const char* metricReportFormatType = "MetricReport";
42 
43 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
44 constexpr const char* redfishEventLogFile = "/var/log/redfish";
45 constexpr const uint32_t inotifyFileAction = IN_MODIFY;
46 std::shared_ptr<boost::asio::posix::stream_descriptor> inotifyConn = nullptr;
47 
48 // <ID, timestamp, RedfishLogId, registryPrefix, MessageId, MessageArgs>
49 using EventLogObjectsType =
50     std::tuple<std::string, std::string, std::string, std::string, std::string,
51                boost::beast::span<std::string>>;
52 
53 namespace message_registries
54 {
55 static const Message*
56     getMsgFromRegistry(const std::string& messageKey,
57                        const boost::beast::span<const MessageEntry>& registry)
58 {
59     boost::beast::span<const MessageEntry>::const_iterator messageIt =
60         std::find_if(registry.cbegin(), registry.cend(),
61                      [&messageKey](const MessageEntry& messageEntry) {
62                          return !messageKey.compare(messageEntry.first);
63                      });
64     if (messageIt != registry.cend())
65     {
66         return &messageIt->second;
67     }
68 
69     return nullptr;
70 }
71 
72 static const Message* formatMessage(const std::string_view& messageID)
73 {
74     // Redfish MessageIds are in the form
75     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
76     // the right Message
77     std::vector<std::string> fields;
78     fields.reserve(4);
79     boost::split(fields, messageID, boost::is_any_of("."));
80     if (fields.size() != 4)
81     {
82         return nullptr;
83     }
84     std::string& registryName = fields[0];
85     std::string& messageKey = fields[3];
86 
87     // Find the right registry and check it for the MessageKey
88     if (std::string(base::header.registryPrefix) == registryName)
89     {
90         return getMsgFromRegistry(
91             messageKey, boost::beast::span<const MessageEntry>(base::registry));
92     }
93     if (std::string(openbmc::header.registryPrefix) == registryName)
94     {
95         return getMsgFromRegistry(
96             messageKey,
97             boost::beast::span<const MessageEntry>(openbmc::registry));
98     }
99     return nullptr;
100 }
101 } // namespace message_registries
102 
103 namespace event_log
104 {
105 bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
106                       const bool firstEntry = true)
107 {
108     static time_t prevTs = 0;
109     static int index = 0;
110     if (firstEntry)
111     {
112         prevTs = 0;
113     }
114 
115     // Get the entry timestamp
116     std::time_t curTs = 0;
117     std::tm timeStruct = {};
118     std::istringstream entryStream(logEntry);
119     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
120     {
121         curTs = std::mktime(&timeStruct);
122         if (curTs == -1)
123         {
124             return false;
125         }
126     }
127     // If the timestamp isn't unique, increment the index
128     index = (curTs == prevTs) ? index + 1 : 0;
129 
130     // Save the timestamp
131     prevTs = curTs;
132 
133     entryID = std::to_string(curTs);
134     if (index > 0)
135     {
136         entryID += "_" + std::to_string(index);
137     }
138     return true;
139 }
140 
141 int getEventLogParams(const std::string& logEntry, std::string& timestamp,
142                       std::string& messageID,
143                       boost::beast::span<std::string>& messageArgs)
144 {
145     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
146     // First get the Timestamp
147     size_t space = logEntry.find_first_of(" ");
148     if (space == std::string::npos)
149     {
150         return -EINVAL;
151     }
152     timestamp = logEntry.substr(0, space);
153     // Then get the log contents
154     size_t entryStart = logEntry.find_first_not_of(" ", space);
155     if (entryStart == std::string::npos)
156     {
157         return -EINVAL;
158     }
159     std::string_view entry(logEntry);
160     entry.remove_prefix(entryStart);
161     // Use split to separate the entry into its fields
162     std::vector<std::string> logEntryFields;
163     boost::split(logEntryFields, entry, boost::is_any_of(","),
164                  boost::token_compress_on);
165     // We need at least a MessageId to be valid
166     if (logEntryFields.size() < 1)
167     {
168         return -EINVAL;
169     }
170     messageID = logEntryFields[0];
171 
172     // Get the MessageArgs from the log if there are any
173     if (logEntryFields.size() > 1)
174     {
175         std::string& messageArgsStart = logEntryFields[1];
176         // If the first string is empty, assume there are no MessageArgs
177         std::size_t messageArgsSize = 0;
178         if (!messageArgsStart.empty())
179         {
180             messageArgsSize = logEntryFields.size() - 1;
181         }
182 
183         messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
184     }
185 
186     return 0;
187 }
188 
189 void getRegistryAndMessageKey(const std::string& messageID,
190                               std::string& registryName,
191                               std::string& messageKey)
192 {
193     // Redfish MessageIds are in the form
194     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
195     // the right Message
196     std::vector<std::string> fields;
197     fields.reserve(4);
198     boost::split(fields, messageID, boost::is_any_of("."));
199     if (fields.size() == 4)
200     {
201         registryName = fields[0];
202         messageKey = fields[3];
203     }
204 }
205 
206 int formatEventLogEntry(const std::string& logEntryID,
207                         const std::string& messageID,
208                         const boost::beast::span<std::string>& messageArgs,
209                         std::string timestamp, const std::string customText,
210                         nlohmann::json& logEntryJson)
211 {
212     // Get the Message from the MessageRegistry
213     const message_registries::Message* message =
214         message_registries::formatMessage(messageID);
215 
216     std::string msg;
217     std::string severity;
218     if (message != nullptr)
219     {
220         msg = message->message;
221         severity = message->severity;
222     }
223 
224     // Fill the MessageArgs into the Message
225     int i = 0;
226     for (const std::string& messageArg : messageArgs)
227     {
228         std::string argStr = "%" + std::to_string(++i);
229         size_t argPos = msg.find(argStr);
230         if (argPos != std::string::npos)
231         {
232             msg.replace(argPos, argStr.length(), messageArg);
233         }
234     }
235 
236     // Get the Created time from the timestamp. The log timestamp is in
237     // RFC3339 format which matches the Redfish format except for the
238     // fractional seconds between the '.' and the '+', so just remove them.
239     std::size_t dot = timestamp.find_first_of(".");
240     std::size_t plus = timestamp.find_first_of("+");
241     if (dot != std::string::npos && plus != std::string::npos)
242     {
243         timestamp.erase(dot, plus - dot);
244     }
245 
246     // Fill in the log entry with the gathered data
247     logEntryJson = {{"EventId", logEntryID},
248                     {"EventType", "Event"},
249                     {"Severity", std::move(severity)},
250                     {"Message", std::move(msg)},
251                     {"MessageId", std::move(messageID)},
252                     {"MessageArgs", std::move(messageArgs)},
253                     {"EventTimestamp", std::move(timestamp)},
254                     {"Context", customText}};
255     return 0;
256 }
257 
258 } // namespace event_log
259 #endif
260 
261 class Subscription
262 {
263   public:
264     std::string id;
265     std::string destinationUrl;
266     std::string protocol;
267     std::string retryPolicy;
268     std::string customText;
269     std::string eventFormatType;
270     std::string subscriptionType;
271     std::vector<std::string> registryMsgIds;
272     std::vector<std::string> registryPrefixes;
273     std::vector<nlohmann::json> httpHeaders; // key-value pair
274     std::vector<nlohmann::json> metricReportDefinitions;
275 
276     Subscription(const Subscription&) = delete;
277     Subscription& operator=(const Subscription&) = delete;
278     Subscription(Subscription&&) = delete;
279     Subscription& operator=(Subscription&&) = delete;
280 
281     Subscription(const std::string& inHost, const std::string& inPort,
282                  const std::string& inPath, const std::string& inUriProto) :
283         eventSeqNum(1),
284         host(inHost), port(inPort), path(inPath), uriProto(inUriProto)
285     {
286         conn = std::make_shared<crow::HttpClient>(
287             crow::connections::systemBus->get_io_context(), host, port, path);
288     }
289     ~Subscription()
290     {
291     }
292 
293     void sendEvent(const std::string& msg)
294     {
295         std::vector<std::pair<std::string, std::string>> reqHeaders;
296         for (const auto& header : httpHeaders)
297         {
298             for (const auto& item : header.items())
299             {
300                 std::string key = item.key();
301                 std::string val = item.value();
302                 reqHeaders.emplace_back(std::pair(key, val));
303             }
304         }
305         conn->setHeaders(reqHeaders);
306         conn->sendData(msg);
307     }
308 
309     void sendTestEventLog()
310     {
311         nlohmann::json logEntryArray;
312         logEntryArray.push_back({});
313         nlohmann::json& logEntryJson = logEntryArray.back();
314 
315         logEntryJson = {{"EventId", "TestID"},
316                         {"EventType", "Event"},
317                         {"Severity", "OK"},
318                         {"Message", "Generated test event"},
319                         {"MessageId", "OpenBMC.0.1.TestEventLog"},
320                         {"MessageArgs", nlohmann::json::array()},
321                         {"EventTimestamp", crow::utility::dateTimeNow()},
322                         {"Context", customText}};
323 
324         nlohmann::json msg = {{"@odata.type", "#Event.v1_4_0.Event"},
325                               {"Id", std::to_string(eventSeqNum)},
326                               {"Name", "Event Log"},
327                               {"Events", logEntryArray}};
328 
329         this->sendEvent(msg.dump());
330         this->eventSeqNum++;
331     }
332 
333 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
334     void filterAndSendEventLogs(
335         const std::vector<EventLogObjectsType>& eventRecords)
336     {
337         nlohmann::json logEntryArray;
338         for (const EventLogObjectsType& logEntry : eventRecords)
339         {
340             const std::string& idStr = std::get<0>(logEntry);
341             const std::string& timestamp = std::get<1>(logEntry);
342             const std::string& messageID = std::get<2>(logEntry);
343             const std::string& registryName = std::get<3>(logEntry);
344             const std::string& messageKey = std::get<4>(logEntry);
345             const boost::beast::span<std::string>& messageArgs =
346                 std::get<5>(logEntry);
347 
348             // If registryPrefixes list is empty, don't filter events
349             // send everything.
350             if (registryPrefixes.size())
351             {
352                 auto obj = std::find(registryPrefixes.begin(),
353                                      registryPrefixes.end(), registryName);
354                 if (obj == registryPrefixes.end())
355                 {
356                     continue;
357                 }
358             }
359 
360             // If registryMsgIds list is empty, don't filter events
361             // send everything.
362             if (registryMsgIds.size())
363             {
364                 auto obj = std::find(registryMsgIds.begin(),
365                                      registryMsgIds.end(), messageKey);
366                 if (obj == registryMsgIds.end())
367                 {
368                     continue;
369                 }
370             }
371 
372             logEntryArray.push_back({});
373             nlohmann::json& bmcLogEntry = logEntryArray.back();
374             if (event_log::formatEventLogEntry(idStr, messageID, messageArgs,
375                                                timestamp, customText,
376                                                bmcLogEntry) != 0)
377             {
378                 BMCWEB_LOG_DEBUG << "Read eventLog entry failed";
379                 continue;
380             }
381         }
382 
383         if (logEntryArray.size() < 1)
384         {
385             BMCWEB_LOG_DEBUG << "No log entries available to be transferred.";
386             return;
387         }
388 
389         nlohmann::json msg = {{"@odata.type", "#Event.v1_4_0.Event"},
390                               {"Id", std::to_string(eventSeqNum)},
391                               {"Name", "Event Log"},
392                               {"Events", logEntryArray}};
393 
394         this->sendEvent(msg.dump());
395         this->eventSeqNum++;
396     }
397 #endif
398 
399     void filterAndSendReports(const std::string& id,
400                               const std::string& readingsTs,
401                               const ReadingsObjType& readings)
402     {
403         std::string metricReportDef =
404             "/redfish/v1/TelemetryService/MetricReportDefinitions/" + id;
405 
406         // Empty list means no filter. Send everything.
407         if (metricReportDefinitions.size())
408         {
409             if (std::find(metricReportDefinitions.begin(),
410                           metricReportDefinitions.end(),
411                           metricReportDef) == metricReportDefinitions.end())
412             {
413                 return;
414             }
415         }
416 
417         nlohmann::json metricValuesArray = nlohmann::json::array();
418         for (const auto& it : readings)
419         {
420             metricValuesArray.push_back({});
421             nlohmann::json& entry = metricValuesArray.back();
422 
423             entry = {{"MetricId", std::get<0>(it)},
424                      {"MetricProperty", std::get<1>(it)},
425                      {"MetricValue", std::to_string(std::get<2>(it))},
426                      {"Timestamp", std::get<3>(it)}};
427         }
428 
429         nlohmann::json msg = {
430             {"@odata.id", "/redfish/v1/TelemetryService/MetricReports/" + id},
431             {"@odata.type", "#MetricReport.v1_3_0.MetricReport"},
432             {"Id", id},
433             {"Name", id},
434             {"Timestamp", readingsTs},
435             {"MetricReportDefinition", {{"@odata.id", metricReportDef}}},
436             {"MetricValues", metricValuesArray}};
437 
438         this->sendEvent(msg.dump());
439     }
440 
441   private:
442     uint64_t eventSeqNum;
443     std::string host;
444     std::string port;
445     std::string path;
446     std::string uriProto;
447     std::shared_ptr<crow::HttpClient> conn;
448 };
449 
450 class EventServiceManager
451 {
452   private:
453     bool serviceEnabled;
454     uint32_t retryAttempts;
455     uint32_t retryTimeoutInterval;
456 
457     EventServiceManager(const EventServiceManager&) = delete;
458     EventServiceManager& operator=(const EventServiceManager&) = delete;
459     EventServiceManager(EventServiceManager&&) = delete;
460     EventServiceManager& operator=(EventServiceManager&&) = delete;
461 
462     EventServiceManager() :
463         noOfEventLogSubscribers(0), noOfMetricReportSubscribers(0)
464     {
465         // TODO: Read the persistent data from store and populate.
466         // Populating with default.
467         serviceEnabled = true;
468         retryAttempts = 3;
469         retryTimeoutInterval = 30; // seconds
470     }
471 
472     std::string lastEventTStr;
473     size_t noOfEventLogSubscribers;
474     size_t noOfMetricReportSubscribers;
475     std::shared_ptr<sdbusplus::bus::match::match> matchTelemetryMonitor;
476     boost::container::flat_map<std::string, std::shared_ptr<Subscription>>
477         subscriptionsMap;
478 
479   public:
480     static EventServiceManager& getInstance()
481     {
482         static EventServiceManager handler;
483         return handler;
484     }
485 
486     void updateSubscriptionData()
487     {
488         // Persist the config and subscription data.
489         // TODO: subscriptionsMap & configData need to be
490         // written to Persist store.
491         return;
492     }
493 
494     EventServiceConfig getEventServiceConfig()
495     {
496         return {serviceEnabled, retryAttempts, retryTimeoutInterval};
497     }
498 
499     void setEventServiceConfig(const EventServiceConfig& cfg)
500     {
501         bool updateConfig = false;
502 
503         if (serviceEnabled != std::get<0>(cfg))
504         {
505             serviceEnabled = std::get<0>(cfg);
506             if (serviceEnabled && noOfMetricReportSubscribers)
507             {
508                 registerMetricReportSignal();
509             }
510             else
511             {
512                 unregisterMetricReportSignal();
513             }
514             updateConfig = true;
515         }
516 
517         if (retryAttempts != std::get<1>(cfg))
518         {
519             retryAttempts = std::get<1>(cfg);
520             updateConfig = true;
521         }
522 
523         if (retryTimeoutInterval != std::get<2>(cfg))
524         {
525             retryTimeoutInterval = std::get<2>(cfg);
526             updateConfig = true;
527         }
528 
529         if (updateConfig)
530         {
531             updateSubscriptionData();
532         }
533     }
534 
535     void updateNoOfSubscribersCount()
536     {
537         size_t eventLogSubCount = 0;
538         size_t metricReportSubCount = 0;
539         for (const auto& it : subscriptionsMap)
540         {
541             std::shared_ptr<Subscription> entry = it.second;
542             if (entry->eventFormatType == eventFormatType)
543             {
544                 eventLogSubCount++;
545             }
546             else if (entry->eventFormatType == metricReportFormatType)
547             {
548                 metricReportSubCount++;
549             }
550         }
551 
552         noOfEventLogSubscribers = eventLogSubCount;
553         if (noOfMetricReportSubscribers != metricReportSubCount)
554         {
555             noOfMetricReportSubscribers = metricReportSubCount;
556             if (noOfMetricReportSubscribers)
557             {
558                 registerMetricReportSignal();
559             }
560             else
561             {
562                 unregisterMetricReportSignal();
563             }
564         }
565     }
566 
567     std::shared_ptr<Subscription> getSubscription(const std::string& id)
568     {
569         auto obj = subscriptionsMap.find(id);
570         if (obj == subscriptionsMap.end())
571         {
572             BMCWEB_LOG_ERROR << "No subscription exist with ID:" << id;
573             return nullptr;
574         }
575         std::shared_ptr<Subscription> subValue = obj->second;
576         return subValue;
577     }
578 
579     std::string addSubscription(const std::shared_ptr<Subscription> subValue)
580     {
581         std::srand(static_cast<uint32_t>(std::time(0)));
582         std::string id;
583 
584         int retry = 3;
585         while (retry)
586         {
587             id = std::to_string(std::rand());
588             auto inserted = subscriptionsMap.insert(std::pair(id, subValue));
589             if (inserted.second)
590             {
591                 break;
592             }
593             --retry;
594         };
595 
596         if (retry <= 0)
597         {
598             BMCWEB_LOG_ERROR << "Failed to generate random number";
599             return std::string("");
600         }
601 
602         updateNoOfSubscribersCount();
603         updateSubscriptionData();
604 
605 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
606         if (lastEventTStr.empty())
607         {
608             cacheLastEventTimestamp();
609         }
610 #endif
611         return id;
612     }
613 
614     bool isSubscriptionExist(const std::string& id)
615     {
616         auto obj = subscriptionsMap.find(id);
617         if (obj == subscriptionsMap.end())
618         {
619             return false;
620         }
621         return true;
622     }
623 
624     void deleteSubscription(const std::string& id)
625     {
626         auto obj = subscriptionsMap.find(id);
627         if (obj != subscriptionsMap.end())
628         {
629             subscriptionsMap.erase(obj);
630             updateNoOfSubscribersCount();
631             updateSubscriptionData();
632         }
633     }
634 
635     size_t getNumberOfSubscriptions()
636     {
637         return subscriptionsMap.size();
638     }
639 
640     std::vector<std::string> getAllIDs()
641     {
642         std::vector<std::string> idList;
643         for (const auto& it : subscriptionsMap)
644         {
645             idList.emplace_back(it.first);
646         }
647         return idList;
648     }
649 
650     bool isDestinationExist(const std::string& destUrl)
651     {
652         for (const auto& it : subscriptionsMap)
653         {
654             std::shared_ptr<Subscription> entry = it.second;
655             if (entry->destinationUrl == destUrl)
656             {
657                 BMCWEB_LOG_ERROR << "Destination exist already" << destUrl;
658                 return true;
659             }
660         }
661         return false;
662     }
663 
664     void sendTestEventLog()
665     {
666         for (const auto& it : this->subscriptionsMap)
667         {
668             std::shared_ptr<Subscription> entry = it.second;
669             entry->sendTestEventLog();
670         }
671     }
672 
673 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
674     void cacheLastEventTimestamp()
675     {
676         std::ifstream logStream(redfishEventLogFile);
677         if (!logStream.good())
678         {
679             BMCWEB_LOG_ERROR << " Redfish log file open failed \n";
680             return;
681         }
682         std::string logEntry;
683         while (std::getline(logStream, logEntry))
684         {
685             size_t space = logEntry.find_first_of(" ");
686             if (space == std::string::npos)
687             {
688                 // Shouldn't enter here but lets skip it.
689                 BMCWEB_LOG_DEBUG << "Invalid log entry found.";
690                 continue;
691             }
692             lastEventTStr = logEntry.substr(0, space);
693         }
694         BMCWEB_LOG_DEBUG << "Last Event time stamp set: " << lastEventTStr;
695     }
696 
697     void readEventLogsFromFile()
698     {
699         if (!serviceEnabled || !noOfEventLogSubscribers)
700         {
701             BMCWEB_LOG_DEBUG << "EventService disabled or no Subscriptions.";
702             return;
703         }
704         std::ifstream logStream(redfishEventLogFile);
705         if (!logStream.good())
706         {
707             BMCWEB_LOG_ERROR << " Redfish log file open failed";
708             return;
709         }
710 
711         std::vector<EventLogObjectsType> eventRecords;
712 
713         bool startLogCollection = false;
714         bool firstEntry = true;
715 
716         std::string logEntry;
717         while (std::getline(logStream, logEntry))
718         {
719             if (!startLogCollection)
720             {
721                 if (boost::starts_with(logEntry, lastEventTStr))
722                 {
723                     startLogCollection = true;
724                 }
725                 continue;
726             }
727 
728             std::string idStr;
729             if (!event_log::getUniqueEntryID(logEntry, idStr, firstEntry))
730             {
731                 continue;
732             }
733             firstEntry = false;
734 
735             std::string timestamp;
736             std::string messageID;
737             boost::beast::span<std::string> messageArgs;
738             if (event_log::getEventLogParams(logEntry, timestamp, messageID,
739                                              messageArgs) != 0)
740             {
741                 BMCWEB_LOG_DEBUG << "Read eventLog entry params failed";
742                 continue;
743             }
744 
745             std::string registryName;
746             std::string messageKey;
747             event_log::getRegistryAndMessageKey(messageID, registryName,
748                                                 messageKey);
749             if (registryName.empty() || messageKey.empty())
750             {
751                 continue;
752             }
753 
754             lastEventTStr = timestamp;
755             eventRecords.emplace_back(idStr, timestamp, messageID, registryName,
756                                       messageKey, messageArgs);
757         }
758 
759         for (const auto& it : this->subscriptionsMap)
760         {
761             std::shared_ptr<Subscription> entry = it.second;
762             if (entry->eventFormatType == "Event")
763             {
764                 entry->filterAndSendEventLogs(eventRecords);
765             }
766         }
767     }
768 
769     static void watchRedfishEventLogFile()
770     {
771         if (inotifyConn == nullptr)
772         {
773             return;
774         }
775 
776         static std::array<char, 1024> readBuffer;
777 
778         inotifyConn->async_read_some(
779             boost::asio::buffer(readBuffer),
780             [&](const boost::system::error_code& ec,
781                 const std::size_t& bytesTransferred) {
782                 if (ec)
783                 {
784                     BMCWEB_LOG_ERROR << "Callback Error: " << ec.message();
785                     return;
786                 }
787                 std::size_t index = 0;
788                 while ((index + sizeof(inotify_event)) <= bytesTransferred)
789                 {
790                     struct inotify_event event;
791                     std::memcpy(&event, &readBuffer[index],
792                                 sizeof(inotify_event));
793                     if (event.mask == inotifyFileAction)
794                     {
795                         EventServiceManager::getInstance()
796                             .readEventLogsFromFile();
797                     }
798                     index += (sizeof(inotify_event) + event.len);
799                 }
800 
801                 watchRedfishEventLogFile();
802             });
803     }
804 
805     static int startEventLogMonitor(boost::asio::io_service& ioc)
806     {
807         inotifyConn =
808             std::make_shared<boost::asio::posix::stream_descriptor>(ioc);
809         int fd = inotify_init1(IN_NONBLOCK);
810         if (fd == -1)
811         {
812             BMCWEB_LOG_ERROR << "inotify_init1 failed.";
813             return -1;
814         }
815         auto wd = inotify_add_watch(fd, redfishEventLogFile, inotifyFileAction);
816         if (wd == -1)
817         {
818             BMCWEB_LOG_ERROR
819                 << "inotify_add_watch failed for redfish log file.";
820             return -1;
821         }
822 
823         // monitor redfish event log file
824         inotifyConn->assign(fd);
825         watchRedfishEventLogFile();
826 
827         return 0;
828     }
829 
830 #endif
831 
832     void getMetricReading(const std::string& service,
833                           const std::string& objPath, const std::string& intf)
834     {
835         std::size_t found = objPath.find_last_of("/");
836         if (found == std::string::npos)
837         {
838             BMCWEB_LOG_DEBUG << "Invalid objPath received";
839             return;
840         }
841 
842         std::string idStr = objPath.substr(found + 1);
843         if (idStr.empty())
844         {
845             BMCWEB_LOG_DEBUG << "Invalid ID in objPath";
846             return;
847         }
848 
849         crow::connections::systemBus->async_method_call(
850             [idStr{std::move(idStr)}](
851                 const boost::system::error_code ec,
852                 boost::container::flat_map<
853                     std::string, std::variant<std::string, ReadingsObjType>>&
854                     resp) {
855                 if (ec)
856                 {
857                     BMCWEB_LOG_DEBUG
858                         << "D-Bus call failed to GetAll metric readings.";
859                     return;
860                 }
861 
862                 const std::string* timestampPtr =
863                     std::get_if<std::string>(&resp["Timestamp"]);
864                 if (!timestampPtr)
865                 {
866                     BMCWEB_LOG_DEBUG << "Failed to Get timestamp.";
867                     return;
868                 }
869 
870                 ReadingsObjType* readingsPtr =
871                     std::get_if<ReadingsObjType>(&resp["Readings"]);
872                 if (!readingsPtr)
873                 {
874                     BMCWEB_LOG_DEBUG << "Failed to Get Readings property.";
875                     return;
876                 }
877 
878                 if (!readingsPtr->size())
879                 {
880                     BMCWEB_LOG_DEBUG << "No metrics report to be transferred";
881                     return;
882                 }
883 
884                 for (const auto& it :
885                      EventServiceManager::getInstance().subscriptionsMap)
886                 {
887                     std::shared_ptr<Subscription> entry = it.second;
888                     if (entry->eventFormatType == metricReportFormatType)
889                     {
890                         entry->filterAndSendReports(idStr, *timestampPtr,
891                                                     *readingsPtr);
892                     }
893                 }
894             },
895             service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
896             intf);
897     }
898 
899     void unregisterMetricReportSignal()
900     {
901         if (matchTelemetryMonitor)
902         {
903             BMCWEB_LOG_DEBUG << "Metrics report signal - Unregister";
904             matchTelemetryMonitor.reset();
905             matchTelemetryMonitor = nullptr;
906         }
907     }
908 
909     void registerMetricReportSignal()
910     {
911         if (!serviceEnabled || matchTelemetryMonitor)
912         {
913             BMCWEB_LOG_DEBUG << "Not registering metric report signal.";
914             return;
915         }
916 
917         BMCWEB_LOG_DEBUG << "Metrics report signal - Register";
918         std::string matchStr(
919             "type='signal',member='ReportUpdate', "
920             "interface='xyz.openbmc_project.MonitoringService.Report'");
921 
922         matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match::match>(
923             *crow::connections::systemBus, matchStr,
924             [this](sdbusplus::message::message& msg) {
925                 if (msg.is_method_error())
926                 {
927                     BMCWEB_LOG_ERROR << "TelemetryMonitor Signal error";
928                     return;
929                 }
930 
931                 std::string service = msg.get_sender();
932                 std::string objPath = msg.get_path();
933                 std::string intf = msg.get_interface();
934                 getMetricReading(service, objPath, intf);
935             });
936     }
937 };
938 
939 } // namespace redfish
940