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