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