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 "dbus_utility.hpp"
18 #include "error_messages.hpp"
19 #include "event_service_store.hpp"
20 #include "filter_expr_executor.hpp"
21 #include "generated/enums/event.hpp"
22 #include "generated/enums/log_entry.hpp"
23 #include "http_client.hpp"
24 #include "metric_report.hpp"
25 #include "ossl_random.hpp"
26 #include "persistent_data.hpp"
27 #include "registries.hpp"
28 #include "registries_selector.hpp"
29 #include "str_utility.hpp"
30 #include "utility.hpp"
31 #include "utils/json_utils.hpp"
32 #include "utils/time_utils.hpp"
33 
34 #include <sys/inotify.h>
35 
36 #include <boost/asio/io_context.hpp>
37 #include <boost/circular_buffer.hpp>
38 #include <boost/container/flat_map.hpp>
39 #include <boost/url/format.hpp>
40 #include <boost/url/url_view_base.hpp>
41 #include <sdbusplus/bus/match.hpp>
42 
43 #include <algorithm>
44 #include <cstdlib>
45 #include <ctime>
46 #include <fstream>
47 #include <memory>
48 #include <ranges>
49 #include <span>
50 
51 namespace redfish
52 {
53 
54 static constexpr const char* eventFormatType = "Event";
55 static constexpr const char* metricReportFormatType = "MetricReport";
56 
57 static constexpr const char* subscriptionTypeSSE = "SSE";
58 static constexpr const char* eventServiceFile =
59     "/var/lib/bmcweb/eventservice_config.json";
60 
61 static constexpr const uint8_t maxNoOfSubscriptions = 20;
62 static constexpr const uint8_t maxNoOfSSESubscriptions = 10;
63 
64 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
65 static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
66 static constexpr const char* redfishEventLogDir = "/var/log";
67 static constexpr const char* redfishEventLogFile = "/var/log/redfish";
68 static constexpr const size_t iEventSize = sizeof(inotify_event);
69 
70 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
71 static int inotifyFd = -1;
72 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
73 static int dirWatchDesc = -1;
74 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
75 static int fileWatchDesc = -1;
76 struct EventLogObjectsType
77 {
78     std::string id;
79     std::string timestamp;
80     std::string messageId;
81     std::vector<std::string> messageArgs;
82 };
83 
84 namespace registries
85 {
86 static const Message*
getMsgFromRegistry(const std::string & messageKey,const std::span<const MessageEntry> & registry)87     getMsgFromRegistry(const std::string& messageKey,
88                        const std::span<const MessageEntry>& registry)
89 {
90     std::span<const MessageEntry>::iterator messageIt = std::ranges::find_if(
91         registry, [&messageKey](const MessageEntry& messageEntry) {
92             return messageKey == messageEntry.first;
93         });
94     if (messageIt != registry.end())
95     {
96         return &messageIt->second;
97     }
98 
99     return nullptr;
100 }
101 
formatMessage(std::string_view messageID)102 static const Message* formatMessage(std::string_view messageID)
103 {
104     // Redfish MessageIds are in the form
105     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
106     // the right Message
107     std::vector<std::string> fields;
108     fields.reserve(4);
109 
110     bmcweb::split(fields, messageID, '.');
111     if (fields.size() != 4)
112     {
113         return nullptr;
114     }
115     const std::string& registryName = fields[0];
116     const std::string& messageKey = fields[3];
117 
118     // Find the right registry and check it for the MessageKey
119     return getMsgFromRegistry(messageKey, getRegistryFromPrefix(registryName));
120 }
121 } // namespace registries
122 
123 namespace event_log
124 {
getUniqueEntryID(const std::string & logEntry,std::string & entryID)125 inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID)
126 {
127     static time_t prevTs = 0;
128     static int index = 0;
129 
130     // Get the entry timestamp
131     std::time_t curTs = 0;
132     std::tm timeStruct = {};
133     std::istringstream entryStream(logEntry);
134     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
135     {
136         curTs = std::mktime(&timeStruct);
137         if (curTs == -1)
138         {
139             return false;
140         }
141     }
142     // If the timestamp isn't unique, increment the index
143     index = (curTs == prevTs) ? index + 1 : 0;
144 
145     // Save the timestamp
146     prevTs = curTs;
147 
148     entryID = std::to_string(curTs);
149     if (index > 0)
150     {
151         entryID += "_" + std::to_string(index);
152     }
153     return true;
154 }
155 
getEventLogParams(const std::string & logEntry,std::string & timestamp,std::string & messageID,std::vector<std::string> & messageArgs)156 inline int getEventLogParams(const std::string& logEntry,
157                              std::string& timestamp, std::string& messageID,
158                              std::vector<std::string>& messageArgs)
159 {
160     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
161     // First get the Timestamp
162     size_t space = logEntry.find_first_of(' ');
163     if (space == std::string::npos)
164     {
165         BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}",
166                          logEntry);
167         return -EINVAL;
168     }
169     timestamp = logEntry.substr(0, space);
170     // Then get the log contents
171     size_t entryStart = logEntry.find_first_not_of(' ', space);
172     if (entryStart == std::string::npos)
173     {
174         BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}",
175                          logEntry);
176         return -EINVAL;
177     }
178     std::string_view entry(logEntry);
179     entry.remove_prefix(entryStart);
180     // Use split to separate the entry into its fields
181     std::vector<std::string> logEntryFields;
182     bmcweb::split(logEntryFields, entry, ',');
183     // We need at least a MessageId to be valid
184     if (logEntryFields.empty())
185     {
186         BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}",
187                          logEntry);
188         return -EINVAL;
189     }
190     messageID = logEntryFields[0];
191 
192     // Get the MessageArgs from the log if there are any
193     if (logEntryFields.size() > 1)
194     {
195         const std::string& messageArgsStart = logEntryFields[1];
196         // If the first string is empty, assume there are no MessageArgs
197         if (!messageArgsStart.empty())
198         {
199             messageArgs.assign(logEntryFields.begin() + 1,
200                                logEntryFields.end());
201         }
202     }
203 
204     return 0;
205 }
206 
getRegistryAndMessageKey(const std::string & messageID,std::string & registryName,std::string & messageKey)207 inline void getRegistryAndMessageKey(const std::string& messageID,
208                                      std::string& registryName,
209                                      std::string& messageKey)
210 {
211     // Redfish MessageIds are in the form
212     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
213     // the right Message
214     std::vector<std::string> fields;
215     fields.reserve(4);
216     bmcweb::split(fields, messageID, '.');
217     if (fields.size() == 4)
218     {
219         registryName = fields[0];
220         messageKey = fields[3];
221     }
222 }
223 
formatEventLogEntry(const std::string & logEntryID,const std::string & messageID,const std::span<std::string_view> messageArgs,std::string timestamp,const std::string & customText,nlohmann::json::object_t & logEntryJson)224 inline int formatEventLogEntry(
225     const std::string& logEntryID, const std::string& messageID,
226     const std::span<std::string_view> messageArgs, std::string timestamp,
227     const std::string& customText, nlohmann::json::object_t& logEntryJson)
228 {
229     // Get the Message from the MessageRegistry
230     const registries::Message* message = registries::formatMessage(messageID);
231 
232     if (message == nullptr)
233     {
234         return -1;
235     }
236 
237     std::string msg =
238         redfish::registries::fillMessageArgs(messageArgs, message->message);
239     if (msg.empty())
240     {
241         return -1;
242     }
243 
244     // Get the Created time from the timestamp. The log timestamp is in
245     // RFC3339 format which matches the Redfish format except for the
246     // fractional seconds between the '.' and the '+', so just remove them.
247     std::size_t dot = timestamp.find_first_of('.');
248     std::size_t plus = timestamp.find_first_of('+', dot);
249     if (dot != std::string::npos && plus != std::string::npos)
250     {
251         timestamp.erase(dot, plus - dot);
252     }
253 
254     // Fill in the log entry with the gathered data
255     logEntryJson["EventId"] = logEntryID;
256 
257     logEntryJson["Severity"] = message->messageSeverity;
258     logEntryJson["Message"] = std::move(msg);
259     logEntryJson["MessageId"] = messageID;
260     logEntryJson["MessageArgs"] = messageArgs;
261     logEntryJson["EventTimestamp"] = std::move(timestamp);
262     logEntryJson["Context"] = customText;
263     return 0;
264 }
265 
266 } // namespace event_log
267 
268 class Subscription : public persistent_data::UserSubscription
269 {
270   public:
271     Subscription(const Subscription&) = delete;
272     Subscription& operator=(const Subscription&) = delete;
273     Subscription(Subscription&&) = delete;
274     Subscription& operator=(Subscription&&) = delete;
275 
Subscription(const boost::urls::url_view_base & url,boost::asio::io_context & ioc)276     Subscription(const boost::urls::url_view_base& url,
277                  boost::asio::io_context& ioc) :
278         policy(std::make_shared<crow::ConnectionPolicy>())
279     {
280         destinationUrl = url;
281         client.emplace(ioc, policy);
282         // Subscription constructor
283         policy->invalidResp = retryRespHandler;
284     }
285 
Subscription(crow::sse_socket::Connection & connIn)286     explicit Subscription(crow::sse_socket::Connection& connIn) :
287         sseConn(&connIn)
288     {}
289 
290     ~Subscription() = default;
291 
sendEventToSubscriber(std::string && msg)292     bool sendEventToSubscriber(std::string&& msg)
293     {
294         persistent_data::EventServiceConfig eventServiceConfig =
295             persistent_data::EventServiceStore::getInstance()
296                 .getEventServiceConfig();
297         if (!eventServiceConfig.enabled)
298         {
299             return false;
300         }
301 
302         if (client)
303         {
304             client->sendData(
305                 std::move(msg), destinationUrl,
306                 static_cast<ensuressl::VerifyCertificate>(verifyCertificate),
307                 httpHeaders, boost::beast::http::verb::post);
308             return true;
309         }
310 
311         if (sseConn != nullptr)
312         {
313             eventSeqNum++;
314             sseConn->sendSseEvent(std::to_string(eventSeqNum), msg);
315         }
316         return true;
317     }
318 
eventMatchesFilter(const nlohmann::json::object_t & eventMessage,std::string_view resType)319     bool eventMatchesFilter(const nlohmann::json::object_t& eventMessage,
320                             std::string_view resType)
321     {
322         // If resourceTypes list is empty, assume all
323         if (!resourceTypes.empty())
324         {
325             // Search the resourceTypes list for the subscription.
326             auto resourceTypeIndex = std::ranges::find_if(
327                 resourceTypes, [resType](const std::string& rtEntry) {
328                     return rtEntry == resType;
329                 });
330             if (resourceTypeIndex == resourceTypes.end())
331             {
332                 BMCWEB_LOG_DEBUG("Not subscribed to this resource");
333                 return false;
334             }
335             BMCWEB_LOG_DEBUG("ResourceType {} found in the subscribed list",
336                              resType);
337         }
338 
339         // If registryMsgIds list is empty, assume all
340         if (!registryMsgIds.empty())
341         {
342             auto eventJson = eventMessage.find("MessageId");
343             if (eventJson == eventMessage.end())
344             {
345                 BMCWEB_LOG_DEBUG("'MessageId' not present");
346                 return false;
347             }
348 
349             const std::string* messageId =
350                 eventJson->second.get_ptr<const std::string*>();
351             if (messageId == nullptr)
352             {
353                 BMCWEB_LOG_ERROR("EventType wasn't a string???");
354                 return false;
355             }
356 
357             std::string registry;
358             std::string messageKey;
359             event_log::getRegistryAndMessageKey(*messageId, registry,
360                                                 messageKey);
361 
362             BMCWEB_LOG_DEBUG("extracted registry {}", registry);
363             BMCWEB_LOG_DEBUG("extracted message key {}", messageKey);
364 
365             auto obj = std::ranges::find(registryMsgIds, registry);
366             if (obj == registryMsgIds.end())
367             {
368                 BMCWEB_LOG_DEBUG("did not find registry {} in registryMsgIds",
369                                  registry);
370                 BMCWEB_LOG_DEBUG("registryMsgIds has {} entries",
371                                  registryMsgIds.size());
372                 return false;
373             }
374         }
375 
376         if (filter)
377         {
378             if (!memberMatches(eventMessage, *filter))
379             {
380                 BMCWEB_LOG_DEBUG("Filter didn't match");
381                 return false;
382             }
383         }
384 
385         return true;
386     }
387 
sendTestEventLog()388     bool sendTestEventLog()
389     {
390         nlohmann::json::array_t logEntryArray;
391         nlohmann::json& logEntryJson = logEntryArray.emplace_back();
392 
393         logEntryJson["EventId"] = "TestID";
394         logEntryJson["Severity"] = log_entry::EventSeverity::OK;
395         logEntryJson["Message"] = "Generated test event";
396         logEntryJson["MessageId"] = "OpenBMC.0.2.TestEventLog";
397         // MemberId is 0 : since we are sending one event record.
398         logEntryJson["MemberId"] = 0;
399         logEntryJson["MessageArgs"] = nlohmann::json::array();
400         logEntryJson["EventTimestamp"] =
401             redfish::time_utils::getDateTimeOffsetNow().first;
402         logEntryJson["Context"] = customText;
403 
404         nlohmann::json msg;
405         msg["@odata.type"] = "#Event.v1_4_0.Event";
406         msg["Id"] = std::to_string(eventSeqNum);
407         msg["Name"] = "Event Log";
408         msg["Events"] = logEntryArray;
409 
410         std::string strMsg =
411             msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
412         return sendEventToSubscriber(std::move(strMsg));
413     }
414 
filterAndSendEventLogs(const std::vector<EventLogObjectsType> & eventRecords)415     void filterAndSendEventLogs(
416         const std::vector<EventLogObjectsType>& eventRecords)
417     {
418         nlohmann::json::array_t logEntryArray;
419         for (const EventLogObjectsType& logEntry : eventRecords)
420         {
421             std::vector<std::string_view> messageArgsView(
422                 logEntry.messageArgs.begin(), logEntry.messageArgs.end());
423 
424             nlohmann::json::object_t bmcLogEntry;
425             if (event_log::formatEventLogEntry(
426                     logEntry.id, logEntry.messageId, messageArgsView,
427                     logEntry.timestamp, customText, bmcLogEntry) != 0)
428             {
429                 BMCWEB_LOG_DEBUG("Read eventLog entry failed");
430                 continue;
431             }
432 
433             if (!eventMatchesFilter(bmcLogEntry, ""))
434             {
435                 BMCWEB_LOG_DEBUG("Event {} did not match the filter",
436                                  nlohmann::json(bmcLogEntry).dump());
437                 continue;
438             }
439 
440             logEntryArray.emplace_back(std::move(bmcLogEntry));
441         }
442 
443         if (logEntryArray.empty())
444         {
445             BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
446             return;
447         }
448 
449         nlohmann::json msg;
450         msg["@odata.type"] = "#Event.v1_4_0.Event";
451         msg["Id"] = std::to_string(eventSeqNum);
452         msg["Name"] = "Event Log";
453         msg["Events"] = std::move(logEntryArray);
454         std::string strMsg =
455             msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
456         sendEventToSubscriber(std::move(strMsg));
457         eventSeqNum++;
458     }
459 
filterAndSendReports(const std::string & reportId,const telemetry::TimestampReadings & var)460     void filterAndSendReports(const std::string& reportId,
461                               const telemetry::TimestampReadings& var)
462     {
463         boost::urls::url mrdUri = boost::urls::format(
464             "/redfish/v1/TelemetryService/MetricReportDefinitions/{}",
465             reportId);
466 
467         // Empty list means no filter. Send everything.
468         if (!metricReportDefinitions.empty())
469         {
470             if (std::ranges::find(metricReportDefinitions, mrdUri.buffer()) ==
471                 metricReportDefinitions.end())
472             {
473                 return;
474             }
475         }
476 
477         nlohmann::json msg;
478         if (!telemetry::fillReport(msg, reportId, var))
479         {
480             BMCWEB_LOG_ERROR("Failed to fill the MetricReport for DBus "
481                              "Report with id {}",
482                              reportId);
483             return;
484         }
485 
486         // Context is set by user during Event subscription and it must be
487         // set for MetricReport response.
488         if (!customText.empty())
489         {
490             msg["Context"] = customText;
491         }
492 
493         std::string strMsg =
494             msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
495         sendEventToSubscriber(std::move(strMsg));
496     }
497 
updateRetryConfig(uint32_t retryAttempts,uint32_t retryTimeoutInterval)498     void updateRetryConfig(uint32_t retryAttempts,
499                            uint32_t retryTimeoutInterval)
500     {
501         if (policy == nullptr)
502         {
503             BMCWEB_LOG_DEBUG("Retry policy was nullptr, ignoring set");
504             return;
505         }
506         policy->maxRetryAttempts = retryAttempts;
507         policy->retryIntervalSecs = std::chrono::seconds(retryTimeoutInterval);
508     }
509 
getEventSeqNum() const510     uint64_t getEventSeqNum() const
511     {
512         return eventSeqNum;
513     }
514 
setSubscriptionId(const std::string & id2)515     void setSubscriptionId(const std::string& id2)
516     {
517         BMCWEB_LOG_DEBUG("Subscription ID: {}", id2);
518         subId = id2;
519     }
520 
getSubscriptionId()521     std::string getSubscriptionId()
522     {
523         return subId;
524     }
525 
matchSseId(const crow::sse_socket::Connection & thisConn)526     bool matchSseId(const crow::sse_socket::Connection& thisConn)
527     {
528         return &thisConn == sseConn;
529     }
530 
531     // Check used to indicate what response codes are valid as part of our retry
532     // policy.  2XX is considered acceptable
retryRespHandler(unsigned int respCode)533     static boost::system::error_code retryRespHandler(unsigned int respCode)
534     {
535         BMCWEB_LOG_DEBUG(
536             "Checking response code validity for SubscriptionEvent");
537         if ((respCode < 200) || (respCode >= 300))
538         {
539             return boost::system::errc::make_error_code(
540                 boost::system::errc::result_out_of_range);
541         }
542 
543         // Return 0 if the response code is valid
544         return boost::system::errc::make_error_code(
545             boost::system::errc::success);
546     }
547 
548   private:
549     std::string subId;
550     uint64_t eventSeqNum = 1;
551     boost::urls::url host;
552     std::shared_ptr<crow::ConnectionPolicy> policy;
553     crow::sse_socket::Connection* sseConn = nullptr;
554 
555     std::optional<crow::HttpClient> client;
556 
557   public:
558     std::optional<filter_ast::LogicalAnd> filter;
559 };
560 
561 class EventServiceManager
562 {
563   private:
564     bool serviceEnabled = false;
565     uint32_t retryAttempts = 0;
566     uint32_t retryTimeoutInterval = 0;
567 
568     std::streampos redfishLogFilePosition{0};
569     size_t noOfEventLogSubscribers{0};
570     size_t noOfMetricReportSubscribers{0};
571     std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor;
572     boost::container::flat_map<std::string, std::shared_ptr<Subscription>>
573         subscriptionsMap;
574 
575     uint64_t eventId{1};
576 
577     struct Event
578     {
579         std::string id;
580         nlohmann::json message;
581     };
582 
583     constexpr static size_t maxMessages = 200;
584     boost::circular_buffer<Event> messages{maxMessages};
585 
586     boost::asio::io_context& ioc;
587 
588   public:
589     EventServiceManager(const EventServiceManager&) = delete;
590     EventServiceManager& operator=(const EventServiceManager&) = delete;
591     EventServiceManager(EventServiceManager&&) = delete;
592     EventServiceManager& operator=(EventServiceManager&&) = delete;
593     ~EventServiceManager() = default;
594 
EventServiceManager(boost::asio::io_context & iocIn)595     explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn)
596     {
597         // Load config from persist store.
598         initConfig();
599     }
600 
601     static EventServiceManager&
getInstance(boost::asio::io_context * ioc=nullptr)602         getInstance(boost::asio::io_context* ioc = nullptr)
603     {
604         static EventServiceManager handler(*ioc);
605         return handler;
606     }
607 
initConfig()608     void initConfig()
609     {
610         loadOldBehavior();
611 
612         persistent_data::EventServiceConfig eventServiceConfig =
613             persistent_data::EventServiceStore::getInstance()
614                 .getEventServiceConfig();
615 
616         serviceEnabled = eventServiceConfig.enabled;
617         retryAttempts = eventServiceConfig.retryAttempts;
618         retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval;
619 
620         for (const auto& it : persistent_data::EventServiceStore::getInstance()
621                                   .subscriptionsConfigMap)
622         {
623             std::shared_ptr<persistent_data::UserSubscription> newSub =
624                 it.second;
625 
626             boost::system::result<boost::urls::url> url =
627                 boost::urls::parse_absolute_uri(newSub->destinationUrl);
628 
629             if (!url)
630             {
631                 BMCWEB_LOG_ERROR(
632                     "Failed to validate and split destination url");
633                 continue;
634             }
635             std::shared_ptr<Subscription> subValue =
636                 std::make_shared<Subscription>(*url, ioc);
637 
638             subValue->id = newSub->id;
639             subValue->destinationUrl = newSub->destinationUrl;
640             subValue->protocol = newSub->protocol;
641             subValue->verifyCertificate = newSub->verifyCertificate;
642             subValue->retryPolicy = newSub->retryPolicy;
643             subValue->customText = newSub->customText;
644             subValue->eventFormatType = newSub->eventFormatType;
645             subValue->subscriptionType = newSub->subscriptionType;
646             subValue->registryMsgIds = newSub->registryMsgIds;
647             subValue->registryPrefixes = newSub->registryPrefixes;
648             subValue->resourceTypes = newSub->resourceTypes;
649             subValue->httpHeaders = newSub->httpHeaders;
650             subValue->metricReportDefinitions = newSub->metricReportDefinitions;
651 
652             if (subValue->id.empty())
653             {
654                 BMCWEB_LOG_ERROR("Failed to add subscription");
655             }
656             subscriptionsMap.insert(std::pair(subValue->id, subValue));
657 
658             updateNoOfSubscribersCount();
659 
660             if constexpr (!BMCWEB_REDFISH_DBUS_LOG)
661             {
662                 cacheRedfishLogFile();
663             }
664 
665             // Update retry configuration.
666             subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
667         }
668     }
669 
loadOldBehavior()670     static void loadOldBehavior()
671     {
672         std::ifstream eventConfigFile(eventServiceFile);
673         if (!eventConfigFile.good())
674         {
675             BMCWEB_LOG_DEBUG("Old eventService config not exist");
676             return;
677         }
678         auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false);
679         if (jsonData.is_discarded())
680         {
681             BMCWEB_LOG_ERROR("Old eventService config parse error.");
682             return;
683         }
684 
685         const nlohmann::json::object_t* obj =
686             jsonData.get_ptr<const nlohmann::json::object_t*>();
687         for (const auto& item : *obj)
688         {
689             if (item.first == "Configuration")
690             {
691                 persistent_data::EventServiceStore::getInstance()
692                     .getEventServiceConfig()
693                     .fromJson(item.second);
694             }
695             else if (item.first == "Subscriptions")
696             {
697                 for (const auto& elem : item.second)
698                 {
699                     std::shared_ptr<persistent_data::UserSubscription>
700                         newSubscription =
701                             persistent_data::UserSubscription::fromJson(elem,
702                                                                         true);
703                     if (newSubscription == nullptr)
704                     {
705                         BMCWEB_LOG_ERROR("Problem reading subscription "
706                                          "from old persistent store");
707                         continue;
708                     }
709 
710                     std::uniform_int_distribution<uint32_t> dist(0);
711                     bmcweb::OpenSSLGenerator gen;
712 
713                     std::string id;
714 
715                     int retry = 3;
716                     while (retry != 0)
717                     {
718                         id = std::to_string(dist(gen));
719                         if (gen.error())
720                         {
721                             retry = 0;
722                             break;
723                         }
724                         newSubscription->id = id;
725                         auto inserted =
726                             persistent_data::EventServiceStore::getInstance()
727                                 .subscriptionsConfigMap.insert(
728                                     std::pair(id, newSubscription));
729                         if (inserted.second)
730                         {
731                             break;
732                         }
733                         --retry;
734                     }
735 
736                     if (retry <= 0)
737                     {
738                         BMCWEB_LOG_ERROR(
739                             "Failed to generate random number from old "
740                             "persistent store");
741                         continue;
742                     }
743                 }
744             }
745 
746             persistent_data::getConfig().writeData();
747             std::error_code ec;
748             std::filesystem::remove(eventServiceFile, ec);
749             if (ec)
750             {
751                 BMCWEB_LOG_DEBUG(
752                     "Failed to remove old event service file.  Ignoring");
753             }
754             else
755             {
756                 BMCWEB_LOG_DEBUG("Remove old eventservice config");
757             }
758         }
759     }
760 
updateSubscriptionData() const761     void updateSubscriptionData() const
762     {
763         persistent_data::EventServiceStore::getInstance()
764             .eventServiceConfig.enabled = serviceEnabled;
765         persistent_data::EventServiceStore::getInstance()
766             .eventServiceConfig.retryAttempts = retryAttempts;
767         persistent_data::EventServiceStore::getInstance()
768             .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval;
769 
770         persistent_data::getConfig().writeData();
771     }
772 
setEventServiceConfig(const persistent_data::EventServiceConfig & cfg)773     void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg)
774     {
775         bool updateConfig = false;
776         bool updateRetryCfg = false;
777 
778         if (serviceEnabled != cfg.enabled)
779         {
780             serviceEnabled = cfg.enabled;
781             if (serviceEnabled && noOfMetricReportSubscribers != 0U)
782             {
783                 registerMetricReportSignal();
784             }
785             else
786             {
787                 unregisterMetricReportSignal();
788             }
789             updateConfig = true;
790         }
791 
792         if (retryAttempts != cfg.retryAttempts)
793         {
794             retryAttempts = cfg.retryAttempts;
795             updateConfig = true;
796             updateRetryCfg = true;
797         }
798 
799         if (retryTimeoutInterval != cfg.retryTimeoutInterval)
800         {
801             retryTimeoutInterval = cfg.retryTimeoutInterval;
802             updateConfig = true;
803             updateRetryCfg = true;
804         }
805 
806         if (updateConfig)
807         {
808             updateSubscriptionData();
809         }
810 
811         if (updateRetryCfg)
812         {
813             // Update the changed retry config to all subscriptions
814             for (const auto& it :
815                  EventServiceManager::getInstance().subscriptionsMap)
816             {
817                 Subscription& entry = *it.second;
818                 entry.updateRetryConfig(retryAttempts, retryTimeoutInterval);
819             }
820         }
821     }
822 
updateNoOfSubscribersCount()823     void updateNoOfSubscribersCount()
824     {
825         size_t eventLogSubCount = 0;
826         size_t metricReportSubCount = 0;
827         for (const auto& it : subscriptionsMap)
828         {
829             std::shared_ptr<Subscription> entry = it.second;
830             if (entry->eventFormatType == eventFormatType)
831             {
832                 eventLogSubCount++;
833             }
834             else if (entry->eventFormatType == metricReportFormatType)
835             {
836                 metricReportSubCount++;
837             }
838         }
839 
840         noOfEventLogSubscribers = eventLogSubCount;
841         if (noOfMetricReportSubscribers != metricReportSubCount)
842         {
843             noOfMetricReportSubscribers = metricReportSubCount;
844             if (noOfMetricReportSubscribers != 0U)
845             {
846                 registerMetricReportSignal();
847             }
848             else
849             {
850                 unregisterMetricReportSignal();
851             }
852         }
853     }
854 
getSubscription(const std::string & id)855     std::shared_ptr<Subscription> getSubscription(const std::string& id)
856     {
857         auto obj = subscriptionsMap.find(id);
858         if (obj == subscriptionsMap.end())
859         {
860             BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id);
861             return nullptr;
862         }
863         std::shared_ptr<Subscription> subValue = obj->second;
864         return subValue;
865     }
866 
867     std::string
addSubscriptionInternal(const std::shared_ptr<Subscription> & subValue)868         addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue)
869     {
870         std::uniform_int_distribution<uint32_t> dist(0);
871         bmcweb::OpenSSLGenerator gen;
872 
873         std::string id;
874 
875         int retry = 3;
876         while (retry != 0)
877         {
878             id = std::to_string(dist(gen));
879             if (gen.error())
880             {
881                 retry = 0;
882                 break;
883             }
884             auto inserted = subscriptionsMap.insert(std::pair(id, subValue));
885             if (inserted.second)
886             {
887                 break;
888             }
889             --retry;
890         }
891 
892         if (retry <= 0)
893         {
894             BMCWEB_LOG_ERROR("Failed to generate random number");
895             return "";
896         }
897 
898         std::shared_ptr<persistent_data::UserSubscription> newSub =
899             std::make_shared<persistent_data::UserSubscription>();
900         newSub->id = id;
901         newSub->destinationUrl = subValue->destinationUrl;
902         newSub->protocol = subValue->protocol;
903         newSub->retryPolicy = subValue->retryPolicy;
904         newSub->customText = subValue->customText;
905         newSub->eventFormatType = subValue->eventFormatType;
906         newSub->subscriptionType = subValue->subscriptionType;
907         newSub->registryMsgIds = subValue->registryMsgIds;
908         newSub->registryPrefixes = subValue->registryPrefixes;
909         newSub->resourceTypes = subValue->resourceTypes;
910         newSub->httpHeaders = subValue->httpHeaders;
911         newSub->metricReportDefinitions = subValue->metricReportDefinitions;
912         persistent_data::EventServiceStore::getInstance()
913             .subscriptionsConfigMap.emplace(newSub->id, newSub);
914 
915         updateNoOfSubscribersCount();
916 
917         if constexpr (!BMCWEB_REDFISH_DBUS_LOG)
918         {
919             if (redfishLogFilePosition != 0)
920             {
921                 cacheRedfishLogFile();
922             }
923         }
924         // Update retry configuration.
925         subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
926 
927         // Set Subscription ID for back trace
928         subValue->setSubscriptionId(id);
929 
930         return id;
931     }
932 
933     std::string
addSSESubscription(const std::shared_ptr<Subscription> & subValue,std::string_view lastEventId)934         addSSESubscription(const std::shared_ptr<Subscription>& subValue,
935                            std::string_view lastEventId)
936     {
937         std::string id = addSubscriptionInternal(subValue);
938 
939         if (!lastEventId.empty())
940         {
941             BMCWEB_LOG_INFO("Attempting to find message for last id {}",
942                             lastEventId);
943             boost::circular_buffer<Event>::iterator lastEvent =
944                 std::find_if(messages.begin(), messages.end(),
945                              [&lastEventId](const Event& event) {
946                                  return event.id == lastEventId;
947                              });
948             // Can't find a matching ID
949             if (lastEvent == messages.end())
950             {
951                 nlohmann::json msg = messages::eventBufferExceeded();
952                 // If the buffer overloaded, send all messages.
953                 subValue->sendEventToSubscriber(msg);
954                 lastEvent = messages.begin();
955             }
956             else
957             {
958                 // Skip the last event the user already has
959                 lastEvent++;
960             }
961 
962             for (boost::circular_buffer<Event>::const_iterator event =
963                      lastEvent;
964                  lastEvent != messages.end(); lastEvent++)
965             {
966                 subValue->sendEventToSubscriber(event->message);
967             }
968         }
969         return id;
970     }
971 
972     std::string
addPushSubscription(const std::shared_ptr<Subscription> & subValue)973         addPushSubscription(const std::shared_ptr<Subscription>& subValue)
974     {
975         std::string id = addSubscriptionInternal(subValue);
976 
977         updateSubscriptionData();
978         return id;
979     }
980 
isSubscriptionExist(const std::string & id)981     bool isSubscriptionExist(const std::string& id)
982     {
983         auto obj = subscriptionsMap.find(id);
984         return obj != subscriptionsMap.end();
985     }
986 
deleteSubscription(const std::string & id)987     void deleteSubscription(const std::string& id)
988     {
989         auto obj = subscriptionsMap.find(id);
990         if (obj != subscriptionsMap.end())
991         {
992             subscriptionsMap.erase(obj);
993             auto obj2 = persistent_data::EventServiceStore::getInstance()
994                             .subscriptionsConfigMap.find(id);
995             persistent_data::EventServiceStore::getInstance()
996                 .subscriptionsConfigMap.erase(obj2);
997             updateNoOfSubscribersCount();
998             updateSubscriptionData();
999         }
1000     }
1001 
deleteSseSubscription(const crow::sse_socket::Connection & thisConn)1002     void deleteSseSubscription(const crow::sse_socket::Connection& thisConn)
1003     {
1004         for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();)
1005         {
1006             std::shared_ptr<Subscription> entry = it->second;
1007             bool entryIsThisConn = entry->matchSseId(thisConn);
1008             if (entryIsThisConn)
1009             {
1010                 persistent_data::EventServiceStore::getInstance()
1011                     .subscriptionsConfigMap.erase(
1012                         it->second->getSubscriptionId());
1013                 it = subscriptionsMap.erase(it);
1014                 return;
1015             }
1016             it++;
1017         }
1018     }
1019 
getNumberOfSubscriptions() const1020     size_t getNumberOfSubscriptions() const
1021     {
1022         return subscriptionsMap.size();
1023     }
1024 
getNumberOfSSESubscriptions() const1025     size_t getNumberOfSSESubscriptions() const
1026     {
1027         auto size = std::ranges::count_if(
1028             subscriptionsMap,
1029             [](const std::pair<std::string, std::shared_ptr<Subscription>>&
1030                    entry) {
1031                 return (entry.second->subscriptionType == subscriptionTypeSSE);
1032             });
1033         return static_cast<size_t>(size);
1034     }
1035 
getAllIDs()1036     std::vector<std::string> getAllIDs()
1037     {
1038         std::vector<std::string> idList;
1039         for (const auto& it : subscriptionsMap)
1040         {
1041             idList.emplace_back(it.first);
1042         }
1043         return idList;
1044     }
1045 
sendTestEventLog()1046     bool sendTestEventLog()
1047     {
1048         for (const auto& it : subscriptionsMap)
1049         {
1050             std::shared_ptr<Subscription> entry = it.second;
1051             if (!entry->sendTestEventLog())
1052             {
1053                 return false;
1054             }
1055         }
1056         return true;
1057     }
1058 
sendEvent(nlohmann::json::object_t eventMessage,std::string_view origin,std::string_view resourceType)1059     void sendEvent(nlohmann::json::object_t eventMessage,
1060                    std::string_view origin, std::string_view resourceType)
1061     {
1062         eventMessage["EventId"] = eventId;
1063 
1064         eventMessage["EventTimestamp"] =
1065             redfish::time_utils::getDateTimeOffsetNow().first;
1066         eventMessage["OriginOfCondition"] = origin;
1067 
1068         // MemberId is 0 : since we are sending one event record.
1069         eventMessage["MemberId"] = 0;
1070 
1071         messages.push_back(Event(std::to_string(eventId), eventMessage));
1072 
1073         for (auto& it : subscriptionsMap)
1074         {
1075             std::shared_ptr<Subscription>& entry = it.second;
1076             if (!entry->eventMatchesFilter(eventMessage, resourceType))
1077             {
1078                 BMCWEB_LOG_DEBUG("Filter didn't match");
1079                 continue;
1080             }
1081 
1082             nlohmann::json::array_t eventRecord;
1083             eventRecord.emplace_back(eventMessage);
1084 
1085             nlohmann::json msgJson;
1086 
1087             msgJson["@odata.type"] = "#Event.v1_4_0.Event";
1088             msgJson["Name"] = "Event Log";
1089             msgJson["Id"] = eventId;
1090             msgJson["Events"] = std::move(eventRecord);
1091 
1092             std::string strMsg = msgJson.dump(
1093                 2, ' ', true, nlohmann::json::error_handler_t::replace);
1094             entry->sendEventToSubscriber(std::move(strMsg));
1095             eventId++; // increment the eventId
1096         }
1097     }
1098 
resetRedfishFilePosition()1099     void resetRedfishFilePosition()
1100     {
1101         // Control would be here when Redfish file is created.
1102         // Reset File Position as new file is created
1103         redfishLogFilePosition = 0;
1104     }
1105 
cacheRedfishLogFile()1106     void cacheRedfishLogFile()
1107     {
1108         // Open the redfish file and read till the last record.
1109 
1110         std::ifstream logStream(redfishEventLogFile);
1111         if (!logStream.good())
1112         {
1113             BMCWEB_LOG_ERROR(" Redfish log file open failed ");
1114             return;
1115         }
1116         std::string logEntry;
1117         while (std::getline(logStream, logEntry))
1118         {
1119             redfishLogFilePosition = logStream.tellg();
1120         }
1121     }
1122 
readEventLogsFromFile()1123     void readEventLogsFromFile()
1124     {
1125         std::ifstream logStream(redfishEventLogFile);
1126         if (!logStream.good())
1127         {
1128             BMCWEB_LOG_ERROR(" Redfish log file open failed");
1129             return;
1130         }
1131 
1132         std::vector<EventLogObjectsType> eventRecords;
1133 
1134         std::string logEntry;
1135 
1136         BMCWEB_LOG_DEBUG("Redfish log file: seek to {}",
1137                          static_cast<int>(redfishLogFilePosition));
1138 
1139         // Get the read pointer to the next log to be read.
1140         logStream.seekg(redfishLogFilePosition);
1141 
1142         while (std::getline(logStream, logEntry))
1143         {
1144             BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry");
1145             // Update Pointer position
1146             redfishLogFilePosition = logStream.tellg();
1147 
1148             std::string idStr;
1149             if (!event_log::getUniqueEntryID(logEntry, idStr))
1150             {
1151                 BMCWEB_LOG_DEBUG(
1152                     "Redfish log file: could not get unique entry id for {}",
1153                     logEntry);
1154                 continue;
1155             }
1156 
1157             if (!serviceEnabled || noOfEventLogSubscribers == 0)
1158             {
1159                 // If Service is not enabled, no need to compute
1160                 // the remaining items below.
1161                 // But, Loop must continue to keep track of Timestamp
1162                 BMCWEB_LOG_DEBUG(
1163                     "Redfish log file: no subscribers / event service not enabled");
1164                 continue;
1165             }
1166 
1167             std::string timestamp;
1168             std::string messageID;
1169             std::vector<std::string> messageArgs;
1170             if (event_log::getEventLogParams(logEntry, timestamp, messageID,
1171                                              messageArgs) != 0)
1172             {
1173                 BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}",
1174                                  logEntry);
1175                 continue;
1176             }
1177 
1178             eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs);
1179         }
1180 
1181         if (!serviceEnabled || noOfEventLogSubscribers == 0)
1182         {
1183             BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions.");
1184             return;
1185         }
1186 
1187         if (eventRecords.empty())
1188         {
1189             // No Records to send
1190             BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
1191             return;
1192         }
1193 
1194         for (const auto& it : subscriptionsMap)
1195         {
1196             std::shared_ptr<Subscription> entry = it.second;
1197             if (entry->eventFormatType == "Event")
1198             {
1199                 entry->filterAndSendEventLogs(eventRecords);
1200             }
1201         }
1202     }
1203 
watchRedfishEventLogFile()1204     static void watchRedfishEventLogFile()
1205     {
1206         if (!inotifyConn)
1207         {
1208             BMCWEB_LOG_ERROR("inotify Connection is not present");
1209             return;
1210         }
1211 
1212         static std::array<char, 1024> readBuffer;
1213 
1214         inotifyConn->async_read_some(
1215             boost::asio::buffer(readBuffer),
1216             [&](const boost::system::error_code& ec,
1217                 const std::size_t& bytesTransferred) {
1218                 if (ec == boost::asio::error::operation_aborted)
1219                 {
1220                     BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
1221                     return;
1222                 }
1223                 if (ec)
1224                 {
1225                     BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
1226                     return;
1227                 }
1228 
1229                 BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
1230 
1231                 std::size_t index = 0;
1232                 while ((index + iEventSize) <= bytesTransferred)
1233                 {
1234                     struct inotify_event event
1235                     {};
1236                     std::memcpy(&event, &readBuffer[index], iEventSize);
1237                     if (event.wd == dirWatchDesc)
1238                     {
1239                         if ((event.len == 0) ||
1240                             (index + iEventSize + event.len > bytesTransferred))
1241                         {
1242                             index += (iEventSize + event.len);
1243                             continue;
1244                         }
1245 
1246                         std::string fileName(&readBuffer[index + iEventSize]);
1247                         if (fileName != "redfish")
1248                         {
1249                             index += (iEventSize + event.len);
1250                             continue;
1251                         }
1252 
1253                         BMCWEB_LOG_DEBUG(
1254                             "Redfish log file created/deleted. event.name: {}",
1255                             fileName);
1256                         if (event.mask == IN_CREATE)
1257                         {
1258                             if (fileWatchDesc != -1)
1259                             {
1260                                 BMCWEB_LOG_DEBUG(
1261                                     "Remove and Add inotify watcher on "
1262                                     "redfish event log file");
1263                                 // Remove existing inotify watcher and add
1264                                 // with new redfish event log file.
1265                                 inotify_rm_watch(inotifyFd, fileWatchDesc);
1266                                 fileWatchDesc = -1;
1267                             }
1268 
1269                             fileWatchDesc = inotify_add_watch(
1270                                 inotifyFd, redfishEventLogFile, IN_MODIFY);
1271                             if (fileWatchDesc == -1)
1272                             {
1273                                 BMCWEB_LOG_ERROR("inotify_add_watch failed for "
1274                                                  "redfish log file.");
1275                                 return;
1276                             }
1277 
1278                             EventServiceManager::getInstance()
1279                                 .resetRedfishFilePosition();
1280                             EventServiceManager::getInstance()
1281                                 .readEventLogsFromFile();
1282                         }
1283                         else if ((event.mask == IN_DELETE) ||
1284                                  (event.mask == IN_MOVED_TO))
1285                         {
1286                             if (fileWatchDesc != -1)
1287                             {
1288                                 inotify_rm_watch(inotifyFd, fileWatchDesc);
1289                                 fileWatchDesc = -1;
1290                             }
1291                         }
1292                     }
1293                     else if (event.wd == fileWatchDesc)
1294                     {
1295                         if (event.mask == IN_MODIFY)
1296                         {
1297                             EventServiceManager::getInstance()
1298                                 .readEventLogsFromFile();
1299                         }
1300                     }
1301                     index += (iEventSize + event.len);
1302                 }
1303 
1304                 watchRedfishEventLogFile();
1305             });
1306     }
1307 
startEventLogMonitor(boost::asio::io_context & ioc)1308     static int startEventLogMonitor(boost::asio::io_context& ioc)
1309     {
1310         BMCWEB_LOG_DEBUG("starting Event Log Monitor");
1311 
1312         inotifyConn.emplace(ioc);
1313         inotifyFd = inotify_init1(IN_NONBLOCK);
1314         if (inotifyFd == -1)
1315         {
1316             BMCWEB_LOG_ERROR("inotify_init1 failed.");
1317             return -1;
1318         }
1319 
1320         // Add watch on directory to handle redfish event log file
1321         // create/delete.
1322         dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
1323                                          IN_CREATE | IN_MOVED_TO | IN_DELETE);
1324         if (dirWatchDesc == -1)
1325         {
1326             BMCWEB_LOG_ERROR(
1327                 "inotify_add_watch failed for event log directory.");
1328             return -1;
1329         }
1330 
1331         // Watch redfish event log file for modifications.
1332         fileWatchDesc =
1333             inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
1334         if (fileWatchDesc == -1)
1335         {
1336             BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
1337             // Don't return error if file not exist.
1338             // Watch on directory will handle create/delete of file.
1339         }
1340 
1341         // monitor redfish event log file
1342         inotifyConn->assign(inotifyFd);
1343         watchRedfishEventLogFile();
1344 
1345         return 0;
1346     }
1347 
stopEventLogMonitor()1348     static void stopEventLogMonitor()
1349     {
1350         inotifyConn.reset();
1351     }
1352 
getReadingsForReport(sdbusplus::message_t & msg)1353     static void getReadingsForReport(sdbusplus::message_t& msg)
1354     {
1355         if (msg.is_method_error())
1356         {
1357             BMCWEB_LOG_ERROR("TelemetryMonitor Signal error");
1358             return;
1359         }
1360 
1361         sdbusplus::message::object_path path(msg.get_path());
1362         std::string id = path.filename();
1363         if (id.empty())
1364         {
1365             BMCWEB_LOG_ERROR("Failed to get Id from path");
1366             return;
1367         }
1368 
1369         std::string interface;
1370         dbus::utility::DBusPropertiesMap props;
1371         std::vector<std::string> invalidProps;
1372         msg.read(interface, props, invalidProps);
1373 
1374         auto found = std::ranges::find_if(props, [](const auto& x) {
1375             return x.first == "Readings";
1376         });
1377         if (found == props.end())
1378         {
1379             BMCWEB_LOG_INFO("Failed to get Readings from Report properties");
1380             return;
1381         }
1382 
1383         const telemetry::TimestampReadings* readings =
1384             std::get_if<telemetry::TimestampReadings>(&found->second);
1385         if (readings == nullptr)
1386         {
1387             BMCWEB_LOG_INFO("Failed to get Readings from Report properties");
1388             return;
1389         }
1390 
1391         for (const auto& it :
1392              EventServiceManager::getInstance().subscriptionsMap)
1393         {
1394             Subscription& entry = *it.second;
1395             if (entry.eventFormatType == metricReportFormatType)
1396             {
1397                 entry.filterAndSendReports(id, *readings);
1398             }
1399         }
1400     }
1401 
unregisterMetricReportSignal()1402     void unregisterMetricReportSignal()
1403     {
1404         if (matchTelemetryMonitor)
1405         {
1406             BMCWEB_LOG_DEBUG("Metrics report signal - Unregister");
1407             matchTelemetryMonitor.reset();
1408             matchTelemetryMonitor = nullptr;
1409         }
1410     }
1411 
registerMetricReportSignal()1412     void registerMetricReportSignal()
1413     {
1414         if (!serviceEnabled || matchTelemetryMonitor)
1415         {
1416             BMCWEB_LOG_DEBUG("Not registering metric report signal.");
1417             return;
1418         }
1419 
1420         BMCWEB_LOG_DEBUG("Metrics report signal - Register");
1421         std::string matchStr = "type='signal',member='PropertiesChanged',"
1422                                "interface='org.freedesktop.DBus.Properties',"
1423                                "arg0=xyz.openbmc_project.Telemetry.Report";
1424 
1425         matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>(
1426             *crow::connections::systemBus, matchStr, getReadingsForReport);
1427     }
1428 };
1429 
1430 } // namespace redfish
1431