1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4 #pragma once 5 6 #include "app.hpp" 7 #include "dbus_utility.hpp" 8 #include "error_messages.hpp" 9 #include "generated/enums/log_entry.hpp" 10 #include "generated/enums/log_service.hpp" 11 #include "gzfile.hpp" 12 #include "http_utility.hpp" 13 #include "human_sort.hpp" 14 #include "query.hpp" 15 #include "registries.hpp" 16 #include "registries/base_message_registry.hpp" 17 #include "registries/openbmc_message_registry.hpp" 18 #include "registries/privilege_registry.hpp" 19 #include "task.hpp" 20 #include "task_messages.hpp" 21 #include "utils/dbus_event_log_entry.hpp" 22 #include "utils/dbus_utils.hpp" 23 #include "utils/json_utils.hpp" 24 #include "utils/time_utils.hpp" 25 26 #include <systemd/sd-id128.h> 27 #include <tinyxml2.h> 28 #include <unistd.h> 29 30 #include <boost/beast/http/verb.hpp> 31 #include <boost/container/flat_map.hpp> 32 #include <boost/system/linux_error.hpp> 33 #include <boost/url/format.hpp> 34 #include <sdbusplus/asio/property.hpp> 35 #include <sdbusplus/unpack_properties.hpp> 36 37 #include <array> 38 #include <charconv> 39 #include <cstddef> 40 #include <filesystem> 41 #include <iterator> 42 #include <optional> 43 #include <ranges> 44 #include <span> 45 #include <string> 46 #include <string_view> 47 #include <variant> 48 49 namespace redfish 50 { 51 52 constexpr const char* crashdumpObject = "com.intel.crashdump"; 53 constexpr const char* crashdumpPath = "/com/intel/crashdump"; 54 constexpr const char* crashdumpInterface = "com.intel.crashdump"; 55 constexpr const char* deleteAllInterface = 56 "xyz.openbmc_project.Collection.DeleteAll"; 57 constexpr const char* crashdumpOnDemandInterface = 58 "com.intel.crashdump.OnDemand"; 59 constexpr const char* crashdumpTelemetryInterface = 60 "com.intel.crashdump.Telemetry"; 61 62 enum class DumpCreationProgress 63 { 64 DUMP_CREATE_SUCCESS, 65 DUMP_CREATE_FAILED, 66 DUMP_CREATE_INPROGRESS 67 }; 68 69 namespace fs = std::filesystem; 70 71 inline std::string translateSeverityDbusToRedfish(const std::string& s) 72 { 73 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") || 74 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") || 75 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") || 76 (s == "xyz.openbmc_project.Logging.Entry.Level.Error")) 77 { 78 return "Critical"; 79 } 80 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") || 81 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") || 82 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")) 83 { 84 return "OK"; 85 } 86 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") 87 { 88 return "Warning"; 89 } 90 return ""; 91 } 92 93 inline std::optional<bool> getProviderNotifyAction(const std::string& notify) 94 { 95 std::optional<bool> notifyAction; 96 if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Notify") 97 { 98 notifyAction = true; 99 } 100 else if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Inhibit") 101 { 102 notifyAction = false; 103 } 104 105 return notifyAction; 106 } 107 108 inline std::string getDumpPath(std::string_view dumpType) 109 { 110 std::string dbusDumpPath = "/xyz/openbmc_project/dump/"; 111 std::ranges::transform(dumpType, std::back_inserter(dbusDumpPath), 112 bmcweb::asciiToLower); 113 114 return dbusDumpPath; 115 } 116 117 inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, 118 const bool firstEntry = true) 119 { 120 static time_t prevTs = 0; 121 static int index = 0; 122 if (firstEntry) 123 { 124 prevTs = 0; 125 } 126 127 // Get the entry timestamp 128 std::time_t curTs = 0; 129 std::tm timeStruct = {}; 130 std::istringstream entryStream(logEntry); 131 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 132 { 133 curTs = std::mktime(&timeStruct); 134 } 135 // If the timestamp isn't unique, increment the index 136 if (curTs == prevTs) 137 { 138 index++; 139 } 140 else 141 { 142 // Otherwise, reset it 143 index = 0; 144 } 145 // Save the timestamp 146 prevTs = curTs; 147 148 entryID = std::to_string(curTs); 149 if (index > 0) 150 { 151 entryID += "_" + std::to_string(index); 152 } 153 return true; 154 } 155 156 inline bool 157 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles) 158 { 159 static const std::filesystem::path redfishLogDir = "/var/log"; 160 static const std::string redfishLogFilename = "redfish"; 161 162 // Loop through the directory looking for redfish log files 163 for (const std::filesystem::directory_entry& dirEnt : 164 std::filesystem::directory_iterator(redfishLogDir)) 165 { 166 // If we find a redfish log file, save the path 167 std::string filename = dirEnt.path().filename(); 168 if (filename.starts_with(redfishLogFilename)) 169 { 170 redfishLogFiles.emplace_back(redfishLogDir / filename); 171 } 172 } 173 // As the log files rotate, they are appended with a ".#" that is higher for 174 // the older logs. Since we don't expect more than 10 log files, we 175 // can just sort the list to get them in order from newest to oldest 176 std::ranges::sort(redfishLogFiles); 177 178 return !redfishLogFiles.empty(); 179 } 180 181 inline log_entry::OriginatorTypes 182 mapDbusOriginatorTypeToRedfish(const std::string& originatorType) 183 { 184 if (originatorType == 185 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client") 186 { 187 return log_entry::OriginatorTypes::Client; 188 } 189 if (originatorType == 190 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Internal") 191 { 192 return log_entry::OriginatorTypes::Internal; 193 } 194 if (originatorType == 195 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.SupportingService") 196 { 197 return log_entry::OriginatorTypes::SupportingService; 198 } 199 return log_entry::OriginatorTypes::Invalid; 200 } 201 202 inline void parseDumpEntryFromDbusObject( 203 const dbus::utility::ManagedObjectType::value_type& object, 204 std::string& dumpStatus, uint64_t& size, uint64_t& timestampUs, 205 std::string& originatorId, log_entry::OriginatorTypes& originatorType, 206 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 207 { 208 for (const auto& interfaceMap : object.second) 209 { 210 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress") 211 { 212 for (const auto& propertyMap : interfaceMap.second) 213 { 214 if (propertyMap.first == "Status") 215 { 216 const auto* status = 217 std::get_if<std::string>(&propertyMap.second); 218 if (status == nullptr) 219 { 220 messages::internalError(asyncResp->res); 221 break; 222 } 223 dumpStatus = *status; 224 } 225 } 226 } 227 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry") 228 { 229 for (const auto& propertyMap : interfaceMap.second) 230 { 231 if (propertyMap.first == "Size") 232 { 233 const auto* sizePtr = 234 std::get_if<uint64_t>(&propertyMap.second); 235 if (sizePtr == nullptr) 236 { 237 messages::internalError(asyncResp->res); 238 break; 239 } 240 size = *sizePtr; 241 break; 242 } 243 } 244 } 245 else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime") 246 { 247 for (const auto& propertyMap : interfaceMap.second) 248 { 249 if (propertyMap.first == "Elapsed") 250 { 251 const uint64_t* usecsTimeStamp = 252 std::get_if<uint64_t>(&propertyMap.second); 253 if (usecsTimeStamp == nullptr) 254 { 255 messages::internalError(asyncResp->res); 256 break; 257 } 258 timestampUs = *usecsTimeStamp; 259 break; 260 } 261 } 262 } 263 else if (interfaceMap.first == 264 "xyz.openbmc_project.Common.OriginatedBy") 265 { 266 for (const auto& propertyMap : interfaceMap.second) 267 { 268 if (propertyMap.first == "OriginatorId") 269 { 270 const std::string* id = 271 std::get_if<std::string>(&propertyMap.second); 272 if (id == nullptr) 273 { 274 messages::internalError(asyncResp->res); 275 break; 276 } 277 originatorId = *id; 278 } 279 280 if (propertyMap.first == "OriginatorType") 281 { 282 const std::string* type = 283 std::get_if<std::string>(&propertyMap.second); 284 if (type == nullptr) 285 { 286 messages::internalError(asyncResp->res); 287 break; 288 } 289 290 originatorType = mapDbusOriginatorTypeToRedfish(*type); 291 if (originatorType == log_entry::OriginatorTypes::Invalid) 292 { 293 messages::internalError(asyncResp->res); 294 break; 295 } 296 } 297 } 298 } 299 } 300 } 301 302 static std::string getDumpEntriesPath(const std::string& dumpType) 303 { 304 std::string entriesPath; 305 306 if (dumpType == "BMC") 307 { 308 entriesPath = 309 std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/", 310 BMCWEB_REDFISH_MANAGER_URI_NAME); 311 } 312 else if (dumpType == "FaultLog") 313 { 314 entriesPath = 315 std::format("/redfish/v1/Managers/{}/LogServices/FaultLog/Entries/", 316 BMCWEB_REDFISH_MANAGER_URI_NAME); 317 } 318 else if (dumpType == "System") 319 { 320 entriesPath = 321 std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/", 322 BMCWEB_REDFISH_SYSTEM_URI_NAME); 323 } 324 else 325 { 326 BMCWEB_LOG_ERROR("getDumpEntriesPath() invalid dump type: {}", 327 dumpType); 328 } 329 330 // Returns empty string on error 331 return entriesPath; 332 } 333 334 inline void 335 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 336 const std::string& dumpType) 337 { 338 std::string entriesPath = getDumpEntriesPath(dumpType); 339 if (entriesPath.empty()) 340 { 341 messages::internalError(asyncResp->res); 342 return; 343 } 344 345 sdbusplus::message::object_path path("/xyz/openbmc_project/dump"); 346 dbus::utility::getManagedObjects( 347 "xyz.openbmc_project.Dump.Manager", path, 348 [asyncResp, entriesPath, 349 dumpType](const boost::system::error_code& ec, 350 const dbus::utility::ManagedObjectType& objects) { 351 if (ec) 352 { 353 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); 354 messages::internalError(asyncResp->res); 355 return; 356 } 357 358 // Remove ending slash 359 std::string odataIdStr = entriesPath; 360 if (!odataIdStr.empty()) 361 { 362 odataIdStr.pop_back(); 363 } 364 365 asyncResp->res.jsonValue["@odata.type"] = 366 "#LogEntryCollection.LogEntryCollection"; 367 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr); 368 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries"; 369 asyncResp->res.jsonValue["Description"] = 370 "Collection of " + dumpType + " Dump Entries"; 371 372 nlohmann::json::array_t entriesArray; 373 std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; 374 375 dbus::utility::ManagedObjectType resp(objects); 376 std::ranges::sort(resp, [](const auto& l, const auto& r) { 377 return AlphanumLess<std::string>()(l.first.filename(), 378 r.first.filename()); 379 }); 380 381 for (auto& object : resp) 382 { 383 if (object.first.str.find(dumpEntryPath) == std::string::npos) 384 { 385 continue; 386 } 387 uint64_t timestampUs = 0; 388 uint64_t size = 0; 389 std::string dumpStatus; 390 std::string originatorId; 391 log_entry::OriginatorTypes originatorType = 392 log_entry::OriginatorTypes::Internal; 393 nlohmann::json::object_t thisEntry; 394 395 std::string entryID = object.first.filename(); 396 if (entryID.empty()) 397 { 398 continue; 399 } 400 401 parseDumpEntryFromDbusObject(object, dumpStatus, size, 402 timestampUs, originatorId, 403 originatorType, asyncResp); 404 405 if (dumpStatus != 406 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && 407 !dumpStatus.empty()) 408 { 409 // Dump status is not Complete, no need to enumerate 410 continue; 411 } 412 413 thisEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; 414 thisEntry["@odata.id"] = entriesPath + entryID; 415 thisEntry["Id"] = entryID; 416 thisEntry["EntryType"] = "Event"; 417 thisEntry["Name"] = dumpType + " Dump Entry"; 418 thisEntry["Created"] = 419 redfish::time_utils::getDateTimeUintUs(timestampUs); 420 421 if (!originatorId.empty()) 422 { 423 thisEntry["Originator"] = originatorId; 424 thisEntry["OriginatorType"] = originatorType; 425 } 426 427 if (dumpType == "BMC") 428 { 429 thisEntry["DiagnosticDataType"] = "Manager"; 430 thisEntry["AdditionalDataURI"] = 431 entriesPath + entryID + "/attachment"; 432 thisEntry["AdditionalDataSizeBytes"] = size; 433 } 434 else if (dumpType == "System") 435 { 436 thisEntry["DiagnosticDataType"] = "OEM"; 437 thisEntry["OEMDiagnosticDataType"] = "System"; 438 thisEntry["AdditionalDataURI"] = 439 entriesPath + entryID + "/attachment"; 440 thisEntry["AdditionalDataSizeBytes"] = size; 441 } 442 entriesArray.emplace_back(std::move(thisEntry)); 443 } 444 asyncResp->res.jsonValue["Members@odata.count"] = 445 entriesArray.size(); 446 asyncResp->res.jsonValue["Members"] = std::move(entriesArray); 447 }); 448 } 449 450 inline void 451 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 452 const std::string& entryID, const std::string& dumpType) 453 { 454 std::string entriesPath = getDumpEntriesPath(dumpType); 455 if (entriesPath.empty()) 456 { 457 messages::internalError(asyncResp->res); 458 return; 459 } 460 461 sdbusplus::message::object_path path("/xyz/openbmc_project/dump"); 462 dbus::utility::getManagedObjects( 463 "xyz.openbmc_project.Dump.Manager", path, 464 [asyncResp, entryID, dumpType, 465 entriesPath](const boost::system::error_code& ec, 466 const dbus::utility::ManagedObjectType& resp) { 467 if (ec) 468 { 469 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); 470 messages::internalError(asyncResp->res); 471 return; 472 } 473 474 bool foundDumpEntry = false; 475 std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; 476 477 for (const auto& objectPath : resp) 478 { 479 if (objectPath.first.str != dumpEntryPath + entryID) 480 { 481 continue; 482 } 483 484 foundDumpEntry = true; 485 uint64_t timestampUs = 0; 486 uint64_t size = 0; 487 std::string dumpStatus; 488 std::string originatorId; 489 log_entry::OriginatorTypes originatorType = 490 log_entry::OriginatorTypes::Internal; 491 492 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size, 493 timestampUs, originatorId, 494 originatorType, asyncResp); 495 496 if (dumpStatus != 497 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && 498 !dumpStatus.empty()) 499 { 500 // Dump status is not Complete 501 // return not found until status is changed to Completed 502 messages::resourceNotFound(asyncResp->res, 503 dumpType + " dump", entryID); 504 return; 505 } 506 507 asyncResp->res.jsonValue["@odata.type"] = 508 "#LogEntry.v1_11_0.LogEntry"; 509 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID; 510 asyncResp->res.jsonValue["Id"] = entryID; 511 asyncResp->res.jsonValue["EntryType"] = "Event"; 512 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry"; 513 asyncResp->res.jsonValue["Created"] = 514 redfish::time_utils::getDateTimeUintUs(timestampUs); 515 516 if (!originatorId.empty()) 517 { 518 asyncResp->res.jsonValue["Originator"] = originatorId; 519 asyncResp->res.jsonValue["OriginatorType"] = originatorType; 520 } 521 522 if (dumpType == "BMC") 523 { 524 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager"; 525 asyncResp->res.jsonValue["AdditionalDataURI"] = 526 entriesPath + entryID + "/attachment"; 527 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; 528 } 529 else if (dumpType == "System") 530 { 531 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM"; 532 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = 533 "System"; 534 asyncResp->res.jsonValue["AdditionalDataURI"] = 535 entriesPath + entryID + "/attachment"; 536 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; 537 } 538 } 539 if (!foundDumpEntry) 540 { 541 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); 542 messages::resourceNotFound(asyncResp->res, dumpType + " dump", 543 entryID); 544 return; 545 } 546 }); 547 } 548 549 inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 550 const std::string& entryID, 551 const std::string& dumpType) 552 { 553 auto respHandler = [asyncResp, 554 entryID](const boost::system::error_code& ec) { 555 BMCWEB_LOG_DEBUG("Dump Entry doDelete callback: Done"); 556 if (ec) 557 { 558 if (ec.value() == EBADR) 559 { 560 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 561 return; 562 } 563 BMCWEB_LOG_ERROR( 564 "Dump (DBus) doDelete respHandler got error {} entryID={}", ec, 565 entryID); 566 messages::internalError(asyncResp->res); 567 return; 568 } 569 }; 570 571 crow::connections::systemBus->async_method_call( 572 respHandler, "xyz.openbmc_project.Dump.Manager", 573 std::format("{}/entry/{}", getDumpPath(dumpType), entryID), 574 "xyz.openbmc_project.Object.Delete", "Delete"); 575 } 576 inline bool checkSizeLimit(int fd, crow::Response& res) 577 { 578 long long int size = lseek(fd, 0, SEEK_END); 579 if (size <= 0) 580 { 581 BMCWEB_LOG_ERROR("Failed to get size of file, lseek() returned {}", 582 size); 583 messages::internalError(res); 584 return false; 585 } 586 587 // Arbitrary max size of 20MB to accommodate BMC dumps 588 constexpr long long int maxFileSize = 20LL * 1024LL * 1024LL; 589 if (size > maxFileSize) 590 { 591 BMCWEB_LOG_ERROR("File size {} exceeds maximum allowed size of {}", 592 size, maxFileSize); 593 messages::internalError(res); 594 return false; 595 } 596 off_t rc = lseek(fd, 0, SEEK_SET); 597 if (rc < 0) 598 { 599 BMCWEB_LOG_ERROR("Failed to reset file offset to 0"); 600 messages::internalError(res); 601 return false; 602 } 603 return true; 604 } 605 inline void downloadEntryCallback( 606 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 607 const std::string& entryID, const std::string& downloadEntryType, 608 const boost::system::error_code& ec, 609 const sdbusplus::message::unix_fd& unixfd) 610 { 611 if (ec.value() == EBADR) 612 { 613 messages::resourceNotFound(asyncResp->res, "EntryAttachment", entryID); 614 return; 615 } 616 if (ec) 617 { 618 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 619 messages::internalError(asyncResp->res); 620 return; 621 } 622 623 // Make sure we know how to process the retrieved entry attachment 624 if ((downloadEntryType != "BMC") && (downloadEntryType != "System")) 625 { 626 BMCWEB_LOG_ERROR("downloadEntryCallback() invalid entry type: {}", 627 downloadEntryType); 628 messages::internalError(asyncResp->res); 629 } 630 631 int fd = -1; 632 fd = dup(unixfd); 633 if (fd < 0) 634 { 635 BMCWEB_LOG_ERROR("Failed to open file"); 636 messages::internalError(asyncResp->res); 637 return; 638 } 639 if (!checkSizeLimit(fd, asyncResp->res)) 640 { 641 close(fd); 642 return; 643 } 644 if (downloadEntryType == "System") 645 { 646 if (!asyncResp->res.openFd(fd, bmcweb::EncodingType::Base64)) 647 { 648 messages::internalError(asyncResp->res); 649 close(fd); 650 return; 651 } 652 asyncResp->res.addHeader( 653 boost::beast::http::field::content_transfer_encoding, "Base64"); 654 return; 655 } 656 if (!asyncResp->res.openFd(fd)) 657 { 658 messages::internalError(asyncResp->res); 659 close(fd); 660 return; 661 } 662 asyncResp->res.addHeader(boost::beast::http::field::content_type, 663 "application/octet-stream"); 664 } 665 666 inline void 667 downloadDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 668 const std::string& entryID, const std::string& dumpType) 669 { 670 if (dumpType != "BMC") 671 { 672 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); 673 messages::resourceNotFound(asyncResp->res, dumpType + " dump", entryID); 674 return; 675 } 676 677 std::string dumpEntryPath = 678 std::format("{}/entry/{}", getDumpPath(dumpType), entryID); 679 680 auto downloadDumpEntryHandler = 681 [asyncResp, entryID, 682 dumpType](const boost::system::error_code& ec, 683 const sdbusplus::message::unix_fd& unixfd) { 684 downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); 685 }; 686 687 crow::connections::systemBus->async_method_call( 688 std::move(downloadDumpEntryHandler), "xyz.openbmc_project.Dump.Manager", 689 dumpEntryPath, "xyz.openbmc_project.Dump.Entry", "GetFileHandle"); 690 } 691 692 inline void downloadEventLogEntry( 693 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 694 const std::string& systemName, const std::string& entryID, 695 const std::string& dumpType) 696 { 697 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 698 { 699 // Option currently returns no systems. TBD 700 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 701 systemName); 702 return; 703 } 704 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 705 { 706 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 707 systemName); 708 return; 709 } 710 711 std::string entryPath = 712 sdbusplus::message::object_path("/xyz/openbmc_project/logging/entry") / 713 entryID; 714 715 auto downloadEventLogEntryHandler = 716 [asyncResp, entryID, 717 dumpType](const boost::system::error_code& ec, 718 const sdbusplus::message::unix_fd& unixfd) { 719 downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); 720 }; 721 722 crow::connections::systemBus->async_method_call( 723 std::move(downloadEventLogEntryHandler), "xyz.openbmc_project.Logging", 724 entryPath, "xyz.openbmc_project.Logging.Entry", "GetEntry"); 725 } 726 727 inline DumpCreationProgress 728 mapDbusStatusToDumpProgress(const std::string& status) 729 { 730 if (status == 731 "xyz.openbmc_project.Common.Progress.OperationStatus.Failed" || 732 status == "xyz.openbmc_project.Common.Progress.OperationStatus.Aborted") 733 { 734 return DumpCreationProgress::DUMP_CREATE_FAILED; 735 } 736 if (status == 737 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed") 738 { 739 return DumpCreationProgress::DUMP_CREATE_SUCCESS; 740 } 741 return DumpCreationProgress::DUMP_CREATE_INPROGRESS; 742 } 743 744 inline DumpCreationProgress 745 getDumpCompletionStatus(const dbus::utility::DBusPropertiesMap& values) 746 { 747 for (const auto& [key, val] : values) 748 { 749 if (key == "Status") 750 { 751 const std::string* value = std::get_if<std::string>(&val); 752 if (value == nullptr) 753 { 754 BMCWEB_LOG_ERROR("Status property value is null"); 755 return DumpCreationProgress::DUMP_CREATE_FAILED; 756 } 757 return mapDbusStatusToDumpProgress(*value); 758 } 759 } 760 return DumpCreationProgress::DUMP_CREATE_INPROGRESS; 761 } 762 763 inline std::string getDumpEntryPath(const std::string& dumpPath) 764 { 765 if (dumpPath == "/xyz/openbmc_project/dump/bmc/entry") 766 { 767 return std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/", 768 BMCWEB_REDFISH_MANAGER_URI_NAME); 769 } 770 if (dumpPath == "/xyz/openbmc_project/dump/system/entry") 771 { 772 return std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/", 773 BMCWEB_REDFISH_SYSTEM_URI_NAME); 774 } 775 return ""; 776 } 777 778 inline void createDumpTaskCallback( 779 task::Payload&& payload, 780 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 781 const sdbusplus::message::object_path& createdObjPath) 782 { 783 const std::string dumpPath = createdObjPath.parent_path().str; 784 const std::string dumpId = createdObjPath.filename(); 785 786 std::string dumpEntryPath = getDumpEntryPath(dumpPath); 787 788 if (dumpEntryPath.empty()) 789 { 790 BMCWEB_LOG_ERROR("Invalid dump type received"); 791 messages::internalError(asyncResp->res); 792 return; 793 } 794 795 crow::connections::systemBus->async_method_call( 796 [asyncResp, payload = std::move(payload), createdObjPath, 797 dumpEntryPath{std::move(dumpEntryPath)}, 798 dumpId](const boost::system::error_code& ec, 799 const std::string& introspectXml) { 800 if (ec) 801 { 802 BMCWEB_LOG_ERROR("Introspect call failed with error: {}", 803 ec.message()); 804 messages::internalError(asyncResp->res); 805 return; 806 } 807 808 // Check if the created dump object has implemented Progress 809 // interface to track dump completion. If yes, fetch the "Status" 810 // property of the interface, modify the task state accordingly. 811 // Else, return task completed. 812 tinyxml2::XMLDocument doc; 813 814 doc.Parse(introspectXml.data(), introspectXml.size()); 815 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 816 if (pRoot == nullptr) 817 { 818 BMCWEB_LOG_ERROR("XML document failed to parse"); 819 messages::internalError(asyncResp->res); 820 return; 821 } 822 tinyxml2::XMLElement* interfaceNode = 823 pRoot->FirstChildElement("interface"); 824 825 bool isProgressIntfPresent = false; 826 while (interfaceNode != nullptr) 827 { 828 const char* thisInterfaceName = 829 interfaceNode->Attribute("name"); 830 if (thisInterfaceName != nullptr) 831 { 832 if (thisInterfaceName == 833 std::string_view("xyz.openbmc_project.Common.Progress")) 834 { 835 interfaceNode = 836 interfaceNode->NextSiblingElement("interface"); 837 continue; 838 } 839 isProgressIntfPresent = true; 840 break; 841 } 842 interfaceNode = interfaceNode->NextSiblingElement("interface"); 843 } 844 845 std::shared_ptr<task::TaskData> task = task::TaskData::createTask( 846 [createdObjPath, dumpEntryPath, dumpId, isProgressIntfPresent]( 847 const boost::system::error_code& ec2, 848 sdbusplus::message_t& msg, 849 const std::shared_ptr<task::TaskData>& taskData) { 850 if (ec2) 851 { 852 BMCWEB_LOG_ERROR("{}: Error in creating dump", 853 createdObjPath.str); 854 taskData->messages.emplace_back( 855 messages::internalError()); 856 taskData->state = "Cancelled"; 857 return task::completed; 858 } 859 860 if (isProgressIntfPresent) 861 { 862 dbus::utility::DBusPropertiesMap values; 863 std::string prop; 864 msg.read(prop, values); 865 866 DumpCreationProgress dumpStatus = 867 getDumpCompletionStatus(values); 868 if (dumpStatus == 869 DumpCreationProgress::DUMP_CREATE_FAILED) 870 { 871 BMCWEB_LOG_ERROR("{}: Error in creating dump", 872 createdObjPath.str); 873 taskData->state = "Cancelled"; 874 return task::completed; 875 } 876 877 if (dumpStatus == 878 DumpCreationProgress::DUMP_CREATE_INPROGRESS) 879 { 880 BMCWEB_LOG_DEBUG( 881 "{}: Dump creation task is in progress", 882 createdObjPath.str); 883 return !task::completed; 884 } 885 } 886 887 nlohmann::json retMessage = messages::success(); 888 taskData->messages.emplace_back(retMessage); 889 890 boost::urls::url url = boost::urls::format( 891 "/redfish/v1/Managers/{}/LogServices/Dump/Entries/{}", 892 BMCWEB_REDFISH_MANAGER_URI_NAME, dumpId); 893 894 std::string headerLoc = "Location: "; 895 headerLoc += url.buffer(); 896 897 taskData->payload->httpHeaders.emplace_back( 898 std::move(headerLoc)); 899 900 BMCWEB_LOG_DEBUG("{}: Dump creation task completed", 901 createdObjPath.str); 902 taskData->state = "Completed"; 903 return task::completed; 904 }, 905 "type='signal',interface='org.freedesktop.DBus.Properties'," 906 "member='PropertiesChanged',path='" + 907 createdObjPath.str + "'"); 908 909 // The task timer is set to max time limit within which the 910 // requested dump will be collected. 911 task->startTimer(std::chrono::minutes(6)); 912 task->populateResp(asyncResp->res); 913 task->payload.emplace(payload); 914 }, 915 "xyz.openbmc_project.Dump.Manager", createdObjPath, 916 "org.freedesktop.DBus.Introspectable", "Introspect"); 917 } 918 919 inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 920 const crow::Request& req, const std::string& dumpType) 921 { 922 std::string dumpPath = getDumpEntriesPath(dumpType); 923 if (dumpPath.empty()) 924 { 925 messages::internalError(asyncResp->res); 926 return; 927 } 928 929 std::optional<std::string> diagnosticDataType; 930 std::optional<std::string> oemDiagnosticDataType; 931 932 if (!redfish::json_util::readJsonAction( // 933 req, asyncResp->res, // 934 "DiagnosticDataType", diagnosticDataType, // 935 "OEMDiagnosticDataType", oemDiagnosticDataType // 936 )) 937 { 938 return; 939 } 940 941 if (dumpType == "System") 942 { 943 if (!oemDiagnosticDataType || !diagnosticDataType) 944 { 945 BMCWEB_LOG_ERROR( 946 "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!"); 947 messages::actionParameterMissing( 948 asyncResp->res, "CollectDiagnosticData", 949 "DiagnosticDataType & OEMDiagnosticDataType"); 950 return; 951 } 952 if ((*oemDiagnosticDataType != "System") || 953 (*diagnosticDataType != "OEM")) 954 { 955 BMCWEB_LOG_ERROR("Wrong parameter values passed"); 956 messages::internalError(asyncResp->res); 957 return; 958 } 959 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump/", 960 BMCWEB_REDFISH_SYSTEM_URI_NAME); 961 } 962 else if (dumpType == "BMC") 963 { 964 if (!diagnosticDataType) 965 { 966 BMCWEB_LOG_ERROR( 967 "CreateDump action parameter 'DiagnosticDataType' not found!"); 968 messages::actionParameterMissing( 969 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType"); 970 return; 971 } 972 if (*diagnosticDataType != "Manager") 973 { 974 BMCWEB_LOG_ERROR( 975 "Wrong parameter value passed for 'DiagnosticDataType'"); 976 messages::internalError(asyncResp->res); 977 return; 978 } 979 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump/", 980 BMCWEB_REDFISH_MANAGER_URI_NAME); 981 } 982 else 983 { 984 BMCWEB_LOG_ERROR("CreateDump failed. Unknown dump type"); 985 messages::internalError(asyncResp->res); 986 return; 987 } 988 989 std::vector<std::pair<std::string, std::variant<std::string, uint64_t>>> 990 createDumpParamVec; 991 992 if (req.session != nullptr) 993 { 994 createDumpParamVec.emplace_back( 995 "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorId", 996 req.session->clientIp); 997 createDumpParamVec.emplace_back( 998 "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorType", 999 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client"); 1000 } 1001 1002 crow::connections::systemBus->async_method_call( 1003 [asyncResp, payload(task::Payload(req)), 1004 dumpPath](const boost::system::error_code& ec, 1005 const sdbusplus::message_t& msg, 1006 const sdbusplus::message::object_path& objPath) mutable { 1007 if (ec) 1008 { 1009 BMCWEB_LOG_ERROR("CreateDump resp_handler got error {}", ec); 1010 const sd_bus_error* dbusError = msg.get_error(); 1011 if (dbusError == nullptr) 1012 { 1013 messages::internalError(asyncResp->res); 1014 return; 1015 } 1016 1017 BMCWEB_LOG_ERROR("CreateDump DBus error: {} and error msg: {}", 1018 dbusError->name, dbusError->message); 1019 if (std::string_view( 1020 "xyz.openbmc_project.Common.Error.NotAllowed") == 1021 dbusError->name) 1022 { 1023 messages::resourceInStandby(asyncResp->res); 1024 return; 1025 } 1026 if (std::string_view( 1027 "xyz.openbmc_project.Dump.Create.Error.Disabled") == 1028 dbusError->name) 1029 { 1030 messages::serviceDisabled(asyncResp->res, dumpPath); 1031 return; 1032 } 1033 if (std::string_view( 1034 "xyz.openbmc_project.Common.Error.Unavailable") == 1035 dbusError->name) 1036 { 1037 messages::resourceInUse(asyncResp->res); 1038 return; 1039 } 1040 // Other Dbus errors such as: 1041 // xyz.openbmc_project.Common.Error.InvalidArgument & 1042 // org.freedesktop.DBus.Error.InvalidArgs are all related to 1043 // the dbus call that is made here in the bmcweb 1044 // implementation and has nothing to do with the client's 1045 // input in the request. Hence, returning internal error 1046 // back to the client. 1047 messages::internalError(asyncResp->res); 1048 return; 1049 } 1050 BMCWEB_LOG_DEBUG("Dump Created. Path: {}", objPath.str); 1051 createDumpTaskCallback(std::move(payload), asyncResp, objPath); 1052 }, 1053 "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType), 1054 "xyz.openbmc_project.Dump.Create", "CreateDump", createDumpParamVec); 1055 } 1056 1057 inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1058 const std::string& dumpType) 1059 { 1060 crow::connections::systemBus->async_method_call( 1061 [asyncResp](const boost::system::error_code& ec) { 1062 if (ec) 1063 { 1064 BMCWEB_LOG_ERROR("clearDump resp_handler got error {}", ec); 1065 messages::internalError(asyncResp->res); 1066 return; 1067 } 1068 }, 1069 "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType), 1070 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 1071 } 1072 1073 inline void parseCrashdumpParameters( 1074 const dbus::utility::DBusPropertiesMap& params, std::string& filename, 1075 std::string& timestamp, std::string& logfile) 1076 { 1077 const std::string* filenamePtr = nullptr; 1078 const std::string* timestampPtr = nullptr; 1079 const std::string* logfilePtr = nullptr; 1080 1081 const bool success = sdbusplus::unpackPropertiesNoThrow( 1082 dbus_utils::UnpackErrorPrinter(), params, "Timestamp", timestampPtr, 1083 "Filename", filenamePtr, "Log", logfilePtr); 1084 1085 if (!success) 1086 { 1087 return; 1088 } 1089 1090 if (filenamePtr != nullptr) 1091 { 1092 filename = *filenamePtr; 1093 } 1094 1095 if (timestampPtr != nullptr) 1096 { 1097 timestamp = *timestampPtr; 1098 } 1099 1100 if (logfilePtr != nullptr) 1101 { 1102 logfile = *logfilePtr; 1103 } 1104 } 1105 1106 inline void requestRoutesSystemLogServiceCollection(App& app) 1107 { 1108 /** 1109 * Functions triggers appropriate requests on DBus 1110 */ 1111 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/") 1112 .privileges(redfish::privileges::getLogServiceCollection) 1113 .methods( 1114 boost::beast::http::verb:: 1115 get)([&app](const crow::Request& req, 1116 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1117 const std::string& systemName) { 1118 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1119 { 1120 return; 1121 } 1122 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1123 { 1124 // Option currently returns no systems. TBD 1125 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1126 systemName); 1127 return; 1128 } 1129 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1130 { 1131 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1132 systemName); 1133 return; 1134 } 1135 1136 // Collections don't include the static data added by SubRoute 1137 // because it has a duplicate entry for members 1138 asyncResp->res.jsonValue["@odata.type"] = 1139 "#LogServiceCollection.LogServiceCollection"; 1140 asyncResp->res.jsonValue["@odata.id"] = 1141 std::format("/redfish/v1/Systems/{}/LogServices", 1142 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1143 asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 1144 asyncResp->res.jsonValue["Description"] = 1145 "Collection of LogServices for this Computer System"; 1146 nlohmann::json& logServiceArray = 1147 asyncResp->res.jsonValue["Members"]; 1148 logServiceArray = nlohmann::json::array(); 1149 nlohmann::json::object_t eventLog; 1150 eventLog["@odata.id"] = 1151 std::format("/redfish/v1/Systems/{}/LogServices/EventLog", 1152 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1153 logServiceArray.emplace_back(std::move(eventLog)); 1154 if constexpr (BMCWEB_REDFISH_DUMP_LOG) 1155 { 1156 nlohmann::json::object_t dumpLog; 1157 dumpLog["@odata.id"] = 1158 std::format("/redfish/v1/Systems/{}/LogServices/Dump", 1159 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1160 logServiceArray.emplace_back(std::move(dumpLog)); 1161 } 1162 1163 if constexpr (BMCWEB_REDFISH_CPU_LOG) 1164 { 1165 nlohmann::json::object_t crashdump; 1166 crashdump["@odata.id"] = 1167 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", 1168 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1169 logServiceArray.emplace_back(std::move(crashdump)); 1170 } 1171 1172 if constexpr (BMCWEB_REDFISH_HOST_LOGGER) 1173 { 1174 nlohmann::json::object_t hostlogger; 1175 hostlogger["@odata.id"] = 1176 std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", 1177 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1178 logServiceArray.emplace_back(std::move(hostlogger)); 1179 } 1180 asyncResp->res.jsonValue["Members@odata.count"] = 1181 logServiceArray.size(); 1182 1183 constexpr std::array<std::string_view, 1> interfaces = { 1184 "xyz.openbmc_project.State.Boot.PostCode"}; 1185 dbus::utility::getSubTreePaths( 1186 "/", 0, interfaces, 1187 [asyncResp](const boost::system::error_code& ec, 1188 const dbus::utility::MapperGetSubTreePathsResponse& 1189 subtreePath) { 1190 if (ec) 1191 { 1192 BMCWEB_LOG_ERROR("{}", ec); 1193 return; 1194 } 1195 1196 for (const auto& pathStr : subtreePath) 1197 { 1198 if (pathStr.find("PostCode") != std::string::npos) 1199 { 1200 nlohmann::json& logServiceArrayLocal = 1201 asyncResp->res.jsonValue["Members"]; 1202 nlohmann::json::object_t member; 1203 member["@odata.id"] = std::format( 1204 "/redfish/v1/Systems/{}/LogServices/PostCodes", 1205 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1206 1207 logServiceArrayLocal.emplace_back( 1208 std::move(member)); 1209 1210 asyncResp->res.jsonValue["Members@odata.count"] = 1211 logServiceArrayLocal.size(); 1212 return; 1213 } 1214 } 1215 }); 1216 }); 1217 } 1218 1219 inline void requestRoutesEventLogService(App& app) 1220 { 1221 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/") 1222 .privileges(redfish::privileges::getLogService) 1223 .methods( 1224 boost::beast::http::verb:: 1225 get)([&app](const crow::Request& req, 1226 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1227 const std::string& systemName) { 1228 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1229 { 1230 return; 1231 } 1232 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1233 { 1234 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1235 systemName); 1236 return; 1237 } 1238 asyncResp->res.jsonValue["@odata.id"] = 1239 std::format("/redfish/v1/Systems/{}/LogServices/EventLog", 1240 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1241 asyncResp->res.jsonValue["@odata.type"] = 1242 "#LogService.v1_2_0.LogService"; 1243 asyncResp->res.jsonValue["Name"] = "Event Log Service"; 1244 asyncResp->res.jsonValue["Description"] = 1245 "System Event Log Service"; 1246 asyncResp->res.jsonValue["Id"] = "EventLog"; 1247 asyncResp->res.jsonValue["OverWritePolicy"] = 1248 log_service::OverWritePolicy::WrapsWhenFull; 1249 1250 std::pair<std::string, std::string> redfishDateTimeOffset = 1251 redfish::time_utils::getDateTimeOffsetNow(); 1252 1253 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 1254 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 1255 redfishDateTimeOffset.second; 1256 1257 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( 1258 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries", 1259 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1260 asyncResp->res 1261 .jsonValue["Actions"]["#LogService.ClearLog"]["target"] 1262 1263 = std::format( 1264 "/redfish/v1/Systems/{}/LogServices/EventLog/Actions/LogService.ClearLog", 1265 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1266 }); 1267 } 1268 1269 inline void handleSystemsLogServicesEventLogActionsClearPost( 1270 App& app, const crow::Request& req, 1271 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1272 const std::string& systemName) 1273 { 1274 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1275 { 1276 return; 1277 } 1278 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1279 { 1280 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1281 systemName); 1282 return; 1283 } 1284 1285 // Clear the EventLog by deleting the log files 1286 std::vector<std::filesystem::path> redfishLogFiles; 1287 if (getRedfishLogFiles(redfishLogFiles)) 1288 { 1289 for (const std::filesystem::path& file : redfishLogFiles) 1290 { 1291 std::error_code ec; 1292 std::filesystem::remove(file, ec); 1293 } 1294 } 1295 1296 // Reload rsyslog so it knows to start new log files 1297 crow::connections::systemBus->async_method_call( 1298 [asyncResp](const boost::system::error_code& ec) { 1299 if (ec) 1300 { 1301 BMCWEB_LOG_ERROR("Failed to reload rsyslog: {}", ec); 1302 messages::internalError(asyncResp->res); 1303 return; 1304 } 1305 1306 messages::success(asyncResp->res); 1307 }, 1308 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 1309 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service", 1310 "replace"); 1311 } 1312 1313 inline void requestRoutesJournalEventLogClear(App& app) 1314 { 1315 BMCWEB_ROUTE( 1316 app, 1317 "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/") 1318 .privileges({{"ConfigureComponents"}}) 1319 .methods(boost::beast::http::verb::post)(std::bind_front( 1320 handleSystemsLogServicesEventLogActionsClearPost, std::ref(app))); 1321 } 1322 1323 enum class LogParseError 1324 { 1325 success, 1326 parseFailed, 1327 messageIdNotInRegistry, 1328 }; 1329 1330 static LogParseError fillEventLogEntryJson( 1331 const std::string& logEntryID, const std::string& logEntry, 1332 nlohmann::json::object_t& logEntryJson) 1333 { 1334 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 1335 // First get the Timestamp 1336 size_t space = logEntry.find_first_of(' '); 1337 if (space == std::string::npos) 1338 { 1339 return LogParseError::parseFailed; 1340 } 1341 std::string timestamp = logEntry.substr(0, space); 1342 // Then get the log contents 1343 size_t entryStart = logEntry.find_first_not_of(' ', space); 1344 if (entryStart == std::string::npos) 1345 { 1346 return LogParseError::parseFailed; 1347 } 1348 std::string_view entry(logEntry); 1349 entry.remove_prefix(entryStart); 1350 // Use split to separate the entry into its fields 1351 std::vector<std::string> logEntryFields; 1352 bmcweb::split(logEntryFields, entry, ','); 1353 // We need at least a MessageId to be valid 1354 auto logEntryIter = logEntryFields.begin(); 1355 if (logEntryIter == logEntryFields.end()) 1356 { 1357 return LogParseError::parseFailed; 1358 } 1359 std::string& messageID = *logEntryIter; 1360 // Get the Message from the MessageRegistry 1361 const registries::Message* message = registries::getMessage(messageID); 1362 1363 logEntryIter++; 1364 if (message == nullptr) 1365 { 1366 BMCWEB_LOG_WARNING("Log entry not found in registry: {}", logEntry); 1367 return LogParseError::messageIdNotInRegistry; 1368 } 1369 1370 std::vector<std::string_view> messageArgs(logEntryIter, 1371 logEntryFields.end()); 1372 messageArgs.resize(message->numberOfArgs); 1373 1374 std::string msg = 1375 redfish::registries::fillMessageArgs(messageArgs, message->message); 1376 if (msg.empty()) 1377 { 1378 return LogParseError::parseFailed; 1379 } 1380 1381 // Get the Created time from the timestamp. The log timestamp is in RFC3339 1382 // format which matches the Redfish format except for the fractional seconds 1383 // between the '.' and the '+', so just remove them. 1384 std::size_t dot = timestamp.find_first_of('.'); 1385 std::size_t plus = timestamp.find_first_of('+'); 1386 if (dot != std::string::npos && plus != std::string::npos) 1387 { 1388 timestamp.erase(dot, plus - dot); 1389 } 1390 1391 // Fill in the log entry with the gathered data 1392 logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 1393 logEntryJson["@odata.id"] = boost::urls::format( 1394 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}", 1395 BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID); 1396 logEntryJson["Name"] = "System Event Log Entry"; 1397 logEntryJson["Id"] = logEntryID; 1398 logEntryJson["Message"] = std::move(msg); 1399 logEntryJson["MessageId"] = std::move(messageID); 1400 logEntryJson["MessageArgs"] = messageArgs; 1401 logEntryJson["EntryType"] = "Event"; 1402 logEntryJson["Severity"] = message->messageSeverity; 1403 logEntryJson["Created"] = std::move(timestamp); 1404 return LogParseError::success; 1405 } 1406 1407 inline void fillEventLogLogEntryFromPropertyMap( 1408 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1409 const dbus::utility::DBusPropertiesMap& resp, 1410 nlohmann::json& objectToFillOut) 1411 { 1412 std::optional<DbusEventLogEntry> optEntry = 1413 fillDbusEventLogEntryFromPropertyMap(resp); 1414 1415 if (!optEntry.has_value()) 1416 { 1417 messages::internalError(asyncResp->res); 1418 return; 1419 } 1420 DbusEventLogEntry entry = optEntry.value(); 1421 1422 objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 1423 objectToFillOut["@odata.id"] = boost::urls::format( 1424 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}", 1425 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id)); 1426 objectToFillOut["Name"] = "System Event Log Entry"; 1427 objectToFillOut["Id"] = std::to_string(entry.Id); 1428 objectToFillOut["Message"] = entry.Message; 1429 objectToFillOut["Resolved"] = entry.Resolved; 1430 std::optional<bool> notifyAction = 1431 getProviderNotifyAction(entry.ServiceProviderNotify); 1432 if (notifyAction) 1433 { 1434 objectToFillOut["ServiceProviderNotified"] = *notifyAction; 1435 } 1436 if ((entry.Resolution != nullptr) && !entry.Resolution->empty()) 1437 { 1438 objectToFillOut["Resolution"] = *entry.Resolution; 1439 } 1440 objectToFillOut["EntryType"] = "Event"; 1441 objectToFillOut["Severity"] = 1442 translateSeverityDbusToRedfish(entry.Severity); 1443 objectToFillOut["Created"] = 1444 redfish::time_utils::getDateTimeUintMs(entry.Timestamp); 1445 objectToFillOut["Modified"] = 1446 redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp); 1447 if (entry.Path != nullptr) 1448 { 1449 objectToFillOut["AdditionalDataURI"] = boost::urls::format( 1450 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}/attachment", 1451 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id)); 1452 } 1453 } 1454 1455 inline void afterLogEntriesGetManagedObjects( 1456 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1457 const boost::system::error_code& ec, 1458 const dbus::utility::ManagedObjectType& resp) 1459 { 1460 if (ec) 1461 { 1462 // TODO Handle for specific error code 1463 BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}", 1464 ec); 1465 messages::internalError(asyncResp->res); 1466 return; 1467 } 1468 nlohmann::json::array_t entriesArray; 1469 for (const auto& objectPath : resp) 1470 { 1471 dbus::utility::DBusPropertiesMap propsFlattened; 1472 auto isEntry = 1473 std::ranges::find_if(objectPath.second, [](const auto& object) { 1474 return object.first == "xyz.openbmc_project.Logging.Entry"; 1475 }); 1476 if (isEntry == objectPath.second.end()) 1477 { 1478 continue; 1479 } 1480 for (const auto& interfaceMap : objectPath.second) 1481 { 1482 for (const auto& propertyMap : interfaceMap.second) 1483 { 1484 propsFlattened.emplace_back(propertyMap.first, 1485 propertyMap.second); 1486 } 1487 } 1488 fillEventLogLogEntryFromPropertyMap(asyncResp, propsFlattened, 1489 entriesArray.emplace_back()); 1490 } 1491 1492 std::ranges::sort(entriesArray, [](const nlohmann::json& left, 1493 const nlohmann::json& right) { 1494 return (left["Id"] <= right["Id"]); 1495 }); 1496 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size(); 1497 asyncResp->res.jsonValue["Members"] = std::move(entriesArray); 1498 } 1499 1500 inline void handleSystemsLogServiceEventLogLogEntryCollection( 1501 App& app, const crow::Request& req, 1502 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1503 const std::string& systemName) 1504 { 1505 query_param::QueryCapabilities capabilities = { 1506 .canDelegateTop = true, 1507 .canDelegateSkip = true, 1508 }; 1509 query_param::Query delegatedQuery; 1510 if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, 1511 delegatedQuery, capabilities)) 1512 { 1513 return; 1514 } 1515 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1516 { 1517 // Option currently returns no systems. TBD 1518 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1519 systemName); 1520 return; 1521 } 1522 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1523 { 1524 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1525 systemName); 1526 return; 1527 } 1528 1529 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); 1530 size_t skip = delegatedQuery.skip.value_or(0); 1531 1532 // Collections don't include the static data added by SubRoute 1533 // because it has a duplicate entry for members 1534 asyncResp->res.jsonValue["@odata.type"] = 1535 "#LogEntryCollection.LogEntryCollection"; 1536 asyncResp->res.jsonValue["@odata.id"] = 1537 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", 1538 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1539 asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 1540 asyncResp->res.jsonValue["Description"] = 1541 "Collection of System Event Log Entries"; 1542 1543 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; 1544 logEntryArray = nlohmann::json::array(); 1545 // Go through the log files and create a unique ID for each 1546 // entry 1547 std::vector<std::filesystem::path> redfishLogFiles; 1548 getRedfishLogFiles(redfishLogFiles); 1549 uint64_t entryCount = 0; 1550 std::string logEntry; 1551 1552 // Oldest logs are in the last file, so start there and loop 1553 // backwards 1554 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) 1555 { 1556 std::ifstream logStream(*it); 1557 if (!logStream.is_open()) 1558 { 1559 continue; 1560 } 1561 1562 // Reset the unique ID on the first entry 1563 bool firstEntry = true; 1564 while (std::getline(logStream, logEntry)) 1565 { 1566 std::string idStr; 1567 if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 1568 { 1569 continue; 1570 } 1571 firstEntry = false; 1572 1573 nlohmann::json::object_t bmcLogEntry; 1574 LogParseError status = 1575 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); 1576 if (status == LogParseError::messageIdNotInRegistry) 1577 { 1578 continue; 1579 } 1580 if (status != LogParseError::success) 1581 { 1582 messages::internalError(asyncResp->res); 1583 return; 1584 } 1585 1586 entryCount++; 1587 // Handle paging using skip (number of entries to skip from the 1588 // start) and top (number of entries to display) 1589 if (entryCount <= skip || entryCount > skip + top) 1590 { 1591 continue; 1592 } 1593 1594 logEntryArray.emplace_back(std::move(bmcLogEntry)); 1595 } 1596 } 1597 asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1598 if (skip + top < entryCount) 1599 { 1600 asyncResp->res.jsonValue["Members@odata.nextLink"] = 1601 boost::urls::format( 1602 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries?$skip={}", 1603 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(skip + top)); 1604 } 1605 } 1606 1607 inline void requestRoutesJournalEventLogEntryCollection(App& app) 1608 { 1609 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/") 1610 .privileges(redfish::privileges::getLogEntryCollection) 1611 .methods(boost::beast::http::verb::get)(std::bind_front( 1612 handleSystemsLogServiceEventLogLogEntryCollection, std::ref(app))); 1613 } 1614 1615 inline void handleSystemsLogServiceEventLogEntriesGet( 1616 App& app, const crow::Request& req, 1617 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1618 const std::string& systemName, const std::string& param) 1619 { 1620 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1621 { 1622 return; 1623 } 1624 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1625 { 1626 // Option currently returns no systems. TBD 1627 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1628 systemName); 1629 return; 1630 } 1631 1632 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1633 { 1634 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1635 systemName); 1636 return; 1637 } 1638 1639 const std::string& targetID = param; 1640 1641 // Go through the log files and check the unique ID for each 1642 // entry to find the target entry 1643 std::vector<std::filesystem::path> redfishLogFiles; 1644 getRedfishLogFiles(redfishLogFiles); 1645 std::string logEntry; 1646 1647 // Oldest logs are in the last file, so start there and loop 1648 // backwards 1649 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) 1650 { 1651 std::ifstream logStream(*it); 1652 if (!logStream.is_open()) 1653 { 1654 continue; 1655 } 1656 1657 // Reset the unique ID on the first entry 1658 bool firstEntry = true; 1659 while (std::getline(logStream, logEntry)) 1660 { 1661 std::string idStr; 1662 if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 1663 { 1664 continue; 1665 } 1666 firstEntry = false; 1667 1668 if (idStr == targetID) 1669 { 1670 nlohmann::json::object_t bmcLogEntry; 1671 LogParseError status = 1672 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); 1673 if (status != LogParseError::success) 1674 { 1675 messages::internalError(asyncResp->res); 1676 return; 1677 } 1678 asyncResp->res.jsonValue.update(bmcLogEntry); 1679 return; 1680 } 1681 } 1682 } 1683 // Requested ID was not found 1684 messages::resourceNotFound(asyncResp->res, "LogEntry", targetID); 1685 } 1686 1687 inline void requestRoutesJournalEventLogEntry(App& app) 1688 { 1689 BMCWEB_ROUTE( 1690 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1691 .privileges(redfish::privileges::getLogEntry) 1692 .methods(boost::beast::http::verb::get)(std::bind_front( 1693 handleSystemsLogServiceEventLogEntriesGet, std::ref(app))); 1694 } 1695 1696 inline void dBusEventLogEntryCollection( 1697 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1698 { 1699 // Collections don't include the static data added by SubRoute 1700 // because it has a duplicate entry for members 1701 asyncResp->res.jsonValue["@odata.type"] = 1702 "#LogEntryCollection.LogEntryCollection"; 1703 asyncResp->res.jsonValue["@odata.id"] = 1704 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", 1705 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1706 asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 1707 asyncResp->res.jsonValue["Description"] = 1708 "Collection of System Event Log Entries"; 1709 1710 // DBus implementation of EventLog/Entries 1711 // Make call to Logging Service to find all log entry objects 1712 sdbusplus::message::object_path path("/xyz/openbmc_project/logging"); 1713 dbus::utility::getManagedObjects( 1714 "xyz.openbmc_project.Logging", path, 1715 [asyncResp](const boost::system::error_code& ec, 1716 const dbus::utility::ManagedObjectType& resp) { 1717 afterLogEntriesGetManagedObjects(asyncResp, ec, resp); 1718 }); 1719 } 1720 1721 inline void requestRoutesDBusEventLogEntryCollection(App& app) 1722 { 1723 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/") 1724 .privileges(redfish::privileges::getLogEntryCollection) 1725 .methods(boost::beast::http::verb::get)( 1726 [&app](const crow::Request& req, 1727 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1728 const std::string& systemName) { 1729 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1730 { 1731 return; 1732 } 1733 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1734 { 1735 // Option currently returns no systems. TBD 1736 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1737 systemName); 1738 return; 1739 } 1740 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1741 { 1742 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1743 systemName); 1744 return; 1745 } 1746 dBusEventLogEntryCollection(asyncResp); 1747 }); 1748 } 1749 1750 inline void dBusEventLogEntryGet( 1751 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID) 1752 { 1753 dbus::utility::escapePathForDbus(entryID); 1754 1755 // DBus implementation of EventLog/Entries 1756 // Make call to Logging Service to find all log entry objects 1757 dbus::utility::getAllProperties( 1758 "xyz.openbmc_project.Logging", 1759 "/xyz/openbmc_project/logging/entry/" + entryID, "", 1760 [asyncResp, entryID](const boost::system::error_code& ec, 1761 const dbus::utility::DBusPropertiesMap& resp) { 1762 if (ec.value() == EBADR) 1763 { 1764 messages::resourceNotFound(asyncResp->res, "EventLogEntry", 1765 entryID); 1766 return; 1767 } 1768 if (ec) 1769 { 1770 BMCWEB_LOG_ERROR( 1771 "EventLogEntry (DBus) resp_handler got error {}", ec); 1772 messages::internalError(asyncResp->res); 1773 return; 1774 } 1775 1776 fillEventLogLogEntryFromPropertyMap(asyncResp, resp, 1777 asyncResp->res.jsonValue); 1778 }); 1779 } 1780 1781 inline void 1782 dBusEventLogEntryPatch(const crow::Request& req, 1783 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1784 const std::string& entryId) 1785 { 1786 std::optional<bool> resolved; 1787 1788 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved)) 1789 { 1790 return; 1791 } 1792 BMCWEB_LOG_DEBUG("Set Resolved"); 1793 1794 setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging", 1795 "/xyz/openbmc_project/logging/entry/" + entryId, 1796 "xyz.openbmc_project.Logging.Entry", "Resolved", 1797 resolved.value_or(false)); 1798 } 1799 1800 inline void dBusEventLogEntryDelete( 1801 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID) 1802 { 1803 BMCWEB_LOG_DEBUG("Do delete single event entries."); 1804 1805 dbus::utility::escapePathForDbus(entryID); 1806 1807 // Process response from Logging service. 1808 auto respHandler = [asyncResp, 1809 entryID](const boost::system::error_code& ec) { 1810 BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done"); 1811 if (ec) 1812 { 1813 if (ec.value() == EBADR) 1814 { 1815 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 1816 return; 1817 } 1818 // TODO Handle for specific error code 1819 BMCWEB_LOG_ERROR( 1820 "EventLogEntry (DBus) doDelete respHandler got error {}", ec); 1821 asyncResp->res.result( 1822 boost::beast::http::status::internal_server_error); 1823 return; 1824 } 1825 1826 asyncResp->res.result(boost::beast::http::status::ok); 1827 }; 1828 1829 // Make call to Logging service to request Delete Log 1830 crow::connections::systemBus->async_method_call( 1831 respHandler, "xyz.openbmc_project.Logging", 1832 "/xyz/openbmc_project/logging/entry/" + entryID, 1833 "xyz.openbmc_project.Object.Delete", "Delete"); 1834 } 1835 1836 inline void requestRoutesDBusEventLogEntry(App& app) 1837 { 1838 BMCWEB_ROUTE( 1839 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1840 .privileges(redfish::privileges::getLogEntry) 1841 .methods(boost::beast::http::verb::get)( 1842 [&app](const crow::Request& req, 1843 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1844 const std::string& systemName, const std::string& entryId) { 1845 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1846 { 1847 return; 1848 } 1849 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1850 { 1851 // Option currently returns no systems. TBD 1852 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1853 systemName); 1854 return; 1855 } 1856 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1857 { 1858 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1859 systemName); 1860 return; 1861 } 1862 1863 dBusEventLogEntryGet(asyncResp, entryId); 1864 }); 1865 1866 BMCWEB_ROUTE( 1867 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1868 .privileges(redfish::privileges::patchLogEntry) 1869 .methods(boost::beast::http::verb::patch)( 1870 [&app](const crow::Request& req, 1871 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1872 const std::string& systemName, const std::string& entryId) { 1873 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1874 { 1875 return; 1876 } 1877 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1878 { 1879 // Option currently returns no systems. TBD 1880 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1881 systemName); 1882 return; 1883 } 1884 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1885 { 1886 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1887 systemName); 1888 return; 1889 } 1890 1891 dBusEventLogEntryPatch(req, asyncResp, entryId); 1892 }); 1893 1894 BMCWEB_ROUTE( 1895 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1896 .privileges(redfish::privileges::deleteLogEntry) 1897 1898 .methods(boost::beast::http::verb::delete_)( 1899 [&app](const crow::Request& req, 1900 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1901 const std::string& systemName, const std::string& param) { 1902 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1903 { 1904 return; 1905 } 1906 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1907 { 1908 // Option currently returns no systems. TBD 1909 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1910 systemName); 1911 return; 1912 } 1913 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1914 { 1915 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1916 systemName); 1917 return; 1918 } 1919 dBusEventLogEntryDelete(asyncResp, param); 1920 }); 1921 } 1922 1923 constexpr const char* hostLoggerFolderPath = "/var/log/console"; 1924 1925 inline bool 1926 getHostLoggerFiles(const std::string& hostLoggerFilePath, 1927 std::vector<std::filesystem::path>& hostLoggerFiles) 1928 { 1929 std::error_code ec; 1930 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec); 1931 if (ec) 1932 { 1933 BMCWEB_LOG_WARNING("{}", ec.message()); 1934 return false; 1935 } 1936 for (const std::filesystem::directory_entry& it : logPath) 1937 { 1938 std::string filename = it.path().filename(); 1939 // Prefix of each log files is "log". Find the file and save the 1940 // path 1941 if (filename.starts_with("log")) 1942 { 1943 hostLoggerFiles.emplace_back(it.path()); 1944 } 1945 } 1946 // As the log files rotate, they are appended with a ".#" that is higher for 1947 // the older logs. Since we start from oldest logs, sort the name in 1948 // descending order. 1949 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(), 1950 AlphanumLess<std::string>()); 1951 1952 return true; 1953 } 1954 1955 inline bool getHostLoggerEntries( 1956 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip, 1957 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount) 1958 { 1959 GzFileReader logFile; 1960 1961 // Go though all log files and expose host logs. 1962 for (const std::filesystem::path& it : hostLoggerFiles) 1963 { 1964 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount)) 1965 { 1966 BMCWEB_LOG_ERROR("fail to expose host logs"); 1967 return false; 1968 } 1969 } 1970 // Get lastMessage from constructor by getter 1971 std::string lastMessage = logFile.getLastMessage(); 1972 if (!lastMessage.empty()) 1973 { 1974 logCount++; 1975 if (logCount > skip && logCount <= (skip + top)) 1976 { 1977 logEntries.push_back(lastMessage); 1978 } 1979 } 1980 return true; 1981 } 1982 1983 inline void handleBMCLogServicesCollectionGet( 1984 crow::App& app, const crow::Request& req, 1985 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1986 const std::string& managerId) 1987 { 1988 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1989 { 1990 return; 1991 } 1992 1993 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1994 { 1995 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1996 return; 1997 } 1998 1999 // Collections don't include the static data added by SubRoute 2000 // because it has a duplicate entry for members 2001 asyncResp->res.jsonValue["@odata.type"] = 2002 "#LogServiceCollection.LogServiceCollection"; 2003 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 2004 "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME); 2005 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 2006 asyncResp->res.jsonValue["Description"] = 2007 "Collection of LogServices for this Manager"; 2008 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"]; 2009 logServiceArray = nlohmann::json::array(); 2010 2011 if constexpr (BMCWEB_REDFISH_BMC_JOURNAL) 2012 { 2013 nlohmann::json::object_t journal; 2014 journal["@odata.id"] = 2015 boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal", 2016 BMCWEB_REDFISH_MANAGER_URI_NAME); 2017 logServiceArray.emplace_back(std::move(journal)); 2018 } 2019 2020 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size(); 2021 2022 if constexpr (BMCWEB_REDFISH_DUMP_LOG) 2023 { 2024 constexpr std::array<std::string_view, 1> interfaces = { 2025 "xyz.openbmc_project.Collection.DeleteAll"}; 2026 dbus::utility::getSubTreePaths( 2027 "/xyz/openbmc_project/dump", 0, interfaces, 2028 [asyncResp](const boost::system::error_code& ec, 2029 const dbus::utility::MapperGetSubTreePathsResponse& 2030 subTreePaths) { 2031 if (ec) 2032 { 2033 BMCWEB_LOG_ERROR( 2034 "handleBMCLogServicesCollectionGet respHandler got error {}", 2035 ec); 2036 // Assume that getting an error simply means there are no 2037 // dump LogServices. Return without adding any error 2038 // response. 2039 return; 2040 } 2041 2042 nlohmann::json& logServiceArrayLocal = 2043 asyncResp->res.jsonValue["Members"]; 2044 2045 for (const std::string& path : subTreePaths) 2046 { 2047 if (path == "/xyz/openbmc_project/dump/bmc") 2048 { 2049 nlohmann::json::object_t member; 2050 member["@odata.id"] = boost::urls::format( 2051 "/redfish/v1/Managers/{}/LogServices/Dump", 2052 BMCWEB_REDFISH_MANAGER_URI_NAME); 2053 logServiceArrayLocal.emplace_back(std::move(member)); 2054 } 2055 else if (path == "/xyz/openbmc_project/dump/faultlog") 2056 { 2057 nlohmann::json::object_t member; 2058 member["@odata.id"] = boost::urls::format( 2059 "/redfish/v1/Managers/{}/LogServices/FaultLog", 2060 BMCWEB_REDFISH_MANAGER_URI_NAME); 2061 logServiceArrayLocal.emplace_back(std::move(member)); 2062 } 2063 } 2064 2065 asyncResp->res.jsonValue["Members@odata.count"] = 2066 logServiceArrayLocal.size(); 2067 }); 2068 } 2069 } 2070 2071 inline void requestRoutesBMCLogServiceCollection(App& app) 2072 { 2073 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/") 2074 .privileges(redfish::privileges::getLogServiceCollection) 2075 .methods(boost::beast::http::verb::get)( 2076 std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app))); 2077 } 2078 2079 inline void 2080 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2081 const std::string& dumpType) 2082 { 2083 std::string dumpPath; 2084 log_service::OverWritePolicy overWritePolicy = 2085 log_service::OverWritePolicy::Invalid; 2086 bool collectDiagnosticDataSupported = false; 2087 2088 if (dumpType == "BMC") 2089 { 2090 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump", 2091 BMCWEB_REDFISH_MANAGER_URI_NAME); 2092 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; 2093 collectDiagnosticDataSupported = true; 2094 } 2095 else if (dumpType == "FaultLog") 2096 { 2097 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog", 2098 BMCWEB_REDFISH_MANAGER_URI_NAME); 2099 overWritePolicy = log_service::OverWritePolicy::Unknown; 2100 collectDiagnosticDataSupported = false; 2101 } 2102 else if (dumpType == "System") 2103 { 2104 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump", 2105 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2106 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; 2107 collectDiagnosticDataSupported = true; 2108 } 2109 else 2110 { 2111 BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}", 2112 dumpType); 2113 messages::internalError(asyncResp->res); 2114 return; 2115 } 2116 2117 asyncResp->res.jsonValue["@odata.id"] = dumpPath; 2118 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 2119 asyncResp->res.jsonValue["Name"] = "Dump LogService"; 2120 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService"; 2121 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename(); 2122 asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy; 2123 2124 std::pair<std::string, std::string> redfishDateTimeOffset = 2125 redfish::time_utils::getDateTimeOffsetNow(); 2126 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 2127 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 2128 redfishDateTimeOffset.second; 2129 2130 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries"; 2131 2132 if (collectDiagnosticDataSupported) 2133 { 2134 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"] 2135 ["target"] = 2136 dumpPath + "/Actions/LogService.CollectDiagnosticData"; 2137 } 2138 2139 constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface}; 2140 dbus::utility::getSubTreePaths( 2141 "/xyz/openbmc_project/dump", 0, interfaces, 2142 [asyncResp, dumpType, dumpPath]( 2143 const boost::system::error_code& ec, 2144 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) { 2145 if (ec) 2146 { 2147 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}", 2148 ec); 2149 // Assume that getting an error simply means there are no dump 2150 // LogServices. Return without adding any error response. 2151 return; 2152 } 2153 std::string dbusDumpPath = getDumpPath(dumpType); 2154 for (const std::string& path : subTreePaths) 2155 { 2156 if (path == dbusDumpPath) 2157 { 2158 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 2159 ["target"] = 2160 dumpPath + "/Actions/LogService.ClearLog"; 2161 break; 2162 } 2163 } 2164 }); 2165 } 2166 2167 inline void handleLogServicesDumpServiceGet( 2168 crow::App& app, const std::string& dumpType, const crow::Request& req, 2169 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2170 const std::string& managerId) 2171 { 2172 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2173 { 2174 return; 2175 } 2176 2177 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2178 { 2179 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2180 return; 2181 } 2182 2183 getDumpServiceInfo(asyncResp, dumpType); 2184 } 2185 2186 inline void handleLogServicesDumpServiceComputerSystemGet( 2187 crow::App& app, const crow::Request& req, 2188 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2189 const std::string& chassisId) 2190 { 2191 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2192 { 2193 return; 2194 } 2195 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2196 { 2197 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2198 return; 2199 } 2200 getDumpServiceInfo(asyncResp, "System"); 2201 } 2202 2203 inline void handleLogServicesDumpEntriesCollectionGet( 2204 crow::App& app, const std::string& dumpType, const crow::Request& req, 2205 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2206 const std::string& managerId) 2207 { 2208 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2209 { 2210 return; 2211 } 2212 2213 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2214 { 2215 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2216 return; 2217 } 2218 getDumpEntryCollection(asyncResp, dumpType); 2219 } 2220 2221 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet( 2222 crow::App& app, const crow::Request& req, 2223 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2224 const std::string& chassisId) 2225 { 2226 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2227 { 2228 return; 2229 } 2230 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2231 { 2232 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2233 return; 2234 } 2235 getDumpEntryCollection(asyncResp, "System"); 2236 } 2237 2238 inline void handleLogServicesDumpEntryGet( 2239 crow::App& app, const std::string& dumpType, const crow::Request& req, 2240 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2241 const std::string& managerId, const std::string& dumpId) 2242 { 2243 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2244 { 2245 return; 2246 } 2247 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2248 { 2249 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2250 return; 2251 } 2252 getDumpEntryById(asyncResp, dumpId, dumpType); 2253 } 2254 2255 inline void handleLogServicesDumpEntryComputerSystemGet( 2256 crow::App& app, const crow::Request& req, 2257 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2258 const std::string& chassisId, const std::string& dumpId) 2259 { 2260 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2261 { 2262 return; 2263 } 2264 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2265 { 2266 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2267 return; 2268 } 2269 getDumpEntryById(asyncResp, dumpId, "System"); 2270 } 2271 2272 inline void handleLogServicesDumpEntryDelete( 2273 crow::App& app, const std::string& dumpType, const crow::Request& req, 2274 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2275 const std::string& managerId, const std::string& dumpId) 2276 { 2277 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2278 { 2279 return; 2280 } 2281 2282 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2283 { 2284 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2285 return; 2286 } 2287 deleteDumpEntry(asyncResp, dumpId, dumpType); 2288 } 2289 2290 inline void handleLogServicesDumpEntryComputerSystemDelete( 2291 crow::App& app, const crow::Request& req, 2292 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2293 const std::string& chassisId, const std::string& dumpId) 2294 { 2295 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2296 { 2297 return; 2298 } 2299 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2300 { 2301 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2302 return; 2303 } 2304 deleteDumpEntry(asyncResp, dumpId, "System"); 2305 } 2306 2307 inline void handleLogServicesDumpEntryDownloadGet( 2308 crow::App& app, const std::string& dumpType, const crow::Request& req, 2309 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2310 const std::string& managerId, const std::string& dumpId) 2311 { 2312 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2313 { 2314 return; 2315 } 2316 2317 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2318 { 2319 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2320 return; 2321 } 2322 downloadDumpEntry(asyncResp, dumpId, dumpType); 2323 } 2324 2325 inline void handleDBusEventLogEntryDownloadGet( 2326 crow::App& app, const std::string& dumpType, const crow::Request& req, 2327 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2328 const std::string& systemName, const std::string& entryID) 2329 { 2330 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2331 { 2332 return; 2333 } 2334 if (!http_helpers::isContentTypeAllowed( 2335 req.getHeaderValue("Accept"), 2336 http_helpers::ContentType::OctetStream, true)) 2337 { 2338 asyncResp->res.result(boost::beast::http::status::bad_request); 2339 return; 2340 } 2341 downloadEventLogEntry(asyncResp, systemName, entryID, dumpType); 2342 } 2343 2344 inline void handleLogServicesDumpCollectDiagnosticDataPost( 2345 crow::App& app, const std::string& dumpType, const crow::Request& req, 2346 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2347 const std::string& managerId) 2348 { 2349 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2350 { 2351 return; 2352 } 2353 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2354 { 2355 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2356 return; 2357 } 2358 2359 createDump(asyncResp, req, dumpType); 2360 } 2361 2362 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost( 2363 crow::App& app, const crow::Request& req, 2364 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2365 const std::string& systemName) 2366 { 2367 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2368 { 2369 return; 2370 } 2371 2372 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2373 { 2374 // Option currently returns no systems. TBD 2375 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2376 systemName); 2377 return; 2378 } 2379 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2380 { 2381 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2382 systemName); 2383 return; 2384 } 2385 createDump(asyncResp, req, "System"); 2386 } 2387 2388 inline void handleLogServicesDumpClearLogPost( 2389 crow::App& app, const std::string& dumpType, const crow::Request& req, 2390 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2391 const std::string& managerId) 2392 { 2393 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2394 { 2395 return; 2396 } 2397 2398 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2399 { 2400 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2401 return; 2402 } 2403 clearDump(asyncResp, dumpType); 2404 } 2405 2406 inline void handleLogServicesDumpClearLogComputerSystemPost( 2407 crow::App& app, const crow::Request& req, 2408 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2409 const std::string& systemName) 2410 { 2411 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2412 { 2413 return; 2414 } 2415 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2416 { 2417 // Option currently returns no systems. TBD 2418 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2419 systemName); 2420 return; 2421 } 2422 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2423 { 2424 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2425 systemName); 2426 return; 2427 } 2428 clearDump(asyncResp, "System"); 2429 } 2430 2431 inline void requestRoutesBMCDumpService(App& app) 2432 { 2433 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/") 2434 .privileges(redfish::privileges::getLogService) 2435 .methods(boost::beast::http::verb::get)(std::bind_front( 2436 handleLogServicesDumpServiceGet, std::ref(app), "BMC")); 2437 } 2438 2439 inline void requestRoutesBMCDumpEntryCollection(App& app) 2440 { 2441 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/") 2442 .privileges(redfish::privileges::getLogEntryCollection) 2443 .methods(boost::beast::http::verb::get)(std::bind_front( 2444 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC")); 2445 } 2446 2447 inline void requestRoutesBMCDumpEntry(App& app) 2448 { 2449 BMCWEB_ROUTE(app, 2450 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/") 2451 .privileges(redfish::privileges::getLogEntry) 2452 .methods(boost::beast::http::verb::get)(std::bind_front( 2453 handleLogServicesDumpEntryGet, std::ref(app), "BMC")); 2454 2455 BMCWEB_ROUTE(app, 2456 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/") 2457 .privileges(redfish::privileges::deleteLogEntry) 2458 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2459 handleLogServicesDumpEntryDelete, std::ref(app), "BMC")); 2460 } 2461 2462 inline void requestRoutesBMCDumpEntryDownload(App& app) 2463 { 2464 BMCWEB_ROUTE( 2465 app, 2466 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/") 2467 .privileges(redfish::privileges::getLogEntry) 2468 .methods(boost::beast::http::verb::get)(std::bind_front( 2469 handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC")); 2470 } 2471 2472 inline void requestRoutesBMCDumpCreate(App& app) 2473 { 2474 BMCWEB_ROUTE( 2475 app, 2476 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/") 2477 .privileges(redfish::privileges::postLogService) 2478 .methods(boost::beast::http::verb::post)( 2479 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost, 2480 std::ref(app), "BMC")); 2481 } 2482 2483 inline void requestRoutesBMCDumpClear(App& app) 2484 { 2485 BMCWEB_ROUTE( 2486 app, 2487 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/") 2488 .privileges(redfish::privileges::postLogService) 2489 .methods(boost::beast::http::verb::post)(std::bind_front( 2490 handleLogServicesDumpClearLogPost, std::ref(app), "BMC")); 2491 } 2492 2493 inline void requestRoutesDBusEventLogEntryDownload(App& app) 2494 { 2495 BMCWEB_ROUTE( 2496 app, 2497 "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment/") 2498 .privileges(redfish::privileges::getLogEntry) 2499 .methods(boost::beast::http::verb::get)(std::bind_front( 2500 handleDBusEventLogEntryDownloadGet, std::ref(app), "System")); 2501 } 2502 2503 inline void requestRoutesFaultLogDumpService(App& app) 2504 { 2505 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/") 2506 .privileges(redfish::privileges::getLogService) 2507 .methods(boost::beast::http::verb::get)(std::bind_front( 2508 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog")); 2509 } 2510 2511 inline void requestRoutesFaultLogDumpEntryCollection(App& app) 2512 { 2513 BMCWEB_ROUTE(app, 2514 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/") 2515 .privileges(redfish::privileges::getLogEntryCollection) 2516 .methods(boost::beast::http::verb::get)( 2517 std::bind_front(handleLogServicesDumpEntriesCollectionGet, 2518 std::ref(app), "FaultLog")); 2519 } 2520 2521 inline void requestRoutesFaultLogDumpEntry(App& app) 2522 { 2523 BMCWEB_ROUTE( 2524 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/") 2525 .privileges(redfish::privileges::getLogEntry) 2526 .methods(boost::beast::http::verb::get)(std::bind_front( 2527 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog")); 2528 2529 BMCWEB_ROUTE( 2530 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/") 2531 .privileges(redfish::privileges::deleteLogEntry) 2532 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2533 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog")); 2534 } 2535 2536 inline void requestRoutesFaultLogDumpClear(App& app) 2537 { 2538 BMCWEB_ROUTE( 2539 app, 2540 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/") 2541 .privileges(redfish::privileges::postLogService) 2542 .methods(boost::beast::http::verb::post)(std::bind_front( 2543 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog")); 2544 } 2545 2546 inline void requestRoutesSystemDumpService(App& app) 2547 { 2548 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/") 2549 .privileges(redfish::privileges::getLogService) 2550 .methods(boost::beast::http::verb::get)(std::bind_front( 2551 handleLogServicesDumpServiceComputerSystemGet, std::ref(app))); 2552 } 2553 2554 inline void requestRoutesSystemDumpEntryCollection(App& app) 2555 { 2556 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/") 2557 .privileges(redfish::privileges::getLogEntryCollection) 2558 .methods(boost::beast::http::verb::get)(std::bind_front( 2559 handleLogServicesDumpEntriesCollectionComputerSystemGet, 2560 std::ref(app))); 2561 } 2562 2563 inline void requestRoutesSystemDumpEntry(App& app) 2564 { 2565 BMCWEB_ROUTE(app, 2566 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/") 2567 .privileges(redfish::privileges::getLogEntry) 2568 .methods(boost::beast::http::verb::get)(std::bind_front( 2569 handleLogServicesDumpEntryComputerSystemGet, std::ref(app))); 2570 2571 BMCWEB_ROUTE(app, 2572 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/") 2573 .privileges(redfish::privileges::deleteLogEntry) 2574 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2575 handleLogServicesDumpEntryComputerSystemDelete, std::ref(app))); 2576 } 2577 2578 inline void requestRoutesSystemDumpCreate(App& app) 2579 { 2580 BMCWEB_ROUTE( 2581 app, 2582 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/") 2583 .privileges(redfish::privileges::postLogService) 2584 .methods(boost::beast::http::verb::post)(std::bind_front( 2585 handleLogServicesDumpCollectDiagnosticDataComputerSystemPost, 2586 std::ref(app))); 2587 } 2588 2589 inline void requestRoutesSystemDumpClear(App& app) 2590 { 2591 BMCWEB_ROUTE( 2592 app, 2593 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/") 2594 .privileges(redfish::privileges::postLogService) 2595 .methods(boost::beast::http::verb::post)(std::bind_front( 2596 handleLogServicesDumpClearLogComputerSystemPost, std::ref(app))); 2597 } 2598 2599 inline void requestRoutesCrashdumpService(App& app) 2600 { 2601 // Note: Deviated from redfish privilege registry for GET & HEAD 2602 // method for security reasons. 2603 /** 2604 * Functions triggers appropriate requests on DBus 2605 */ 2606 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/") 2607 // This is incorrect, should be: 2608 //.privileges(redfish::privileges::getLogService) 2609 .privileges({{"ConfigureManager"}}) 2610 .methods( 2611 boost::beast::http::verb:: 2612 get)([&app](const crow::Request& req, 2613 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2614 const std::string& systemName) { 2615 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2616 { 2617 return; 2618 } 2619 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2620 { 2621 // Option currently returns no systems. TBD 2622 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2623 systemName); 2624 return; 2625 } 2626 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2627 { 2628 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2629 systemName); 2630 return; 2631 } 2632 2633 // Copy over the static data to include the entries added by 2634 // SubRoute 2635 asyncResp->res.jsonValue["@odata.id"] = 2636 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", 2637 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2638 asyncResp->res.jsonValue["@odata.type"] = 2639 "#LogService.v1_2_0.LogService"; 2640 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service"; 2641 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service"; 2642 asyncResp->res.jsonValue["Id"] = "Crashdump"; 2643 asyncResp->res.jsonValue["OverWritePolicy"] = 2644 log_service::OverWritePolicy::WrapsWhenFull; 2645 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 2646 2647 std::pair<std::string, std::string> redfishDateTimeOffset = 2648 redfish::time_utils::getDateTimeOffsetNow(); 2649 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 2650 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 2651 redfishDateTimeOffset.second; 2652 2653 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( 2654 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", 2655 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2656 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 2657 ["target"] = std::format( 2658 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog", 2659 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2660 asyncResp->res 2661 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"] 2662 ["target"] = std::format( 2663 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData", 2664 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2665 }); 2666 } 2667 2668 void inline requestRoutesCrashdumpClear(App& app) 2669 { 2670 BMCWEB_ROUTE( 2671 app, 2672 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/") 2673 // This is incorrect, should be: 2674 //.privileges(redfish::privileges::postLogService) 2675 .privileges({{"ConfigureComponents"}}) 2676 .methods(boost::beast::http::verb::post)( 2677 [&app](const crow::Request& req, 2678 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2679 const std::string& systemName) { 2680 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2681 { 2682 return; 2683 } 2684 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2685 { 2686 // Option currently returns no systems. TBD 2687 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2688 systemName); 2689 return; 2690 } 2691 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2692 { 2693 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2694 systemName); 2695 return; 2696 } 2697 crow::connections::systemBus->async_method_call( 2698 [asyncResp](const boost::system::error_code& ec, 2699 const std::string&) { 2700 if (ec) 2701 { 2702 messages::internalError(asyncResp->res); 2703 return; 2704 } 2705 messages::success(asyncResp->res); 2706 }, 2707 crashdumpObject, crashdumpPath, deleteAllInterface, 2708 "DeleteAll"); 2709 }); 2710 } 2711 2712 inline void 2713 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2714 const std::string& logID, nlohmann::json& logEntryJson) 2715 { 2716 auto getStoredLogCallback = 2717 [asyncResp, logID, 2718 &logEntryJson](const boost::system::error_code& ec, 2719 const dbus::utility::DBusPropertiesMap& params) { 2720 if (ec) 2721 { 2722 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message()); 2723 if (ec.value() == 2724 boost::system::linux_error::bad_request_descriptor) 2725 { 2726 messages::resourceNotFound(asyncResp->res, "LogEntry", 2727 logID); 2728 } 2729 else 2730 { 2731 messages::internalError(asyncResp->res); 2732 } 2733 return; 2734 } 2735 2736 std::string timestamp{}; 2737 std::string filename{}; 2738 std::string logfile{}; 2739 parseCrashdumpParameters(params, filename, timestamp, logfile); 2740 2741 if (filename.empty() || timestamp.empty()) 2742 { 2743 messages::resourceNotFound(asyncResp->res, "LogEntry", logID); 2744 return; 2745 } 2746 2747 std::string crashdumpURI = 2748 std::format( 2749 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/", 2750 BMCWEB_REDFISH_SYSTEM_URI_NAME) + 2751 logID + "/" + filename; 2752 nlohmann::json::object_t logEntry; 2753 logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 2754 logEntry["@odata.id"] = boost::urls::format( 2755 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}", 2756 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID); 2757 logEntry["Name"] = "CPU Crashdump"; 2758 logEntry["Id"] = logID; 2759 logEntry["EntryType"] = log_entry::LogEntryType::Oem; 2760 logEntry["AdditionalDataURI"] = std::move(crashdumpURI); 2761 logEntry["DiagnosticDataType"] = "OEM"; 2762 logEntry["OEMDiagnosticDataType"] = "PECICrashdump"; 2763 logEntry["Created"] = std::move(timestamp); 2764 2765 // If logEntryJson references an array of LogEntry resources 2766 // ('Members' list), then push this as a new entry, otherwise set it 2767 // directly 2768 if (logEntryJson.is_array()) 2769 { 2770 logEntryJson.push_back(logEntry); 2771 asyncResp->res.jsonValue["Members@odata.count"] = 2772 logEntryJson.size(); 2773 } 2774 else 2775 { 2776 logEntryJson.update(logEntry); 2777 } 2778 }; 2779 dbus::utility::getAllProperties( 2780 crashdumpObject, crashdumpPath + std::string("/") + logID, 2781 crashdumpInterface, std::move(getStoredLogCallback)); 2782 } 2783 2784 inline void requestRoutesCrashdumpEntryCollection(App& app) 2785 { 2786 // Note: Deviated from redfish privilege registry for GET & HEAD 2787 // method for security reasons. 2788 /** 2789 * Functions triggers appropriate requests on DBus 2790 */ 2791 BMCWEB_ROUTE(app, 2792 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/") 2793 // This is incorrect, should be. 2794 //.privileges(redfish::privileges::postLogEntryCollection) 2795 .privileges({{"ConfigureComponents"}}) 2796 .methods( 2797 boost::beast::http::verb:: 2798 get)([&app](const crow::Request& req, 2799 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2800 const std::string& systemName) { 2801 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2802 { 2803 return; 2804 } 2805 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2806 { 2807 // Option currently returns no systems. TBD 2808 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2809 systemName); 2810 return; 2811 } 2812 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2813 { 2814 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2815 systemName); 2816 return; 2817 } 2818 2819 constexpr std::array<std::string_view, 1> interfaces = { 2820 crashdumpInterface}; 2821 dbus::utility::getSubTreePaths( 2822 "/", 0, interfaces, 2823 [asyncResp](const boost::system::error_code& ec, 2824 const std::vector<std::string>& resp) { 2825 if (ec) 2826 { 2827 if (ec.value() != 2828 boost::system::errc::no_such_file_or_directory) 2829 { 2830 BMCWEB_LOG_DEBUG("failed to get entries ec: {}", 2831 ec.message()); 2832 messages::internalError(asyncResp->res); 2833 return; 2834 } 2835 } 2836 asyncResp->res.jsonValue["@odata.type"] = 2837 "#LogEntryCollection.LogEntryCollection"; 2838 asyncResp->res.jsonValue["@odata.id"] = std::format( 2839 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", 2840 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2841 asyncResp->res.jsonValue["Name"] = 2842 "Open BMC Crashdump Entries"; 2843 asyncResp->res.jsonValue["Description"] = 2844 "Collection of Crashdump Entries"; 2845 asyncResp->res.jsonValue["Members"] = 2846 nlohmann::json::array(); 2847 asyncResp->res.jsonValue["Members@odata.count"] = 0; 2848 2849 for (const std::string& path : resp) 2850 { 2851 const sdbusplus::message::object_path objPath(path); 2852 // Get the log ID 2853 std::string logID = objPath.filename(); 2854 if (logID.empty()) 2855 { 2856 continue; 2857 } 2858 // Add the log entry to the array 2859 logCrashdumpEntry(asyncResp, logID, 2860 asyncResp->res.jsonValue["Members"]); 2861 } 2862 }); 2863 }); 2864 } 2865 2866 inline void requestRoutesCrashdumpEntry(App& app) 2867 { 2868 // Note: Deviated from redfish privilege registry for GET & HEAD 2869 // method for security reasons. 2870 2871 BMCWEB_ROUTE( 2872 app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/") 2873 // this is incorrect, should be 2874 // .privileges(redfish::privileges::getLogEntry) 2875 .privileges({{"ConfigureComponents"}}) 2876 .methods(boost::beast::http::verb::get)( 2877 [&app](const crow::Request& req, 2878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2879 const std::string& systemName, const std::string& param) { 2880 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2881 { 2882 return; 2883 } 2884 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2885 { 2886 // Option currently returns no systems. TBD 2887 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2888 systemName); 2889 return; 2890 } 2891 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2892 { 2893 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2894 systemName); 2895 return; 2896 } 2897 const std::string& logID = param; 2898 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); 2899 }); 2900 } 2901 2902 inline void requestRoutesCrashdumpFile(App& app) 2903 { 2904 // Note: Deviated from redfish privilege registry for GET & HEAD 2905 // method for security reasons. 2906 BMCWEB_ROUTE( 2907 app, 2908 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/") 2909 .privileges(redfish::privileges::getLogEntry) 2910 .methods(boost::beast::http::verb::get)( 2911 [](const crow::Request& req, 2912 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2913 const std::string& systemName, const std::string& logID, 2914 const std::string& fileName) { 2915 // Do not call getRedfishRoute here since the crashdump file is 2916 // not a Redfish resource. 2917 2918 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2919 { 2920 // Option currently returns no systems. TBD 2921 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2922 systemName); 2923 return; 2924 } 2925 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2926 { 2927 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2928 systemName); 2929 return; 2930 } 2931 2932 auto getStoredLogCallback = 2933 [asyncResp, logID, fileName, 2934 url(boost::urls::url(req.url()))]( 2935 const boost::system::error_code& ec, 2936 const std::vector<std::pair< 2937 std::string, dbus::utility::DbusVariantType>>& 2938 resp) { 2939 if (ec) 2940 { 2941 BMCWEB_LOG_DEBUG("failed to get log ec: {}", 2942 ec.message()); 2943 messages::internalError(asyncResp->res); 2944 return; 2945 } 2946 2947 std::string dbusFilename{}; 2948 std::string dbusTimestamp{}; 2949 std::string dbusFilepath{}; 2950 2951 parseCrashdumpParameters(resp, dbusFilename, 2952 dbusTimestamp, dbusFilepath); 2953 2954 if (dbusFilename.empty() || dbusTimestamp.empty() || 2955 dbusFilepath.empty()) 2956 { 2957 messages::resourceNotFound(asyncResp->res, 2958 "LogEntry", logID); 2959 return; 2960 } 2961 2962 // Verify the file name parameter is correct 2963 if (fileName != dbusFilename) 2964 { 2965 messages::resourceNotFound(asyncResp->res, 2966 "LogEntry", logID); 2967 return; 2968 } 2969 2970 if (asyncResp->res.openFile(dbusFilepath) != 2971 crow::OpenCode::Success) 2972 { 2973 messages::resourceNotFound(asyncResp->res, 2974 "LogEntry", logID); 2975 return; 2976 } 2977 2978 // Configure this to be a file download when accessed 2979 // from a browser 2980 asyncResp->res.addHeader( 2981 boost::beast::http::field::content_disposition, 2982 "attachment"); 2983 }; 2984 dbus::utility::getAllProperties( 2985 *crow::connections::systemBus, crashdumpObject, 2986 crashdumpPath + std::string("/") + logID, 2987 crashdumpInterface, std::move(getStoredLogCallback)); 2988 }); 2989 } 2990 2991 enum class OEMDiagnosticType 2992 { 2993 onDemand, 2994 telemetry, 2995 invalid, 2996 }; 2997 2998 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr) 2999 { 3000 if (oemDiagStr == "OnDemand") 3001 { 3002 return OEMDiagnosticType::onDemand; 3003 } 3004 if (oemDiagStr == "Telemetry") 3005 { 3006 return OEMDiagnosticType::telemetry; 3007 } 3008 3009 return OEMDiagnosticType::invalid; 3010 } 3011 3012 inline void requestRoutesCrashdumpCollect(App& app) 3013 { 3014 // Note: Deviated from redfish privilege registry for GET & HEAD 3015 // method for security reasons. 3016 BMCWEB_ROUTE( 3017 app, 3018 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/") 3019 // The below is incorrect; Should be ConfigureManager 3020 //.privileges(redfish::privileges::postLogService) 3021 .privileges({{"ConfigureComponents"}}) 3022 .methods(boost::beast::http::verb::post)( 3023 [&app](const crow::Request& req, 3024 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3025 const std::string& systemName) { 3026 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3027 { 3028 return; 3029 } 3030 3031 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 3032 { 3033 // Option currently returns no systems. TBD 3034 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3035 systemName); 3036 return; 3037 } 3038 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 3039 { 3040 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3041 systemName); 3042 return; 3043 } 3044 3045 std::string diagnosticDataType; 3046 std::string oemDiagnosticDataType; 3047 if (!redfish::json_util::readJsonAction( // 3048 req, asyncResp->res, // 3049 "DiagnosticDataType", diagnosticDataType, // 3050 "OEMDiagnosticDataType", oemDiagnosticDataType // 3051 )) 3052 { 3053 return; 3054 } 3055 3056 if (diagnosticDataType != "OEM") 3057 { 3058 BMCWEB_LOG_ERROR( 3059 "Only OEM DiagnosticDataType supported for Crashdump"); 3060 messages::actionParameterValueFormatError( 3061 asyncResp->res, diagnosticDataType, 3062 "DiagnosticDataType", "CollectDiagnosticData"); 3063 return; 3064 } 3065 3066 OEMDiagnosticType oemDiagType = 3067 getOEMDiagnosticType(oemDiagnosticDataType); 3068 3069 std::string iface; 3070 std::string method; 3071 std::string taskMatchStr; 3072 if (oemDiagType == OEMDiagnosticType::onDemand) 3073 { 3074 iface = crashdumpOnDemandInterface; 3075 method = "GenerateOnDemandLog"; 3076 taskMatchStr = 3077 "type='signal'," 3078 "interface='org.freedesktop.DBus.Properties'," 3079 "member='PropertiesChanged'," 3080 "arg0namespace='com.intel.crashdump'"; 3081 } 3082 else if (oemDiagType == OEMDiagnosticType::telemetry) 3083 { 3084 iface = crashdumpTelemetryInterface; 3085 method = "GenerateTelemetryLog"; 3086 taskMatchStr = 3087 "type='signal'," 3088 "interface='org.freedesktop.DBus.Properties'," 3089 "member='PropertiesChanged'," 3090 "arg0namespace='com.intel.crashdump'"; 3091 } 3092 else 3093 { 3094 BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}", 3095 oemDiagnosticDataType); 3096 messages::actionParameterValueFormatError( 3097 asyncResp->res, oemDiagnosticDataType, 3098 "OEMDiagnosticDataType", "CollectDiagnosticData"); 3099 return; 3100 } 3101 3102 auto collectCrashdumpCallback = 3103 [asyncResp, payload(task::Payload(req)), 3104 taskMatchStr](const boost::system::error_code& ec, 3105 const std::string&) mutable { 3106 if (ec) 3107 { 3108 if (ec.value() == 3109 boost::system::errc::operation_not_supported) 3110 { 3111 messages::resourceInStandby(asyncResp->res); 3112 } 3113 else if (ec.value() == boost::system::errc:: 3114 device_or_resource_busy) 3115 { 3116 messages::serviceTemporarilyUnavailable( 3117 asyncResp->res, "60"); 3118 } 3119 else 3120 { 3121 messages::internalError(asyncResp->res); 3122 } 3123 return; 3124 } 3125 std::shared_ptr<task::TaskData> task = 3126 task::TaskData::createTask( 3127 [](const boost::system::error_code& ec2, 3128 sdbusplus::message_t&, 3129 const std::shared_ptr<task::TaskData>& 3130 taskData) { 3131 if (!ec2) 3132 { 3133 taskData->messages.emplace_back( 3134 messages::taskCompletedOK( 3135 std::to_string( 3136 taskData->index))); 3137 taskData->state = "Completed"; 3138 } 3139 return task::completed; 3140 }, 3141 taskMatchStr); 3142 3143 task->startTimer(std::chrono::minutes(5)); 3144 task->populateResp(asyncResp->res); 3145 task->payload.emplace(std::move(payload)); 3146 }; 3147 3148 crow::connections::systemBus->async_method_call( 3149 std::move(collectCrashdumpCallback), crashdumpObject, 3150 crashdumpPath, iface, method); 3151 }); 3152 } 3153 3154 inline void dBusLogServiceActionsClear( 3155 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 3156 { 3157 BMCWEB_LOG_DEBUG("Do delete all entries."); 3158 3159 // Process response from Logging service. 3160 auto respHandler = [asyncResp](const boost::system::error_code& ec) { 3161 BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done"); 3162 if (ec) 3163 { 3164 // TODO Handle for specific error code 3165 BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec); 3166 asyncResp->res.result( 3167 boost::beast::http::status::internal_server_error); 3168 return; 3169 } 3170 3171 asyncResp->res.result(boost::beast::http::status::no_content); 3172 }; 3173 3174 // Make call to Logging service to request Clear Log 3175 crow::connections::systemBus->async_method_call( 3176 respHandler, "xyz.openbmc_project.Logging", 3177 "/xyz/openbmc_project/logging", 3178 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 3179 } 3180 3181 /** 3182 * DBusLogServiceActionsClear class supports POST method for ClearLog action. 3183 */ 3184 inline void requestRoutesDBusLogServiceActionsClear(App& app) 3185 { 3186 /** 3187 * Function handles POST method request. 3188 * The Clear Log actions does not require any parameter.The action deletes 3189 * all entries found in the Entries collection for this Log Service. 3190 */ 3191 3192 BMCWEB_ROUTE( 3193 app, 3194 "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/") 3195 .privileges(redfish::privileges::postLogService) 3196 .methods(boost::beast::http::verb::post)( 3197 [&app](const crow::Request& req, 3198 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3199 const std::string& systemName) { 3200 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3201 { 3202 return; 3203 } 3204 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 3205 { 3206 // Option currently returns no systems. TBD 3207 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3208 systemName); 3209 return; 3210 } 3211 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 3212 { 3213 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3214 systemName); 3215 return; 3216 } 3217 dBusLogServiceActionsClear(asyncResp); 3218 }); 3219 } 3220 3221 } // namespace redfish 3222