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