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