xref: /openbmc/bmcweb/features/redfish/include/event_service_manager.hpp (revision b80ba2e4b0aad093f27d05fabc1cee1fac650d57)
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_log.hpp"
20 #include "event_matches_filter.hpp"
21 #include "event_service_store.hpp"
22 #include "metric_report.hpp"
23 #include "ossl_random.hpp"
24 #include "persistent_data.hpp"
25 #include "registries.hpp"
26 #include "registries_selector.hpp"
27 #include "str_utility.hpp"
28 #include "subscription.hpp"
29 #include "utils/time_utils.hpp"
30 
31 #include <sys/inotify.h>
32 
33 #include <boost/asio/io_context.hpp>
34 #include <boost/circular_buffer.hpp>
35 #include <boost/container/flat_map.hpp>
36 #include <boost/url/format.hpp>
37 #include <boost/url/url_view_base.hpp>
38 #include <sdbusplus/bus/match.hpp>
39 
40 #include <algorithm>
41 #include <cstdlib>
42 #include <ctime>
43 #include <format>
44 #include <fstream>
45 #include <memory>
46 #include <span>
47 #include <string>
48 #include <string_view>
49 #include <utility>
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* eventServiceFile =
58     "/var/lib/bmcweb/eventservice_config.json";
59 
60 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
61 static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
62 static constexpr const char* redfishEventLogDir = "/var/log";
63 static constexpr const char* redfishEventLogFile = "/var/log/redfish";
64 static constexpr const size_t iEventSize = sizeof(inotify_event);
65 
66 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
67 static int inotifyFd = -1;
68 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
69 static int dirWatchDesc = -1;
70 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
71 static int fileWatchDesc = -1;
72 
73 namespace registries
74 {
75 inline const Message*
76     getMsgFromRegistry(const std::string& messageKey,
77                        const std::span<const MessageEntry>& registry)
78 {
79     std::span<const MessageEntry>::iterator messageIt = std::ranges::find_if(
80         registry, [&messageKey](const MessageEntry& messageEntry) {
81             return messageKey == messageEntry.first;
82         });
83     if (messageIt != registry.end())
84     {
85         return &messageIt->second;
86     }
87 
88     return nullptr;
89 }
90 
91 inline const Message* formatMessage(std::string_view messageID)
92 {
93     // Redfish MessageIds are in the form
94     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
95     // the right Message
96     std::vector<std::string> fields;
97     fields.reserve(4);
98 
99     bmcweb::split(fields, messageID, '.');
100     if (fields.size() != 4)
101     {
102         return nullptr;
103     }
104     const std::string& registryName = fields[0];
105     const std::string& messageKey = fields[3];
106 
107     // Find the right registry and check it for the MessageKey
108     return getMsgFromRegistry(messageKey, getRegistryFromPrefix(registryName));
109 }
110 } // namespace registries
111 
112 class EventServiceManager
113 {
114   private:
115     bool serviceEnabled = false;
116     uint32_t retryAttempts = 0;
117     uint32_t retryTimeoutInterval = 0;
118 
119     std::streampos redfishLogFilePosition{0};
120     size_t noOfEventLogSubscribers{0};
121     size_t noOfMetricReportSubscribers{0};
122     std::shared_ptr<sdbusplus::bus::match_t> matchTelemetryMonitor;
123     boost::container::flat_map<std::string, std::shared_ptr<Subscription>>
124         subscriptionsMap;
125 
126     uint64_t eventId{1};
127 
128     struct Event
129     {
130         std::string id;
131         nlohmann::json message;
132     };
133 
134     constexpr static size_t maxMessages = 200;
135     boost::circular_buffer<Event> messages{maxMessages};
136 
137     boost::asio::io_context& ioc;
138 
139   public:
140     EventServiceManager(const EventServiceManager&) = delete;
141     EventServiceManager& operator=(const EventServiceManager&) = delete;
142     EventServiceManager(EventServiceManager&&) = delete;
143     EventServiceManager& operator=(EventServiceManager&&) = delete;
144     ~EventServiceManager() = default;
145 
146     explicit EventServiceManager(boost::asio::io_context& iocIn) : ioc(iocIn)
147     {
148         // Load config from persist store.
149         initConfig();
150     }
151 
152     static EventServiceManager&
153         getInstance(boost::asio::io_context* ioc = nullptr)
154     {
155         static EventServiceManager handler(*ioc);
156         return handler;
157     }
158 
159     void initConfig()
160     {
161         loadOldBehavior();
162 
163         persistent_data::EventServiceConfig eventServiceConfig =
164             persistent_data::EventServiceStore::getInstance()
165                 .getEventServiceConfig();
166 
167         serviceEnabled = eventServiceConfig.enabled;
168         retryAttempts = eventServiceConfig.retryAttempts;
169         retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval;
170 
171         for (const auto& it : persistent_data::EventServiceStore::getInstance()
172                                   .subscriptionsConfigMap)
173         {
174             std::shared_ptr<persistent_data::UserSubscription> newSub =
175                 it.second;
176 
177             boost::system::result<boost::urls::url> url =
178                 boost::urls::parse_absolute_uri(newSub->destinationUrl);
179 
180             if (!url)
181             {
182                 BMCWEB_LOG_ERROR(
183                     "Failed to validate and split destination url");
184                 continue;
185             }
186             std::shared_ptr<Subscription> subValue =
187                 std::make_shared<Subscription>(newSub, *url, ioc);
188             std::string id = subValue->userSub->id;
189             subValue->deleter = [id]() {
190                 EventServiceManager::getInstance().deleteSubscription(id);
191             };
192 
193             subscriptionsMap.emplace(id, subValue);
194 
195             updateNoOfSubscribersCount();
196 
197             if constexpr (!BMCWEB_REDFISH_DBUS_LOG)
198             {
199                 cacheRedfishLogFile();
200             }
201 
202             // Update retry configuration.
203             subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
204         }
205     }
206 
207     static void loadOldBehavior()
208     {
209         std::ifstream eventConfigFile(eventServiceFile);
210         if (!eventConfigFile.good())
211         {
212             BMCWEB_LOG_DEBUG("Old eventService config not exist");
213             return;
214         }
215         auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false);
216         if (jsonData.is_discarded())
217         {
218             BMCWEB_LOG_ERROR("Old eventService config parse error.");
219             return;
220         }
221 
222         const nlohmann::json::object_t* obj =
223             jsonData.get_ptr<const nlohmann::json::object_t*>();
224         for (const auto& item : *obj)
225         {
226             if (item.first == "Configuration")
227             {
228                 persistent_data::EventServiceStore::getInstance()
229                     .getEventServiceConfig()
230                     .fromJson(item.second);
231             }
232             else if (item.first == "Subscriptions")
233             {
234                 for (const auto& elem : item.second)
235                 {
236                     std::optional<persistent_data::UserSubscription>
237                         newSubscription =
238                             persistent_data::UserSubscription::fromJson(elem,
239                                                                         true);
240                     if (!newSubscription)
241                     {
242                         BMCWEB_LOG_ERROR("Problem reading subscription "
243                                          "from old persistent store");
244                         continue;
245                     }
246                     persistent_data::UserSubscription& newSub =
247                         *newSubscription;
248 
249                     std::uniform_int_distribution<uint32_t> dist(0);
250                     bmcweb::OpenSSLGenerator gen;
251 
252                     std::string id;
253 
254                     int retry = 3;
255                     while (retry != 0)
256                     {
257                         id = std::to_string(dist(gen));
258                         if (gen.error())
259                         {
260                             retry = 0;
261                             break;
262                         }
263                         newSub.id = id;
264                         auto inserted =
265                             persistent_data::EventServiceStore::getInstance()
266                                 .subscriptionsConfigMap.insert(std::pair(
267                                     id, std::make_shared<
268                                             persistent_data::UserSubscription>(
269                                             newSub)));
270                         if (inserted.second)
271                         {
272                             break;
273                         }
274                         --retry;
275                     }
276 
277                     if (retry <= 0)
278                     {
279                         BMCWEB_LOG_ERROR(
280                             "Failed to generate random number from old "
281                             "persistent store");
282                         continue;
283                     }
284                 }
285             }
286 
287             persistent_data::getConfig().writeData();
288             std::error_code ec;
289             std::filesystem::remove(eventServiceFile, ec);
290             if (ec)
291             {
292                 BMCWEB_LOG_DEBUG(
293                     "Failed to remove old event service file.  Ignoring");
294             }
295             else
296             {
297                 BMCWEB_LOG_DEBUG("Remove old eventservice config");
298             }
299         }
300     }
301 
302     void updateSubscriptionData() const
303     {
304         persistent_data::EventServiceStore::getInstance()
305             .eventServiceConfig.enabled = serviceEnabled;
306         persistent_data::EventServiceStore::getInstance()
307             .eventServiceConfig.retryAttempts = retryAttempts;
308         persistent_data::EventServiceStore::getInstance()
309             .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval;
310 
311         persistent_data::getConfig().writeData();
312     }
313 
314     void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg)
315     {
316         bool updateConfig = false;
317         bool updateRetryCfg = false;
318 
319         if (serviceEnabled != cfg.enabled)
320         {
321             serviceEnabled = cfg.enabled;
322             if (serviceEnabled && noOfMetricReportSubscribers != 0U)
323             {
324                 registerMetricReportSignal();
325             }
326             else
327             {
328                 unregisterMetricReportSignal();
329             }
330             updateConfig = true;
331         }
332 
333         if (retryAttempts != cfg.retryAttempts)
334         {
335             retryAttempts = cfg.retryAttempts;
336             updateConfig = true;
337             updateRetryCfg = true;
338         }
339 
340         if (retryTimeoutInterval != cfg.retryTimeoutInterval)
341         {
342             retryTimeoutInterval = cfg.retryTimeoutInterval;
343             updateConfig = true;
344             updateRetryCfg = true;
345         }
346 
347         if (updateConfig)
348         {
349             updateSubscriptionData();
350         }
351 
352         if (updateRetryCfg)
353         {
354             // Update the changed retry config to all subscriptions
355             for (const auto& it :
356                  EventServiceManager::getInstance().subscriptionsMap)
357             {
358                 Subscription& entry = *it.second;
359                 entry.updateRetryConfig(retryAttempts, retryTimeoutInterval);
360             }
361         }
362     }
363 
364     void updateNoOfSubscribersCount()
365     {
366         size_t eventLogSubCount = 0;
367         size_t metricReportSubCount = 0;
368         for (const auto& it : subscriptionsMap)
369         {
370             std::shared_ptr<Subscription> entry = it.second;
371             if (entry->userSub->eventFormatType == eventFormatType)
372             {
373                 eventLogSubCount++;
374             }
375             else if (entry->userSub->eventFormatType == metricReportFormatType)
376             {
377                 metricReportSubCount++;
378             }
379         }
380 
381         noOfEventLogSubscribers = eventLogSubCount;
382         if (noOfMetricReportSubscribers != metricReportSubCount)
383         {
384             noOfMetricReportSubscribers = metricReportSubCount;
385             if (noOfMetricReportSubscribers != 0U)
386             {
387                 registerMetricReportSignal();
388             }
389             else
390             {
391                 unregisterMetricReportSignal();
392             }
393         }
394     }
395 
396     std::shared_ptr<Subscription> getSubscription(const std::string& id)
397     {
398         auto obj = subscriptionsMap.find(id);
399         if (obj == subscriptionsMap.end())
400         {
401             BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id);
402             return nullptr;
403         }
404         std::shared_ptr<Subscription> subValue = obj->second;
405         return subValue;
406     }
407 
408     std::string
409         addSubscriptionInternal(const std::shared_ptr<Subscription>& subValue)
410     {
411         std::uniform_int_distribution<uint32_t> dist(0);
412         bmcweb::OpenSSLGenerator gen;
413 
414         std::string id;
415 
416         int retry = 3;
417         while (retry != 0)
418         {
419             id = std::to_string(dist(gen));
420             if (gen.error())
421             {
422                 retry = 0;
423                 break;
424             }
425             auto inserted = subscriptionsMap.insert(std::pair(id, subValue));
426             if (inserted.second)
427             {
428                 break;
429             }
430             --retry;
431         }
432 
433         if (retry <= 0)
434         {
435             BMCWEB_LOG_ERROR("Failed to generate random number");
436             return "";
437         }
438 
439         // Set Subscription ID for back trace
440         subValue->userSub->id = id;
441 
442         persistent_data::EventServiceStore::getInstance()
443             .subscriptionsConfigMap.emplace(id, subValue->userSub);
444 
445         updateNoOfSubscribersCount();
446 
447         if constexpr (!BMCWEB_REDFISH_DBUS_LOG)
448         {
449             if (redfishLogFilePosition != 0)
450             {
451                 cacheRedfishLogFile();
452             }
453         }
454         // Update retry configuration.
455         subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
456 
457         return id;
458     }
459 
460     std::string
461         addSSESubscription(const std::shared_ptr<Subscription>& subValue,
462                            std::string_view lastEventId)
463     {
464         std::string id = addSubscriptionInternal(subValue);
465 
466         if (!lastEventId.empty())
467         {
468             BMCWEB_LOG_INFO("Attempting to find message for last id {}",
469                             lastEventId);
470             boost::circular_buffer<Event>::iterator lastEvent =
471                 std::find_if(messages.begin(), messages.end(),
472                              [&lastEventId](const Event& event) {
473                                  return event.id == lastEventId;
474                              });
475             // Can't find a matching ID
476             if (lastEvent == messages.end())
477             {
478                 nlohmann::json msg = messages::eventBufferExceeded();
479                 // If the buffer overloaded, send all messages.
480                 subValue->sendEventToSubscriber(msg);
481                 lastEvent = messages.begin();
482             }
483             else
484             {
485                 // Skip the last event the user already has
486                 lastEvent++;
487             }
488 
489             for (boost::circular_buffer<Event>::const_iterator event =
490                      lastEvent;
491                  lastEvent != messages.end(); lastEvent++)
492             {
493                 subValue->sendEventToSubscriber(event->message);
494             }
495         }
496         return id;
497     }
498 
499     std::string
500         addPushSubscription(const std::shared_ptr<Subscription>& subValue)
501     {
502         std::string id = addSubscriptionInternal(subValue);
503         subValue->deleter = [id]() {
504             EventServiceManager::getInstance().deleteSubscription(id);
505         };
506         updateSubscriptionData();
507         return id;
508     }
509 
510     bool isSubscriptionExist(const std::string& id)
511     {
512         auto obj = subscriptionsMap.find(id);
513         return obj != subscriptionsMap.end();
514     }
515 
516     bool deleteSubscription(const std::string& id)
517     {
518         auto obj = subscriptionsMap.find(id);
519         if (obj == subscriptionsMap.end())
520         {
521             BMCWEB_LOG_WARNING("Could not find subscription with id {}", id);
522             return false;
523         }
524         subscriptionsMap.erase(obj);
525         auto& event = persistent_data::EventServiceStore::getInstance();
526         auto persistentObj = event.subscriptionsConfigMap.find(id);
527         if (persistentObj == event.subscriptionsConfigMap.end())
528         {
529             BMCWEB_LOG_ERROR("Subscription wasn't in persistent data");
530             return true;
531         }
532         persistent_data::EventServiceStore::getInstance()
533             .subscriptionsConfigMap.erase(persistentObj);
534         updateNoOfSubscribersCount();
535         updateSubscriptionData();
536 
537         return true;
538     }
539 
540     void deleteSseSubscription(const crow::sse_socket::Connection& thisConn)
541     {
542         for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();)
543         {
544             std::shared_ptr<Subscription> entry = it->second;
545             bool entryIsThisConn = entry->matchSseId(thisConn);
546             if (entryIsThisConn)
547             {
548                 persistent_data::EventServiceStore::getInstance()
549                     .subscriptionsConfigMap.erase(entry->userSub->id);
550                 it = subscriptionsMap.erase(it);
551                 return;
552             }
553             it++;
554         }
555     }
556 
557     size_t getNumberOfSubscriptions() const
558     {
559         return subscriptionsMap.size();
560     }
561 
562     size_t getNumberOfSSESubscriptions() const
563     {
564         auto size = std::ranges::count_if(
565             subscriptionsMap,
566             [](const std::pair<std::string, std::shared_ptr<Subscription>>&
567                    entry) {
568                 return (entry.second->userSub->subscriptionType ==
569                         subscriptionTypeSSE);
570             });
571         return static_cast<size_t>(size);
572     }
573 
574     std::vector<std::string> getAllIDs()
575     {
576         std::vector<std::string> idList;
577         for (const auto& it : subscriptionsMap)
578         {
579             idList.emplace_back(it.first);
580         }
581         return idList;
582     }
583 
584     bool sendTestEventLog()
585     {
586         for (const auto& it : subscriptionsMap)
587         {
588             std::shared_ptr<Subscription> entry = it.second;
589             if (!entry->sendTestEventLog())
590             {
591                 return false;
592             }
593         }
594         return true;
595     }
596 
597     void sendEvent(nlohmann::json::object_t eventMessage,
598                    std::string_view origin, std::string_view resourceType)
599     {
600         eventMessage["EventId"] = eventId;
601 
602         eventMessage["EventTimestamp"] =
603             redfish::time_utils::getDateTimeOffsetNow().first;
604         eventMessage["OriginOfCondition"] = origin;
605 
606         // MemberId is 0 : since we are sending one event record.
607         eventMessage["MemberId"] = "0";
608 
609         messages.push_back(Event(std::to_string(eventId), eventMessage));
610 
611         for (auto& it : subscriptionsMap)
612         {
613             std::shared_ptr<Subscription>& entry = it.second;
614             if (!eventMatchesFilter(*entry->userSub, eventMessage,
615                                     resourceType))
616             {
617                 BMCWEB_LOG_DEBUG("Filter didn't match");
618                 continue;
619             }
620 
621             nlohmann::json::array_t eventRecord;
622             eventRecord.emplace_back(eventMessage);
623 
624             nlohmann::json msgJson;
625 
626             msgJson["@odata.type"] = "#Event.v1_4_0.Event";
627             msgJson["Name"] = "Event Log";
628             msgJson["Id"] = eventId;
629             msgJson["Events"] = std::move(eventRecord);
630 
631             std::string strMsg = msgJson.dump(
632                 2, ' ', true, nlohmann::json::error_handler_t::replace);
633             entry->sendEventToSubscriber(std::move(strMsg));
634             eventId++; // increment the eventId
635         }
636     }
637 
638     void resetRedfishFilePosition()
639     {
640         // Control would be here when Redfish file is created.
641         // Reset File Position as new file is created
642         redfishLogFilePosition = 0;
643     }
644 
645     void cacheRedfishLogFile()
646     {
647         // Open the redfish file and read till the last record.
648 
649         std::ifstream logStream(redfishEventLogFile);
650         if (!logStream.good())
651         {
652             BMCWEB_LOG_ERROR(" Redfish log file open failed ");
653             return;
654         }
655         std::string logEntry;
656         while (std::getline(logStream, logEntry))
657         {
658             redfishLogFilePosition = logStream.tellg();
659         }
660     }
661 
662     void readEventLogsFromFile()
663     {
664         std::ifstream logStream(redfishEventLogFile);
665         if (!logStream.good())
666         {
667             BMCWEB_LOG_ERROR(" Redfish log file open failed");
668             return;
669         }
670 
671         std::vector<EventLogObjectsType> eventRecords;
672 
673         std::string logEntry;
674 
675         BMCWEB_LOG_DEBUG("Redfish log file: seek to {}",
676                          static_cast<int>(redfishLogFilePosition));
677 
678         // Get the read pointer to the next log to be read.
679         logStream.seekg(redfishLogFilePosition);
680 
681         while (std::getline(logStream, logEntry))
682         {
683             BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry");
684             // Update Pointer position
685             redfishLogFilePosition = logStream.tellg();
686 
687             std::string idStr;
688             if (!event_log::getUniqueEntryID(logEntry, idStr))
689             {
690                 BMCWEB_LOG_DEBUG(
691                     "Redfish log file: could not get unique entry id for {}",
692                     logEntry);
693                 continue;
694             }
695 
696             if (!serviceEnabled || noOfEventLogSubscribers == 0)
697             {
698                 // If Service is not enabled, no need to compute
699                 // the remaining items below.
700                 // But, Loop must continue to keep track of Timestamp
701                 BMCWEB_LOG_DEBUG(
702                     "Redfish log file: no subscribers / event service not enabled");
703                 continue;
704             }
705 
706             std::string timestamp;
707             std::string messageID;
708             std::vector<std::string> messageArgs;
709             if (event_log::getEventLogParams(logEntry, timestamp, messageID,
710                                              messageArgs) != 0)
711             {
712                 BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}",
713                                  logEntry);
714                 continue;
715             }
716 
717             eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs);
718         }
719 
720         if (!serviceEnabled || noOfEventLogSubscribers == 0)
721         {
722             BMCWEB_LOG_DEBUG("EventService disabled or no Subscriptions.");
723             return;
724         }
725 
726         if (eventRecords.empty())
727         {
728             // No Records to send
729             BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
730             return;
731         }
732 
733         for (const auto& it : subscriptionsMap)
734         {
735             std::shared_ptr<Subscription> entry = it.second;
736             if (entry->userSub->eventFormatType == "Event")
737             {
738                 entry->filterAndSendEventLogs(eventRecords);
739             }
740         }
741     }
742 
743     static void watchRedfishEventLogFile()
744     {
745         if (!inotifyConn)
746         {
747             BMCWEB_LOG_ERROR("inotify Connection is not present");
748             return;
749         }
750 
751         static std::array<char, 1024> readBuffer;
752 
753         inotifyConn->async_read_some(
754             boost::asio::buffer(readBuffer),
755             [&](const boost::system::error_code& ec,
756                 const std::size_t& bytesTransferred) {
757                 if (ec == boost::asio::error::operation_aborted)
758                 {
759                     BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
760                     return;
761                 }
762                 if (ec)
763                 {
764                     BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
765                     return;
766                 }
767 
768                 BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
769 
770                 std::size_t index = 0;
771                 while ((index + iEventSize) <= bytesTransferred)
772                 {
773                     struct inotify_event event
774                     {};
775                     std::memcpy(&event, &readBuffer[index], iEventSize);
776                     if (event.wd == dirWatchDesc)
777                     {
778                         if ((event.len == 0) ||
779                             (index + iEventSize + event.len > bytesTransferred))
780                         {
781                             index += (iEventSize + event.len);
782                             continue;
783                         }
784 
785                         std::string fileName(&readBuffer[index + iEventSize]);
786                         if (fileName != "redfish")
787                         {
788                             index += (iEventSize + event.len);
789                             continue;
790                         }
791 
792                         BMCWEB_LOG_DEBUG(
793                             "Redfish log file created/deleted. event.name: {}",
794                             fileName);
795                         if (event.mask == IN_CREATE)
796                         {
797                             if (fileWatchDesc != -1)
798                             {
799                                 BMCWEB_LOG_DEBUG(
800                                     "Remove and Add inotify watcher on "
801                                     "redfish event log file");
802                                 // Remove existing inotify watcher and add
803                                 // with new redfish event log file.
804                                 inotify_rm_watch(inotifyFd, fileWatchDesc);
805                                 fileWatchDesc = -1;
806                             }
807 
808                             fileWatchDesc = inotify_add_watch(
809                                 inotifyFd, redfishEventLogFile, IN_MODIFY);
810                             if (fileWatchDesc == -1)
811                             {
812                                 BMCWEB_LOG_ERROR("inotify_add_watch failed for "
813                                                  "redfish log file.");
814                                 return;
815                             }
816 
817                             EventServiceManager::getInstance()
818                                 .resetRedfishFilePosition();
819                             EventServiceManager::getInstance()
820                                 .readEventLogsFromFile();
821                         }
822                         else if ((event.mask == IN_DELETE) ||
823                                  (event.mask == IN_MOVED_TO))
824                         {
825                             if (fileWatchDesc != -1)
826                             {
827                                 inotify_rm_watch(inotifyFd, fileWatchDesc);
828                                 fileWatchDesc = -1;
829                             }
830                         }
831                     }
832                     else if (event.wd == fileWatchDesc)
833                     {
834                         if (event.mask == IN_MODIFY)
835                         {
836                             EventServiceManager::getInstance()
837                                 .readEventLogsFromFile();
838                         }
839                     }
840                     index += (iEventSize + event.len);
841                 }
842 
843                 watchRedfishEventLogFile();
844             });
845     }
846 
847     static int startEventLogMonitor(boost::asio::io_context& ioc)
848     {
849         BMCWEB_LOG_DEBUG("starting Event Log Monitor");
850 
851         inotifyConn.emplace(ioc);
852         inotifyFd = inotify_init1(IN_NONBLOCK);
853         if (inotifyFd == -1)
854         {
855             BMCWEB_LOG_ERROR("inotify_init1 failed.");
856             return -1;
857         }
858 
859         // Add watch on directory to handle redfish event log file
860         // create/delete.
861         dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
862                                          IN_CREATE | IN_MOVED_TO | IN_DELETE);
863         if (dirWatchDesc == -1)
864         {
865             BMCWEB_LOG_ERROR(
866                 "inotify_add_watch failed for event log directory.");
867             return -1;
868         }
869 
870         // Watch redfish event log file for modifications.
871         fileWatchDesc =
872             inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
873         if (fileWatchDesc == -1)
874         {
875             BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
876             // Don't return error if file not exist.
877             // Watch on directory will handle create/delete of file.
878         }
879 
880         // monitor redfish event log file
881         inotifyConn->assign(inotifyFd);
882         watchRedfishEventLogFile();
883 
884         return 0;
885     }
886 
887     static void stopEventLogMonitor()
888     {
889         inotifyConn.reset();
890     }
891 
892     static void getReadingsForReport(sdbusplus::message_t& msg)
893     {
894         if (msg.is_method_error())
895         {
896             BMCWEB_LOG_ERROR("TelemetryMonitor Signal error");
897             return;
898         }
899 
900         sdbusplus::message::object_path path(msg.get_path());
901         std::string id = path.filename();
902         if (id.empty())
903         {
904             BMCWEB_LOG_ERROR("Failed to get Id from path");
905             return;
906         }
907 
908         std::string interface;
909         dbus::utility::DBusPropertiesMap props;
910         std::vector<std::string> invalidProps;
911         msg.read(interface, props, invalidProps);
912 
913         auto found = std::ranges::find_if(props, [](const auto& x) {
914             return x.first == "Readings";
915         });
916         if (found == props.end())
917         {
918             BMCWEB_LOG_INFO("Failed to get Readings from Report properties");
919             return;
920         }
921 
922         const telemetry::TimestampReadings* readings =
923             std::get_if<telemetry::TimestampReadings>(&found->second);
924         if (readings == nullptr)
925         {
926             BMCWEB_LOG_INFO("Failed to get Readings from Report properties");
927             return;
928         }
929 
930         for (const auto& it :
931              EventServiceManager::getInstance().subscriptionsMap)
932         {
933             Subscription& entry = *it.second;
934             if (entry.userSub->eventFormatType == metricReportFormatType)
935             {
936                 entry.filterAndSendReports(id, *readings);
937             }
938         }
939     }
940 
941     void unregisterMetricReportSignal()
942     {
943         if (matchTelemetryMonitor)
944         {
945             BMCWEB_LOG_DEBUG("Metrics report signal - Unregister");
946             matchTelemetryMonitor.reset();
947             matchTelemetryMonitor = nullptr;
948         }
949     }
950 
951     void registerMetricReportSignal()
952     {
953         if (!serviceEnabled || matchTelemetryMonitor)
954         {
955             BMCWEB_LOG_DEBUG("Not registering metric report signal.");
956             return;
957         }
958 
959         BMCWEB_LOG_DEBUG("Metrics report signal - Register");
960         std::string matchStr = "type='signal',member='PropertiesChanged',"
961                                "interface='org.freedesktop.DBus.Properties',"
962                                "arg0=xyz.openbmc_project.Telemetry.Report";
963 
964         matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match_t>(
965             *crow::connections::systemBus, matchStr, getReadingsForReport);
966     }
967 };
968 
969 } // namespace redfish
970