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