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 "human_sort.hpp" 19 #include "logging.hpp" 20 #include "query.hpp" 21 #include "registries/privilege_registry.hpp" 22 #include "str_utility.hpp" 23 #include "task.hpp" 24 #include "task_messages.hpp" 25 #include "utils/dbus_utils.hpp" 26 #include "utils/etag_utils.hpp" 27 #include "utils/json_utils.hpp" 28 #include "utils/log_services_utils.hpp" 29 #include "utils/time_utils.hpp" 30 31 #include <asm-generic/errno.h> 32 #include <systemd/sd-bus.h> 33 #include <tinyxml2.h> 34 #include <unistd.h> 35 36 #include <boost/beast/http/field.hpp> 37 #include <boost/beast/http/status.hpp> 38 #include <boost/beast/http/verb.hpp> 39 #include <boost/system/linux_error.hpp> 40 #include <boost/url/format.hpp> 41 #include <boost/url/url.hpp> 42 #include <sdbusplus/message.hpp> 43 #include <sdbusplus/message/native_types.hpp> 44 #include <sdbusplus/unpack_properties.hpp> 45 46 #include <algorithm> 47 #include <array> 48 #include <chrono> 49 #include <cstdint> 50 #include <filesystem> 51 #include <format> 52 #include <functional> 53 #include <iterator> 54 #include <memory> 55 #include <optional> 56 #include <ranges> 57 #include <span> 58 #include <string> 59 #include <string_view> 60 #include <utility> 61 #include <variant> 62 #include <vector> 63 64 namespace redfish 65 { 66 67 constexpr const char* crashdumpObject = "com.intel.crashdump"; 68 constexpr const char* crashdumpPath = "/com/intel/crashdump"; 69 constexpr const char* crashdumpInterface = "com.intel.crashdump"; 70 constexpr const char* deleteAllInterface = 71 "xyz.openbmc_project.Collection.DeleteAll"; 72 constexpr const char* crashdumpOnDemandInterface = 73 "com.intel.crashdump.OnDemand"; 74 constexpr const char* crashdumpTelemetryInterface = 75 "com.intel.crashdump.Telemetry"; 76 77 enum class DumpCreationProgress 78 { 79 DUMP_CREATE_SUCCESS, 80 DUMP_CREATE_FAILED, 81 DUMP_CREATE_INPROGRESS 82 }; 83 84 inline std::string getDumpPath(std::string_view dumpType) 85 { 86 std::string dbusDumpPath = "/xyz/openbmc_project/dump/"; 87 std::ranges::transform(dumpType, std::back_inserter(dbusDumpPath), 88 bmcweb::asciiToLower); 89 90 return dbusDumpPath; 91 } 92 93 inline log_entry::OriginatorTypes mapDbusOriginatorTypeToRedfish( 94 const std::string& originatorType) 95 { 96 if (originatorType == 97 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client") 98 { 99 return log_entry::OriginatorTypes::Client; 100 } 101 if (originatorType == 102 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Internal") 103 { 104 return log_entry::OriginatorTypes::Internal; 105 } 106 if (originatorType == 107 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.SupportingService") 108 { 109 return log_entry::OriginatorTypes::SupportingService; 110 } 111 return log_entry::OriginatorTypes::Invalid; 112 } 113 114 inline void parseDumpEntryFromDbusObject( 115 const dbus::utility::ManagedObjectType::value_type& object, 116 std::string& dumpStatus, uint64_t& size, uint64_t& timestampUs, 117 std::string& originatorId, log_entry::OriginatorTypes& originatorType, 118 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 119 { 120 for (const auto& interfaceMap : object.second) 121 { 122 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress") 123 { 124 for (const auto& propertyMap : interfaceMap.second) 125 { 126 if (propertyMap.first == "Status") 127 { 128 const auto* status = 129 std::get_if<std::string>(&propertyMap.second); 130 if (status == nullptr) 131 { 132 messages::internalError(asyncResp->res); 133 break; 134 } 135 dumpStatus = *status; 136 } 137 } 138 } 139 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry") 140 { 141 for (const auto& propertyMap : interfaceMap.second) 142 { 143 if (propertyMap.first == "Size") 144 { 145 const auto* sizePtr = 146 std::get_if<uint64_t>(&propertyMap.second); 147 if (sizePtr == nullptr) 148 { 149 messages::internalError(asyncResp->res); 150 break; 151 } 152 size = *sizePtr; 153 break; 154 } 155 } 156 } 157 else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime") 158 { 159 for (const auto& propertyMap : interfaceMap.second) 160 { 161 if (propertyMap.first == "Elapsed") 162 { 163 const uint64_t* usecsTimeStamp = 164 std::get_if<uint64_t>(&propertyMap.second); 165 if (usecsTimeStamp == nullptr) 166 { 167 messages::internalError(asyncResp->res); 168 break; 169 } 170 timestampUs = *usecsTimeStamp; 171 break; 172 } 173 } 174 } 175 else if (interfaceMap.first == 176 "xyz.openbmc_project.Common.OriginatedBy") 177 { 178 for (const auto& propertyMap : interfaceMap.second) 179 { 180 if (propertyMap.first == "OriginatorId") 181 { 182 const std::string* id = 183 std::get_if<std::string>(&propertyMap.second); 184 if (id == nullptr) 185 { 186 messages::internalError(asyncResp->res); 187 break; 188 } 189 originatorId = *id; 190 } 191 192 if (propertyMap.first == "OriginatorType") 193 { 194 const std::string* type = 195 std::get_if<std::string>(&propertyMap.second); 196 if (type == nullptr) 197 { 198 messages::internalError(asyncResp->res); 199 break; 200 } 201 202 originatorType = mapDbusOriginatorTypeToRedfish(*type); 203 if (originatorType == log_entry::OriginatorTypes::Invalid) 204 { 205 messages::internalError(asyncResp->res); 206 break; 207 } 208 } 209 } 210 } 211 } 212 } 213 214 static std::string getDumpEntriesPath(const std::string& dumpType) 215 { 216 std::string entriesPath; 217 218 if (dumpType == "BMC") 219 { 220 entriesPath = 221 std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/", 222 BMCWEB_REDFISH_MANAGER_URI_NAME); 223 } 224 else if (dumpType == "FaultLog") 225 { 226 entriesPath = 227 std::format("/redfish/v1/Managers/{}/LogServices/FaultLog/Entries/", 228 BMCWEB_REDFISH_MANAGER_URI_NAME); 229 } 230 else if (dumpType == "System") 231 { 232 entriesPath = 233 std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/", 234 BMCWEB_REDFISH_SYSTEM_URI_NAME); 235 } 236 else 237 { 238 BMCWEB_LOG_ERROR("getDumpEntriesPath() invalid dump type: {}", 239 dumpType); 240 } 241 242 // Returns empty string on error 243 return entriesPath; 244 } 245 246 inline void getDumpEntryCollection( 247 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 248 const std::string& dumpType) 249 { 250 std::string entriesPath = getDumpEntriesPath(dumpType); 251 if (entriesPath.empty()) 252 { 253 messages::internalError(asyncResp->res); 254 return; 255 } 256 257 sdbusplus::message::object_path path("/xyz/openbmc_project/dump"); 258 dbus::utility::getManagedObjects( 259 "xyz.openbmc_project.Dump.Manager", path, 260 [asyncResp, entriesPath, 261 dumpType](const boost::system::error_code& ec, 262 const dbus::utility::ManagedObjectType& objects) { 263 if (ec) 264 { 265 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); 266 messages::internalError(asyncResp->res); 267 return; 268 } 269 270 // Remove ending slash 271 std::string odataIdStr = entriesPath; 272 if (!odataIdStr.empty()) 273 { 274 odataIdStr.pop_back(); 275 } 276 277 asyncResp->res.jsonValue["@odata.type"] = 278 "#LogEntryCollection.LogEntryCollection"; 279 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr); 280 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries"; 281 asyncResp->res.jsonValue["Description"] = 282 "Collection of " + dumpType + " Dump Entries"; 283 284 nlohmann::json::array_t entriesArray; 285 std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; 286 287 dbus::utility::ManagedObjectType resp(objects); 288 std::ranges::sort(resp, [](const auto& l, const auto& r) { 289 return AlphanumLess<std::string>()(l.first.filename(), 290 r.first.filename()); 291 }); 292 293 for (auto& object : resp) 294 { 295 if (object.first.str.find(dumpEntryPath) == std::string::npos) 296 { 297 continue; 298 } 299 uint64_t timestampUs = 0; 300 uint64_t size = 0; 301 std::string dumpStatus; 302 std::string originatorId; 303 log_entry::OriginatorTypes originatorType = 304 log_entry::OriginatorTypes::Internal; 305 nlohmann::json::object_t thisEntry; 306 307 std::string entryID = object.first.filename(); 308 if (entryID.empty()) 309 { 310 continue; 311 } 312 313 parseDumpEntryFromDbusObject(object, dumpStatus, size, 314 timestampUs, originatorId, 315 originatorType, asyncResp); 316 317 if (dumpStatus != 318 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && 319 !dumpStatus.empty()) 320 { 321 // Dump status is not Complete, no need to enumerate 322 continue; 323 } 324 325 thisEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; 326 thisEntry["@odata.id"] = entriesPath + entryID; 327 thisEntry["Id"] = entryID; 328 thisEntry["EntryType"] = "Event"; 329 thisEntry["Name"] = dumpType + " Dump Entry"; 330 thisEntry["Created"] = 331 redfish::time_utils::getDateTimeUintUs(timestampUs); 332 333 if (!originatorId.empty()) 334 { 335 thisEntry["Originator"] = originatorId; 336 thisEntry["OriginatorType"] = originatorType; 337 } 338 339 if (dumpType == "BMC") 340 { 341 thisEntry["DiagnosticDataType"] = "Manager"; 342 thisEntry["AdditionalDataURI"] = 343 entriesPath + entryID + "/attachment"; 344 thisEntry["AdditionalDataSizeBytes"] = size; 345 } 346 else if (dumpType == "System") 347 { 348 thisEntry["DiagnosticDataType"] = "OEM"; 349 thisEntry["OEMDiagnosticDataType"] = "System"; 350 thisEntry["AdditionalDataURI"] = 351 entriesPath + entryID + "/attachment"; 352 thisEntry["AdditionalDataSizeBytes"] = size; 353 } 354 entriesArray.emplace_back(std::move(thisEntry)); 355 } 356 asyncResp->res.jsonValue["Members@odata.count"] = 357 entriesArray.size(); 358 asyncResp->res.jsonValue["Members"] = std::move(entriesArray); 359 }); 360 } 361 362 inline void getDumpEntryById( 363 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 364 const std::string& entryID, const std::string& dumpType) 365 { 366 std::string entriesPath = getDumpEntriesPath(dumpType); 367 if (entriesPath.empty()) 368 { 369 messages::internalError(asyncResp->res); 370 return; 371 } 372 373 sdbusplus::message::object_path path("/xyz/openbmc_project/dump"); 374 dbus::utility::getManagedObjects( 375 "xyz.openbmc_project.Dump.Manager", path, 376 [asyncResp, entryID, dumpType, 377 entriesPath](const boost::system::error_code& ec, 378 const dbus::utility::ManagedObjectType& resp) { 379 if (ec) 380 { 381 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); 382 messages::internalError(asyncResp->res); 383 return; 384 } 385 386 bool foundDumpEntry = false; 387 std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; 388 389 for (const auto& objectPath : resp) 390 { 391 if (objectPath.first.str != dumpEntryPath + entryID) 392 { 393 continue; 394 } 395 396 foundDumpEntry = true; 397 uint64_t timestampUs = 0; 398 uint64_t size = 0; 399 std::string dumpStatus; 400 std::string originatorId; 401 log_entry::OriginatorTypes originatorType = 402 log_entry::OriginatorTypes::Internal; 403 404 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size, 405 timestampUs, originatorId, 406 originatorType, asyncResp); 407 408 if (dumpStatus != 409 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && 410 !dumpStatus.empty()) 411 { 412 // Dump status is not Complete 413 // return not found until status is changed to Completed 414 messages::resourceNotFound(asyncResp->res, 415 dumpType + " dump", entryID); 416 return; 417 } 418 419 asyncResp->res.jsonValue["@odata.type"] = 420 "#LogEntry.v1_11_0.LogEntry"; 421 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID; 422 asyncResp->res.jsonValue["Id"] = entryID; 423 asyncResp->res.jsonValue["EntryType"] = "Event"; 424 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry"; 425 asyncResp->res.jsonValue["Created"] = 426 redfish::time_utils::getDateTimeUintUs(timestampUs); 427 428 if (!originatorId.empty()) 429 { 430 asyncResp->res.jsonValue["Originator"] = originatorId; 431 asyncResp->res.jsonValue["OriginatorType"] = originatorType; 432 } 433 434 if (dumpType == "BMC") 435 { 436 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager"; 437 asyncResp->res.jsonValue["AdditionalDataURI"] = 438 entriesPath + entryID + "/attachment"; 439 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; 440 } 441 else if (dumpType == "System") 442 { 443 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM"; 444 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = 445 "System"; 446 asyncResp->res.jsonValue["AdditionalDataURI"] = 447 entriesPath + entryID + "/attachment"; 448 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; 449 } 450 } 451 if (!foundDumpEntry) 452 { 453 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); 454 messages::resourceNotFound(asyncResp->res, dumpType + " dump", 455 entryID); 456 return; 457 } 458 }); 459 } 460 461 inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 462 const std::string& entryID, 463 const std::string& dumpType) 464 { 465 auto respHandler = [asyncResp, 466 entryID](const boost::system::error_code& ec) { 467 BMCWEB_LOG_DEBUG("Dump Entry doDelete callback: Done"); 468 if (ec) 469 { 470 if (ec.value() == EBADR) 471 { 472 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 473 return; 474 } 475 BMCWEB_LOG_ERROR( 476 "Dump (DBus) doDelete respHandler got error {} entryID={}", ec, 477 entryID); 478 messages::internalError(asyncResp->res); 479 return; 480 } 481 }; 482 483 dbus::utility::async_method_call( 484 asyncResp, respHandler, "xyz.openbmc_project.Dump.Manager", 485 std::format("{}/entry/{}", getDumpPath(dumpType), entryID), 486 "xyz.openbmc_project.Object.Delete", "Delete"); 487 } 488 489 inline void downloadDumpEntry( 490 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 491 const std::string& entryID, const std::string& dumpType) 492 { 493 if (dumpType != "BMC") 494 { 495 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); 496 messages::resourceNotFound(asyncResp->res, dumpType + " dump", entryID); 497 return; 498 } 499 500 std::string dumpEntryPath = 501 std::format("{}/entry/{}", getDumpPath(dumpType), entryID); 502 503 auto downloadDumpEntryHandler = 504 [asyncResp, entryID, 505 dumpType](const boost::system::error_code& ec, 506 const sdbusplus::message::unix_fd& unixfd) { 507 log_services_utils::downloadEntryCallback(asyncResp, entryID, 508 dumpType, ec, unixfd); 509 }; 510 511 dbus::utility::async_method_call( 512 asyncResp, std::move(downloadDumpEntryHandler), 513 "xyz.openbmc_project.Dump.Manager", dumpEntryPath, 514 "xyz.openbmc_project.Dump.Entry", "GetFileHandle"); 515 } 516 517 inline DumpCreationProgress mapDbusStatusToDumpProgress( 518 const std::string& status) 519 { 520 if (status == 521 "xyz.openbmc_project.Common.Progress.OperationStatus.Failed" || 522 status == "xyz.openbmc_project.Common.Progress.OperationStatus.Aborted") 523 { 524 return DumpCreationProgress::DUMP_CREATE_FAILED; 525 } 526 if (status == 527 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed") 528 { 529 return DumpCreationProgress::DUMP_CREATE_SUCCESS; 530 } 531 return DumpCreationProgress::DUMP_CREATE_INPROGRESS; 532 } 533 534 inline DumpCreationProgress getDumpCompletionStatus( 535 const dbus::utility::DBusPropertiesMap& values) 536 { 537 for (const auto& [key, val] : values) 538 { 539 if (key == "Status") 540 { 541 const std::string* value = std::get_if<std::string>(&val); 542 if (value == nullptr) 543 { 544 BMCWEB_LOG_ERROR("Status property value is null"); 545 return DumpCreationProgress::DUMP_CREATE_FAILED; 546 } 547 return mapDbusStatusToDumpProgress(*value); 548 } 549 } 550 return DumpCreationProgress::DUMP_CREATE_INPROGRESS; 551 } 552 553 inline std::string getDumpEntryPath(const std::string& dumpPath) 554 { 555 if (dumpPath == "/xyz/openbmc_project/dump/bmc/entry") 556 { 557 return std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/", 558 BMCWEB_REDFISH_MANAGER_URI_NAME); 559 } 560 if (dumpPath == "/xyz/openbmc_project/dump/system/entry") 561 { 562 return std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/", 563 BMCWEB_REDFISH_SYSTEM_URI_NAME); 564 } 565 return ""; 566 } 567 568 inline void createDumpTaskCallback( 569 task::Payload&& payload, 570 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 571 const sdbusplus::message::object_path& createdObjPath) 572 { 573 const std::string dumpPath = createdObjPath.parent_path().str; 574 const std::string dumpId = createdObjPath.filename(); 575 576 std::string dumpEntryPath = getDumpEntryPath(dumpPath); 577 578 if (dumpEntryPath.empty()) 579 { 580 BMCWEB_LOG_ERROR("Invalid dump type received"); 581 messages::internalError(asyncResp->res); 582 return; 583 } 584 585 dbus::utility::async_method_call( 586 asyncResp, 587 [asyncResp, payload = std::move(payload), createdObjPath, 588 dumpEntryPath{std::move(dumpEntryPath)}, 589 dumpId](const boost::system::error_code& ec, 590 const std::string& introspectXml) { 591 if (ec) 592 { 593 BMCWEB_LOG_ERROR("Introspect call failed with error: {}", 594 ec.message()); 595 messages::internalError(asyncResp->res); 596 return; 597 } 598 599 // Check if the created dump object has implemented Progress 600 // interface to track dump completion. If yes, fetch the "Status" 601 // property of the interface, modify the task state accordingly. 602 // Else, return task completed. 603 tinyxml2::XMLDocument doc; 604 605 doc.Parse(introspectXml.data(), introspectXml.size()); 606 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 607 if (pRoot == nullptr) 608 { 609 BMCWEB_LOG_ERROR("XML document failed to parse"); 610 messages::internalError(asyncResp->res); 611 return; 612 } 613 tinyxml2::XMLElement* interfaceNode = 614 pRoot->FirstChildElement("interface"); 615 616 bool isProgressIntfPresent = false; 617 while (interfaceNode != nullptr) 618 { 619 const char* thisInterfaceName = 620 interfaceNode->Attribute("name"); 621 if (thisInterfaceName != nullptr) 622 { 623 if (thisInterfaceName == 624 std::string_view("xyz.openbmc_project.Common.Progress")) 625 { 626 interfaceNode = 627 interfaceNode->NextSiblingElement("interface"); 628 continue; 629 } 630 isProgressIntfPresent = true; 631 break; 632 } 633 interfaceNode = interfaceNode->NextSiblingElement("interface"); 634 } 635 636 std::shared_ptr<task::TaskData> task = task::TaskData::createTask( 637 [createdObjPath, dumpEntryPath, dumpId, isProgressIntfPresent]( 638 const boost::system::error_code& ec2, 639 sdbusplus::message_t& msg, 640 const std::shared_ptr<task::TaskData>& taskData) { 641 if (ec2) 642 { 643 BMCWEB_LOG_ERROR("{}: Error in creating dump", 644 createdObjPath.str); 645 taskData->messages.emplace_back( 646 messages::internalError()); 647 taskData->state = "Cancelled"; 648 return task::completed; 649 } 650 651 if (isProgressIntfPresent) 652 { 653 dbus::utility::DBusPropertiesMap values; 654 std::string prop; 655 msg.read(prop, values); 656 657 DumpCreationProgress dumpStatus = 658 getDumpCompletionStatus(values); 659 if (dumpStatus == 660 DumpCreationProgress::DUMP_CREATE_FAILED) 661 { 662 BMCWEB_LOG_ERROR("{}: Error in creating dump", 663 createdObjPath.str); 664 taskData->state = "Cancelled"; 665 return task::completed; 666 } 667 668 if (dumpStatus == 669 DumpCreationProgress::DUMP_CREATE_INPROGRESS) 670 { 671 BMCWEB_LOG_DEBUG( 672 "{}: Dump creation task is in progress", 673 createdObjPath.str); 674 return !task::completed; 675 } 676 } 677 678 nlohmann::json retMessage = messages::success(); 679 taskData->messages.emplace_back(retMessage); 680 681 boost::urls::url url = boost::urls::format( 682 "/redfish/v1/Managers/{}/LogServices/Dump/Entries/{}", 683 BMCWEB_REDFISH_MANAGER_URI_NAME, dumpId); 684 685 std::string headerLoc = "Location: "; 686 headerLoc += url.buffer(); 687 688 taskData->payload->httpHeaders.emplace_back( 689 std::move(headerLoc)); 690 691 BMCWEB_LOG_DEBUG("{}: Dump creation task completed", 692 createdObjPath.str); 693 taskData->state = "Completed"; 694 return task::completed; 695 }, 696 "type='signal',interface='org.freedesktop.DBus.Properties'," 697 "member='PropertiesChanged',path='" + 698 createdObjPath.str + "'"); 699 700 // The task timer is set to max time limit within which the 701 // requested dump will be collected. 702 task->startTimer(std::chrono::minutes(6)); 703 task->payload.emplace(payload); 704 task->populateResp(asyncResp->res); 705 }, 706 "xyz.openbmc_project.Dump.Manager", createdObjPath, 707 "org.freedesktop.DBus.Introspectable", "Introspect"); 708 } 709 710 inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 711 const crow::Request& req, const std::string& dumpType) 712 { 713 std::string dumpPath = getDumpEntriesPath(dumpType); 714 if (dumpPath.empty()) 715 { 716 messages::internalError(asyncResp->res); 717 return; 718 } 719 720 std::optional<std::string> diagnosticDataType; 721 std::optional<std::string> oemDiagnosticDataType; 722 723 if (!redfish::json_util::readJsonAction( // 724 req, asyncResp->res, // 725 "DiagnosticDataType", diagnosticDataType, // 726 "OEMDiagnosticDataType", oemDiagnosticDataType // 727 )) 728 { 729 return; 730 } 731 732 if (dumpType == "System") 733 { 734 if (!oemDiagnosticDataType || !diagnosticDataType) 735 { 736 BMCWEB_LOG_ERROR( 737 "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!"); 738 messages::actionParameterMissing( 739 asyncResp->res, "CollectDiagnosticData", 740 "DiagnosticDataType & OEMDiagnosticDataType"); 741 return; 742 } 743 if ((*oemDiagnosticDataType != "System") || 744 (*diagnosticDataType != "OEM")) 745 { 746 BMCWEB_LOG_ERROR("Wrong parameter values passed"); 747 messages::internalError(asyncResp->res); 748 return; 749 } 750 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump/", 751 BMCWEB_REDFISH_SYSTEM_URI_NAME); 752 } 753 else if (dumpType == "BMC") 754 { 755 if (!diagnosticDataType) 756 { 757 BMCWEB_LOG_ERROR( 758 "CreateDump action parameter 'DiagnosticDataType' not found!"); 759 messages::actionParameterMissing( 760 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType"); 761 return; 762 } 763 if (*diagnosticDataType != "Manager") 764 { 765 BMCWEB_LOG_ERROR( 766 "Wrong parameter value passed for 'DiagnosticDataType'"); 767 messages::internalError(asyncResp->res); 768 return; 769 } 770 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump/", 771 BMCWEB_REDFISH_MANAGER_URI_NAME); 772 } 773 else 774 { 775 BMCWEB_LOG_ERROR("CreateDump failed. Unknown dump type"); 776 messages::internalError(asyncResp->res); 777 return; 778 } 779 780 std::vector<std::pair<std::string, std::variant<std::string, uint64_t>>> 781 createDumpParamVec; 782 783 if (req.session != nullptr) 784 { 785 createDumpParamVec.emplace_back( 786 "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorId", 787 req.session->clientIp); 788 createDumpParamVec.emplace_back( 789 "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorType", 790 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client"); 791 } 792 793 dbus::utility::async_method_call( 794 asyncResp, 795 [asyncResp, payload(task::Payload(req)), 796 dumpPath](const boost::system::error_code& ec, 797 const sdbusplus::message_t& msg, 798 const sdbusplus::message::object_path& objPath) mutable { 799 if (ec) 800 { 801 BMCWEB_LOG_ERROR("CreateDump resp_handler got error {}", ec); 802 const sd_bus_error* dbusError = msg.get_error(); 803 if (dbusError == nullptr) 804 { 805 messages::internalError(asyncResp->res); 806 return; 807 } 808 809 BMCWEB_LOG_ERROR("CreateDump DBus error: {} and error msg: {}", 810 dbusError->name, dbusError->message); 811 if (std::string_view( 812 "xyz.openbmc_project.Common.Error.NotAllowed") == 813 dbusError->name) 814 { 815 messages::resourceInStandby(asyncResp->res); 816 return; 817 } 818 if (std::string_view( 819 "xyz.openbmc_project.Dump.Create.Error.Disabled") == 820 dbusError->name) 821 { 822 messages::serviceDisabled(asyncResp->res, dumpPath); 823 return; 824 } 825 if (std::string_view( 826 "xyz.openbmc_project.Common.Error.Unavailable") == 827 dbusError->name) 828 { 829 messages::resourceInUse(asyncResp->res); 830 return; 831 } 832 // Other Dbus errors such as: 833 // xyz.openbmc_project.Common.Error.InvalidArgument & 834 // org.freedesktop.DBus.Error.InvalidArgs are all related to 835 // the dbus call that is made here in the bmcweb 836 // implementation and has nothing to do with the client's 837 // input in the request. Hence, returning internal error 838 // back to the client. 839 messages::internalError(asyncResp->res); 840 return; 841 } 842 BMCWEB_LOG_DEBUG("Dump Created. Path: {}", objPath.str); 843 createDumpTaskCallback(std::move(payload), asyncResp, objPath); 844 }, 845 "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType), 846 "xyz.openbmc_project.Dump.Create", "CreateDump", createDumpParamVec); 847 } 848 849 inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 850 const std::string& dumpType) 851 { 852 dbus::utility::async_method_call( 853 asyncResp, 854 [asyncResp](const boost::system::error_code& ec) { 855 if (ec) 856 { 857 BMCWEB_LOG_ERROR("clearDump resp_handler got error {}", ec); 858 messages::internalError(asyncResp->res); 859 return; 860 } 861 messages::success(asyncResp->res); 862 }, 863 "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType), 864 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 865 } 866 867 inline void parseCrashdumpParameters( 868 const dbus::utility::DBusPropertiesMap& params, std::string& filename, 869 std::string& timestamp, std::string& logfile) 870 { 871 const std::string* filenamePtr = nullptr; 872 const std::string* timestampPtr = nullptr; 873 const std::string* logfilePtr = nullptr; 874 875 const bool success = sdbusplus::unpackPropertiesNoThrow( 876 dbus_utils::UnpackErrorPrinter(), params, "Timestamp", timestampPtr, 877 "Filename", filenamePtr, "Log", logfilePtr); 878 879 if (!success) 880 { 881 return; 882 } 883 884 if (filenamePtr != nullptr) 885 { 886 filename = *filenamePtr; 887 } 888 889 if (timestampPtr != nullptr) 890 { 891 timestamp = *timestampPtr; 892 } 893 894 if (logfilePtr != nullptr) 895 { 896 logfile = *logfilePtr; 897 } 898 } 899 900 inline void requestRoutesSystemLogServiceCollection(App& app) 901 { 902 /** 903 * Functions triggers appropriate requests on DBus 904 */ 905 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/") 906 .privileges(redfish::privileges::getLogServiceCollection) 907 .methods( 908 boost::beast::http::verb:: 909 get)([&app](const crow::Request& req, 910 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 911 const std::string& systemName) { 912 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 913 { 914 return; 915 } 916 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 917 { 918 // Option currently returns no systems. TBD 919 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 920 systemName); 921 return; 922 } 923 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 924 { 925 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 926 systemName); 927 return; 928 } 929 930 // Collections don't include the static data added by SubRoute 931 // because it has a duplicate entry for members 932 asyncResp->res.jsonValue["@odata.type"] = 933 "#LogServiceCollection.LogServiceCollection"; 934 asyncResp->res.jsonValue["@odata.id"] = 935 std::format("/redfish/v1/Systems/{}/LogServices", 936 BMCWEB_REDFISH_SYSTEM_URI_NAME); 937 asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 938 asyncResp->res.jsonValue["Description"] = 939 "Collection of LogServices for this Computer System"; 940 nlohmann::json& logServiceArray = 941 asyncResp->res.jsonValue["Members"]; 942 logServiceArray = nlohmann::json::array(); 943 nlohmann::json::object_t eventLog; 944 eventLog["@odata.id"] = 945 std::format("/redfish/v1/Systems/{}/LogServices/EventLog", 946 BMCWEB_REDFISH_SYSTEM_URI_NAME); 947 logServiceArray.emplace_back(std::move(eventLog)); 948 if constexpr (BMCWEB_REDFISH_DUMP_LOG) 949 { 950 nlohmann::json::object_t dumpLog; 951 dumpLog["@odata.id"] = 952 std::format("/redfish/v1/Systems/{}/LogServices/Dump", 953 BMCWEB_REDFISH_SYSTEM_URI_NAME); 954 logServiceArray.emplace_back(std::move(dumpLog)); 955 } 956 957 if constexpr (BMCWEB_REDFISH_CPU_LOG) 958 { 959 nlohmann::json::object_t crashdump; 960 crashdump["@odata.id"] = 961 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", 962 BMCWEB_REDFISH_SYSTEM_URI_NAME); 963 logServiceArray.emplace_back(std::move(crashdump)); 964 } 965 966 if constexpr (BMCWEB_REDFISH_HOST_LOGGER) 967 { 968 nlohmann::json::object_t hostlogger; 969 hostlogger["@odata.id"] = 970 std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", 971 BMCWEB_REDFISH_SYSTEM_URI_NAME); 972 logServiceArray.emplace_back(std::move(hostlogger)); 973 } 974 asyncResp->res.jsonValue["Members@odata.count"] = 975 logServiceArray.size(); 976 977 constexpr std::array<std::string_view, 1> interfaces = { 978 "xyz.openbmc_project.State.Boot.PostCode"}; 979 dbus::utility::getSubTreePaths( 980 "/", 0, interfaces, 981 [asyncResp](const boost::system::error_code& ec, 982 const dbus::utility::MapperGetSubTreePathsResponse& 983 subtreePath) { 984 if (ec) 985 { 986 BMCWEB_LOG_ERROR("{}", ec); 987 return; 988 } 989 990 for (const auto& pathStr : subtreePath) 991 { 992 if (pathStr.find("PostCode") != std::string::npos) 993 { 994 nlohmann::json& logServiceArrayLocal = 995 asyncResp->res.jsonValue["Members"]; 996 nlohmann::json::object_t member; 997 member["@odata.id"] = std::format( 998 "/redfish/v1/Systems/{}/LogServices/PostCodes", 999 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1000 1001 logServiceArrayLocal.emplace_back( 1002 std::move(member)); 1003 1004 asyncResp->res.jsonValue["Members@odata.count"] = 1005 logServiceArrayLocal.size(); 1006 return; 1007 } 1008 } 1009 }); 1010 }); 1011 } 1012 1013 inline void requestRoutesEventLogService(App& app) 1014 { 1015 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/") 1016 .privileges(redfish::privileges::getLogService) 1017 .methods( 1018 boost::beast::http::verb:: 1019 get)([&app](const crow::Request& req, 1020 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1021 const std::string& systemName) { 1022 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1023 { 1024 return; 1025 } 1026 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1027 { 1028 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1029 systemName); 1030 return; 1031 } 1032 asyncResp->res.jsonValue["@odata.id"] = 1033 std::format("/redfish/v1/Systems/{}/LogServices/EventLog", 1034 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1035 asyncResp->res.jsonValue["@odata.type"] = 1036 "#LogService.v1_2_0.LogService"; 1037 asyncResp->res.jsonValue["Name"] = "Event Log Service"; 1038 asyncResp->res.jsonValue["Description"] = 1039 "System Event Log Service"; 1040 asyncResp->res.jsonValue["Id"] = "EventLog"; 1041 asyncResp->res.jsonValue["OverWritePolicy"] = 1042 log_service::OverWritePolicy::WrapsWhenFull; 1043 1044 std::pair<std::string, std::string> redfishDateTimeOffset = 1045 redfish::time_utils::getDateTimeOffsetNow(); 1046 1047 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 1048 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 1049 redfishDateTimeOffset.second; 1050 1051 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( 1052 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries", 1053 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1054 asyncResp->res 1055 .jsonValue["Actions"]["#LogService.ClearLog"]["target"] 1056 1057 = std::format( 1058 "/redfish/v1/Systems/{}/LogServices/EventLog/Actions/LogService.ClearLog", 1059 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1060 1061 etag_utils::setEtagOmitDateTimeHandler(asyncResp); 1062 }); 1063 } 1064 1065 inline void handleBMCLogServicesCollectionGet( 1066 crow::App& app, const crow::Request& req, 1067 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1068 const std::string& managerId) 1069 { 1070 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1071 { 1072 return; 1073 } 1074 1075 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1076 { 1077 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1078 return; 1079 } 1080 1081 // Collections don't include the static data added by SubRoute 1082 // because it has a duplicate entry for members 1083 asyncResp->res.jsonValue["@odata.type"] = 1084 "#LogServiceCollection.LogServiceCollection"; 1085 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1086 "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME); 1087 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 1088 asyncResp->res.jsonValue["Description"] = 1089 "Collection of LogServices for this Manager"; 1090 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"]; 1091 logServiceArray = nlohmann::json::array(); 1092 1093 if constexpr (BMCWEB_REDFISH_BMC_JOURNAL) 1094 { 1095 nlohmann::json::object_t journal; 1096 journal["@odata.id"] = 1097 boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal", 1098 BMCWEB_REDFISH_MANAGER_URI_NAME); 1099 logServiceArray.emplace_back(std::move(journal)); 1100 } 1101 1102 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size(); 1103 1104 if constexpr (BMCWEB_REDFISH_DUMP_LOG) 1105 { 1106 constexpr std::array<std::string_view, 1> interfaces = { 1107 "xyz.openbmc_project.Collection.DeleteAll"}; 1108 dbus::utility::getSubTreePaths( 1109 "/xyz/openbmc_project/dump", 0, interfaces, 1110 [asyncResp](const boost::system::error_code& ec, 1111 const dbus::utility::MapperGetSubTreePathsResponse& 1112 subTreePaths) { 1113 if (ec) 1114 { 1115 BMCWEB_LOG_ERROR( 1116 "handleBMCLogServicesCollectionGet respHandler got error {}", 1117 ec); 1118 // Assume that getting an error simply means there are no 1119 // dump LogServices. Return without adding any error 1120 // response. 1121 return; 1122 } 1123 1124 nlohmann::json& logServiceArrayLocal = 1125 asyncResp->res.jsonValue["Members"]; 1126 1127 for (const std::string& path : subTreePaths) 1128 { 1129 if (path == "/xyz/openbmc_project/dump/bmc") 1130 { 1131 nlohmann::json::object_t member; 1132 member["@odata.id"] = boost::urls::format( 1133 "/redfish/v1/Managers/{}/LogServices/Dump", 1134 BMCWEB_REDFISH_MANAGER_URI_NAME); 1135 logServiceArrayLocal.emplace_back(std::move(member)); 1136 } 1137 else if (path == "/xyz/openbmc_project/dump/faultlog") 1138 { 1139 nlohmann::json::object_t member; 1140 member["@odata.id"] = boost::urls::format( 1141 "/redfish/v1/Managers/{}/LogServices/FaultLog", 1142 BMCWEB_REDFISH_MANAGER_URI_NAME); 1143 logServiceArrayLocal.emplace_back(std::move(member)); 1144 } 1145 } 1146 1147 asyncResp->res.jsonValue["Members@odata.count"] = 1148 logServiceArrayLocal.size(); 1149 }); 1150 } 1151 } 1152 1153 inline void requestRoutesBMCLogServiceCollection(App& app) 1154 { 1155 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/") 1156 .privileges(redfish::privileges::getLogServiceCollection) 1157 .methods(boost::beast::http::verb::get)( 1158 std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app))); 1159 } 1160 1161 inline void getDumpServiceInfo( 1162 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1163 const std::string& dumpType) 1164 { 1165 std::string dumpPath; 1166 log_service::OverWritePolicy overWritePolicy = 1167 log_service::OverWritePolicy::Invalid; 1168 bool collectDiagnosticDataSupported = false; 1169 1170 if (dumpType == "BMC") 1171 { 1172 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump", 1173 BMCWEB_REDFISH_MANAGER_URI_NAME); 1174 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; 1175 collectDiagnosticDataSupported = true; 1176 } 1177 else if (dumpType == "FaultLog") 1178 { 1179 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog", 1180 BMCWEB_REDFISH_MANAGER_URI_NAME); 1181 overWritePolicy = log_service::OverWritePolicy::Unknown; 1182 collectDiagnosticDataSupported = false; 1183 } 1184 else if (dumpType == "System") 1185 { 1186 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump", 1187 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1188 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; 1189 collectDiagnosticDataSupported = true; 1190 } 1191 else 1192 { 1193 BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}", 1194 dumpType); 1195 messages::internalError(asyncResp->res); 1196 return; 1197 } 1198 1199 asyncResp->res.jsonValue["@odata.id"] = dumpPath; 1200 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 1201 asyncResp->res.jsonValue["Name"] = "Dump LogService"; 1202 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService"; 1203 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename(); 1204 asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy; 1205 1206 std::pair<std::string, std::string> redfishDateTimeOffset = 1207 redfish::time_utils::getDateTimeOffsetNow(); 1208 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 1209 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 1210 redfishDateTimeOffset.second; 1211 1212 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries"; 1213 1214 if (collectDiagnosticDataSupported) 1215 { 1216 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"] 1217 ["target"] = 1218 dumpPath + "/Actions/LogService.CollectDiagnosticData"; 1219 } 1220 1221 etag_utils::setEtagOmitDateTimeHandler(asyncResp); 1222 1223 constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface}; 1224 dbus::utility::getSubTreePaths( 1225 "/xyz/openbmc_project/dump", 0, interfaces, 1226 [asyncResp, dumpType, dumpPath]( 1227 const boost::system::error_code& ec, 1228 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) { 1229 if (ec) 1230 { 1231 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}", 1232 ec); 1233 // Assume that getting an error simply means there are no dump 1234 // LogServices. Return without adding any error response. 1235 return; 1236 } 1237 std::string dbusDumpPath = getDumpPath(dumpType); 1238 for (const std::string& path : subTreePaths) 1239 { 1240 if (path == dbusDumpPath) 1241 { 1242 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 1243 ["target"] = 1244 dumpPath + "/Actions/LogService.ClearLog"; 1245 break; 1246 } 1247 } 1248 }); 1249 } 1250 1251 inline void handleLogServicesDumpServiceGet( 1252 crow::App& app, const std::string& dumpType, const crow::Request& req, 1253 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1254 const std::string& managerId) 1255 { 1256 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1257 { 1258 return; 1259 } 1260 1261 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1262 { 1263 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1264 return; 1265 } 1266 1267 getDumpServiceInfo(asyncResp, dumpType); 1268 } 1269 1270 inline void handleLogServicesDumpServiceComputerSystemGet( 1271 crow::App& app, const crow::Request& req, 1272 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1273 const std::string& chassisId) 1274 { 1275 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1276 { 1277 return; 1278 } 1279 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1280 { 1281 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 1282 return; 1283 } 1284 getDumpServiceInfo(asyncResp, "System"); 1285 } 1286 1287 inline void handleLogServicesDumpEntriesCollectionGet( 1288 crow::App& app, const std::string& dumpType, const crow::Request& req, 1289 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1290 const std::string& managerId) 1291 { 1292 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1293 { 1294 return; 1295 } 1296 1297 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1298 { 1299 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1300 return; 1301 } 1302 getDumpEntryCollection(asyncResp, dumpType); 1303 } 1304 1305 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet( 1306 crow::App& app, const crow::Request& req, 1307 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1308 const std::string& chassisId) 1309 { 1310 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1311 { 1312 return; 1313 } 1314 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1315 { 1316 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 1317 return; 1318 } 1319 getDumpEntryCollection(asyncResp, "System"); 1320 } 1321 1322 inline void handleLogServicesDumpEntryGet( 1323 crow::App& app, const std::string& dumpType, const crow::Request& req, 1324 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1325 const std::string& managerId, const std::string& dumpId) 1326 { 1327 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1328 { 1329 return; 1330 } 1331 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1332 { 1333 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1334 return; 1335 } 1336 getDumpEntryById(asyncResp, dumpId, dumpType); 1337 } 1338 1339 inline void handleLogServicesDumpEntryComputerSystemGet( 1340 crow::App& app, const crow::Request& req, 1341 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1342 const std::string& chassisId, const std::string& dumpId) 1343 { 1344 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1345 { 1346 return; 1347 } 1348 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1349 { 1350 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 1351 return; 1352 } 1353 getDumpEntryById(asyncResp, dumpId, "System"); 1354 } 1355 1356 inline void handleLogServicesDumpEntryDelete( 1357 crow::App& app, const std::string& dumpType, const crow::Request& req, 1358 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1359 const std::string& managerId, const std::string& dumpId) 1360 { 1361 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1362 { 1363 return; 1364 } 1365 1366 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1367 { 1368 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1369 return; 1370 } 1371 deleteDumpEntry(asyncResp, dumpId, dumpType); 1372 } 1373 1374 inline void handleLogServicesDumpEntryComputerSystemDelete( 1375 crow::App& app, const crow::Request& req, 1376 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1377 const std::string& chassisId, const std::string& dumpId) 1378 { 1379 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1380 { 1381 return; 1382 } 1383 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1384 { 1385 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 1386 return; 1387 } 1388 deleteDumpEntry(asyncResp, dumpId, "System"); 1389 } 1390 1391 inline void handleLogServicesDumpEntryDownloadGet( 1392 crow::App& app, const std::string& dumpType, const crow::Request& req, 1393 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1394 const std::string& managerId, const std::string& dumpId) 1395 { 1396 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1397 { 1398 return; 1399 } 1400 1401 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1402 { 1403 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1404 return; 1405 } 1406 downloadDumpEntry(asyncResp, dumpId, dumpType); 1407 } 1408 1409 inline void handleLogServicesDumpCollectDiagnosticDataPost( 1410 crow::App& app, const std::string& dumpType, const crow::Request& req, 1411 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1412 const std::string& managerId) 1413 { 1414 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1415 { 1416 return; 1417 } 1418 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1419 { 1420 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1421 return; 1422 } 1423 1424 createDump(asyncResp, req, dumpType); 1425 } 1426 1427 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost( 1428 crow::App& app, const crow::Request& req, 1429 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1430 const std::string& systemName) 1431 { 1432 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1433 { 1434 return; 1435 } 1436 1437 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1438 { 1439 // Option currently returns no systems. TBD 1440 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1441 systemName); 1442 return; 1443 } 1444 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1445 { 1446 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1447 systemName); 1448 return; 1449 } 1450 createDump(asyncResp, req, "System"); 1451 } 1452 1453 inline void handleLogServicesDumpClearLogPost( 1454 crow::App& app, const std::string& dumpType, const crow::Request& req, 1455 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1456 const std::string& managerId) 1457 { 1458 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1459 { 1460 return; 1461 } 1462 1463 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1464 { 1465 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1466 return; 1467 } 1468 clearDump(asyncResp, dumpType); 1469 } 1470 1471 inline void handleLogServicesDumpClearLogComputerSystemPost( 1472 crow::App& app, const crow::Request& req, 1473 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1474 const std::string& systemName) 1475 { 1476 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1477 { 1478 return; 1479 } 1480 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1481 { 1482 // Option currently returns no systems. TBD 1483 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1484 systemName); 1485 return; 1486 } 1487 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1488 { 1489 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1490 systemName); 1491 return; 1492 } 1493 clearDump(asyncResp, "System"); 1494 } 1495 1496 inline void requestRoutesBMCDumpService(App& app) 1497 { 1498 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/") 1499 .privileges(redfish::privileges::getLogService) 1500 .methods(boost::beast::http::verb::get)(std::bind_front( 1501 handleLogServicesDumpServiceGet, std::ref(app), "BMC")); 1502 } 1503 1504 inline void requestRoutesBMCDumpEntryCollection(App& app) 1505 { 1506 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/") 1507 .privileges(redfish::privileges::getLogEntryCollection) 1508 .methods(boost::beast::http::verb::get)(std::bind_front( 1509 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC")); 1510 } 1511 1512 inline void requestRoutesBMCDumpEntry(App& app) 1513 { 1514 BMCWEB_ROUTE(app, 1515 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/") 1516 .privileges(redfish::privileges::getLogEntry) 1517 .methods(boost::beast::http::verb::get)(std::bind_front( 1518 handleLogServicesDumpEntryGet, std::ref(app), "BMC")); 1519 1520 BMCWEB_ROUTE(app, 1521 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/") 1522 .privileges(redfish::privileges::deleteLogEntry) 1523 .methods(boost::beast::http::verb::delete_)(std::bind_front( 1524 handleLogServicesDumpEntryDelete, std::ref(app), "BMC")); 1525 } 1526 1527 inline void requestRoutesBMCDumpEntryDownload(App& app) 1528 { 1529 BMCWEB_ROUTE( 1530 app, 1531 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/") 1532 .privileges(redfish::privileges::getLogEntry) 1533 .methods(boost::beast::http::verb::get)(std::bind_front( 1534 handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC")); 1535 } 1536 1537 inline void requestRoutesBMCDumpCreate(App& app) 1538 { 1539 BMCWEB_ROUTE( 1540 app, 1541 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/") 1542 .privileges(redfish::privileges::postLogService) 1543 .methods(boost::beast::http::verb::post)( 1544 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost, 1545 std::ref(app), "BMC")); 1546 } 1547 1548 inline void requestRoutesBMCDumpClear(App& app) 1549 { 1550 BMCWEB_ROUTE( 1551 app, 1552 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/") 1553 .privileges(redfish::privileges::postLogService) 1554 .methods(boost::beast::http::verb::post)(std::bind_front( 1555 handleLogServicesDumpClearLogPost, std::ref(app), "BMC")); 1556 } 1557 1558 inline void requestRoutesFaultLogDumpService(App& app) 1559 { 1560 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/") 1561 .privileges(redfish::privileges::getLogService) 1562 .methods(boost::beast::http::verb::get)(std::bind_front( 1563 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog")); 1564 } 1565 1566 inline void requestRoutesFaultLogDumpEntryCollection(App& app) 1567 { 1568 BMCWEB_ROUTE(app, 1569 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/") 1570 .privileges(redfish::privileges::getLogEntryCollection) 1571 .methods(boost::beast::http::verb::get)( 1572 std::bind_front(handleLogServicesDumpEntriesCollectionGet, 1573 std::ref(app), "FaultLog")); 1574 } 1575 1576 inline void requestRoutesFaultLogDumpEntry(App& app) 1577 { 1578 BMCWEB_ROUTE( 1579 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/") 1580 .privileges(redfish::privileges::getLogEntry) 1581 .methods(boost::beast::http::verb::get)(std::bind_front( 1582 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog")); 1583 1584 BMCWEB_ROUTE( 1585 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/") 1586 .privileges(redfish::privileges::deleteLogEntry) 1587 .methods(boost::beast::http::verb::delete_)(std::bind_front( 1588 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog")); 1589 } 1590 1591 inline void requestRoutesFaultLogDumpClear(App& app) 1592 { 1593 BMCWEB_ROUTE( 1594 app, 1595 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/") 1596 .privileges(redfish::privileges::postLogService) 1597 .methods(boost::beast::http::verb::post)(std::bind_front( 1598 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog")); 1599 } 1600 1601 inline void requestRoutesSystemDumpService(App& app) 1602 { 1603 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/") 1604 .privileges(redfish::privileges::getLogService) 1605 .methods(boost::beast::http::verb::get)(std::bind_front( 1606 handleLogServicesDumpServiceComputerSystemGet, std::ref(app))); 1607 } 1608 1609 inline void requestRoutesSystemDumpEntryCollection(App& app) 1610 { 1611 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/") 1612 .privileges(redfish::privileges::getLogEntryCollection) 1613 .methods(boost::beast::http::verb::get)(std::bind_front( 1614 handleLogServicesDumpEntriesCollectionComputerSystemGet, 1615 std::ref(app))); 1616 } 1617 1618 inline void requestRoutesSystemDumpEntry(App& app) 1619 { 1620 BMCWEB_ROUTE(app, 1621 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/") 1622 .privileges(redfish::privileges::getLogEntry) 1623 .methods(boost::beast::http::verb::get)(std::bind_front( 1624 handleLogServicesDumpEntryComputerSystemGet, std::ref(app))); 1625 1626 BMCWEB_ROUTE(app, 1627 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/") 1628 .privileges(redfish::privileges::deleteLogEntry) 1629 .methods(boost::beast::http::verb::delete_)(std::bind_front( 1630 handleLogServicesDumpEntryComputerSystemDelete, std::ref(app))); 1631 } 1632 1633 inline void requestRoutesSystemDumpCreate(App& app) 1634 { 1635 BMCWEB_ROUTE( 1636 app, 1637 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/") 1638 .privileges(redfish::privileges:: 1639 postLogServiceSubOverComputerSystemLogServiceCollection) 1640 .methods(boost::beast::http::verb::post)(std::bind_front( 1641 handleLogServicesDumpCollectDiagnosticDataComputerSystemPost, 1642 std::ref(app))); 1643 } 1644 1645 inline void requestRoutesSystemDumpClear(App& app) 1646 { 1647 BMCWEB_ROUTE( 1648 app, 1649 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/") 1650 .privileges(redfish::privileges:: 1651 postLogServiceSubOverComputerSystemLogServiceCollection) 1652 .methods(boost::beast::http::verb::post)(std::bind_front( 1653 handleLogServicesDumpClearLogComputerSystemPost, std::ref(app))); 1654 } 1655 1656 inline void requestRoutesCrashdumpService(App& app) 1657 { 1658 /** 1659 * Functions triggers appropriate requests on DBus 1660 */ 1661 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/") 1662 .privileges(redfish::privileges::getLogService) 1663 .methods( 1664 boost::beast::http::verb:: 1665 get)([&app](const crow::Request& req, 1666 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1667 const std::string& systemName) { 1668 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1669 { 1670 return; 1671 } 1672 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1673 { 1674 // Option currently returns no systems. TBD 1675 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1676 systemName); 1677 return; 1678 } 1679 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1680 { 1681 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1682 systemName); 1683 return; 1684 } 1685 1686 // Copy over the static data to include the entries added by 1687 // SubRoute 1688 asyncResp->res.jsonValue["@odata.id"] = 1689 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", 1690 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1691 asyncResp->res.jsonValue["@odata.type"] = 1692 "#LogService.v1_2_0.LogService"; 1693 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service"; 1694 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service"; 1695 asyncResp->res.jsonValue["Id"] = "Crashdump"; 1696 asyncResp->res.jsonValue["OverWritePolicy"] = 1697 log_service::OverWritePolicy::WrapsWhenFull; 1698 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 1699 1700 std::pair<std::string, std::string> redfishDateTimeOffset = 1701 redfish::time_utils::getDateTimeOffsetNow(); 1702 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 1703 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 1704 redfishDateTimeOffset.second; 1705 1706 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( 1707 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", 1708 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1709 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 1710 ["target"] = std::format( 1711 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog", 1712 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1713 asyncResp->res 1714 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"] 1715 ["target"] = std::format( 1716 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData", 1717 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1718 1719 etag_utils::setEtagOmitDateTimeHandler(asyncResp); 1720 }); 1721 } 1722 1723 inline void requestRoutesCrashdumpClear(App& app) 1724 { 1725 BMCWEB_ROUTE( 1726 app, 1727 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/") 1728 .privileges(redfish::privileges:: 1729 postLogServiceSubOverComputerSystemLogServiceCollection) 1730 .methods(boost::beast::http::verb::post)( 1731 [&app](const crow::Request& req, 1732 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1733 const std::string& systemName) { 1734 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1735 { 1736 return; 1737 } 1738 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1739 { 1740 // Option currently returns no systems. TBD 1741 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1742 systemName); 1743 return; 1744 } 1745 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1746 { 1747 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1748 systemName); 1749 return; 1750 } 1751 dbus::utility::async_method_call( 1752 asyncResp, 1753 [asyncResp](const boost::system::error_code& ec, 1754 const std::string&) { 1755 if (ec) 1756 { 1757 messages::internalError(asyncResp->res); 1758 return; 1759 } 1760 messages::success(asyncResp->res); 1761 }, 1762 crashdumpObject, crashdumpPath, deleteAllInterface, 1763 "DeleteAll"); 1764 }); 1765 } 1766 1767 inline void logCrashdumpEntry( 1768 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1769 const std::string& logID, nlohmann::json& logEntryJson) 1770 { 1771 auto getStoredLogCallback = 1772 [asyncResp, logID, 1773 &logEntryJson](const boost::system::error_code& ec, 1774 const dbus::utility::DBusPropertiesMap& params) { 1775 if (ec) 1776 { 1777 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message()); 1778 if (ec.value() == 1779 boost::system::linux_error::bad_request_descriptor) 1780 { 1781 messages::resourceNotFound(asyncResp->res, "LogEntry", 1782 logID); 1783 } 1784 else 1785 { 1786 messages::internalError(asyncResp->res); 1787 } 1788 return; 1789 } 1790 1791 std::string timestamp{}; 1792 std::string filename{}; 1793 std::string logfile{}; 1794 parseCrashdumpParameters(params, filename, timestamp, logfile); 1795 1796 if (filename.empty() || timestamp.empty()) 1797 { 1798 messages::resourceNotFound(asyncResp->res, "LogEntry", logID); 1799 return; 1800 } 1801 1802 std::string crashdumpURI = 1803 std::format( 1804 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/", 1805 BMCWEB_REDFISH_SYSTEM_URI_NAME) + 1806 logID + "/" + filename; 1807 nlohmann::json::object_t logEntry; 1808 logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 1809 logEntry["@odata.id"] = boost::urls::format( 1810 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}", 1811 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID); 1812 logEntry["Name"] = "CPU Crashdump"; 1813 logEntry["Id"] = logID; 1814 logEntry["EntryType"] = log_entry::LogEntryType::Oem; 1815 logEntry["AdditionalDataURI"] = std::move(crashdumpURI); 1816 logEntry["DiagnosticDataType"] = "OEM"; 1817 logEntry["OEMDiagnosticDataType"] = "PECICrashdump"; 1818 logEntry["Created"] = std::move(timestamp); 1819 1820 // If logEntryJson references an array of LogEntry resources 1821 // ('Members' list), then push this as a new entry, otherwise set it 1822 // directly 1823 if (logEntryJson.is_array()) 1824 { 1825 logEntryJson.push_back(logEntry); 1826 asyncResp->res.jsonValue["Members@odata.count"] = 1827 logEntryJson.size(); 1828 } 1829 else 1830 { 1831 logEntryJson.update(logEntry); 1832 } 1833 }; 1834 dbus::utility::getAllProperties( 1835 crashdumpObject, crashdumpPath + std::string("/") + logID, 1836 crashdumpInterface, std::move(getStoredLogCallback)); 1837 } 1838 1839 inline void requestRoutesCrashdumpEntryCollection(App& app) 1840 { 1841 /** 1842 * Functions triggers appropriate requests on DBus 1843 */ 1844 BMCWEB_ROUTE(app, 1845 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/") 1846 .privileges(redfish::privileges::getLogEntryCollection) 1847 .methods( 1848 boost::beast::http::verb:: 1849 get)([&app](const crow::Request& req, 1850 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1851 const std::string& systemName) { 1852 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1853 { 1854 return; 1855 } 1856 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1857 { 1858 // Option currently returns no systems. TBD 1859 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1860 systemName); 1861 return; 1862 } 1863 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1864 { 1865 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1866 systemName); 1867 return; 1868 } 1869 1870 constexpr std::array<std::string_view, 1> interfaces = { 1871 crashdumpInterface}; 1872 dbus::utility::getSubTreePaths( 1873 "/", 0, interfaces, 1874 [asyncResp](const boost::system::error_code& ec, 1875 const std::vector<std::string>& resp) { 1876 if (ec) 1877 { 1878 if (ec.value() != 1879 boost::system::errc::no_such_file_or_directory) 1880 { 1881 BMCWEB_LOG_DEBUG("failed to get entries ec: {}", 1882 ec.message()); 1883 messages::internalError(asyncResp->res); 1884 return; 1885 } 1886 } 1887 asyncResp->res.jsonValue["@odata.type"] = 1888 "#LogEntryCollection.LogEntryCollection"; 1889 asyncResp->res.jsonValue["@odata.id"] = std::format( 1890 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", 1891 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1892 asyncResp->res.jsonValue["Name"] = 1893 "Open BMC Crashdump Entries"; 1894 asyncResp->res.jsonValue["Description"] = 1895 "Collection of Crashdump Entries"; 1896 asyncResp->res.jsonValue["Members"] = 1897 nlohmann::json::array(); 1898 asyncResp->res.jsonValue["Members@odata.count"] = 0; 1899 1900 for (const std::string& path : resp) 1901 { 1902 const sdbusplus::message::object_path objPath(path); 1903 // Get the log ID 1904 std::string logID = objPath.filename(); 1905 if (logID.empty()) 1906 { 1907 continue; 1908 } 1909 // Add the log entry to the array 1910 logCrashdumpEntry(asyncResp, logID, 1911 asyncResp->res.jsonValue["Members"]); 1912 } 1913 }); 1914 }); 1915 } 1916 1917 inline void requestRoutesCrashdumpEntry(App& app) 1918 { 1919 BMCWEB_ROUTE( 1920 app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/") 1921 .privileges(redfish::privileges::getLogEntry) 1922 .methods(boost::beast::http::verb::get)( 1923 [&app](const crow::Request& req, 1924 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1925 const std::string& systemName, const std::string& param) { 1926 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1927 { 1928 return; 1929 } 1930 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1931 { 1932 // Option currently returns no systems. TBD 1933 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1934 systemName); 1935 return; 1936 } 1937 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1938 { 1939 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1940 systemName); 1941 return; 1942 } 1943 const std::string& logID = param; 1944 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); 1945 }); 1946 } 1947 1948 inline void requestRoutesCrashdumpFile(App& app) 1949 { 1950 BMCWEB_ROUTE( 1951 app, 1952 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/") 1953 .privileges(redfish::privileges::getLogEntry) 1954 .methods(boost::beast::http::verb::get)( 1955 [](const crow::Request& req, 1956 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1957 const std::string& systemName, const std::string& logID, 1958 const std::string& fileName) { 1959 // Do not call getRedfishRoute here since the crashdump file is 1960 // not a Redfish resource. 1961 1962 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1963 { 1964 // Option currently returns no systems. TBD 1965 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1966 systemName); 1967 return; 1968 } 1969 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1970 { 1971 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1972 systemName); 1973 return; 1974 } 1975 1976 auto getStoredLogCallback = 1977 [asyncResp, logID, fileName, 1978 url(boost::urls::url(req.url()))]( 1979 const boost::system::error_code& ec, 1980 const std::vector<std::pair< 1981 std::string, dbus::utility::DbusVariantType>>& 1982 resp) { 1983 if (ec) 1984 { 1985 BMCWEB_LOG_DEBUG("failed to get log ec: {}", 1986 ec.message()); 1987 messages::internalError(asyncResp->res); 1988 return; 1989 } 1990 1991 std::string dbusFilename{}; 1992 std::string dbusTimestamp{}; 1993 std::string dbusFilepath{}; 1994 1995 parseCrashdumpParameters(resp, dbusFilename, 1996 dbusTimestamp, dbusFilepath); 1997 1998 if (dbusFilename.empty() || dbusTimestamp.empty() || 1999 dbusFilepath.empty()) 2000 { 2001 messages::resourceNotFound(asyncResp->res, 2002 "LogEntry", logID); 2003 return; 2004 } 2005 2006 // Verify the file name parameter is correct 2007 if (fileName != dbusFilename) 2008 { 2009 messages::resourceNotFound(asyncResp->res, 2010 "LogEntry", logID); 2011 return; 2012 } 2013 2014 if (asyncResp->res.openFile(dbusFilepath) != 2015 crow::OpenCode::Success) 2016 { 2017 messages::resourceNotFound(asyncResp->res, 2018 "LogEntry", logID); 2019 return; 2020 } 2021 2022 // Configure this to be a file download when accessed 2023 // from a browser 2024 asyncResp->res.addHeader( 2025 boost::beast::http::field::content_disposition, 2026 "attachment"); 2027 }; 2028 dbus::utility::getAllProperties( 2029 *crow::connections::systemBus, crashdumpObject, 2030 crashdumpPath + std::string("/") + logID, 2031 crashdumpInterface, std::move(getStoredLogCallback)); 2032 }); 2033 } 2034 2035 enum class OEMDiagnosticType 2036 { 2037 onDemand, 2038 telemetry, 2039 invalid, 2040 }; 2041 2042 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr) 2043 { 2044 if (oemDiagStr == "OnDemand") 2045 { 2046 return OEMDiagnosticType::onDemand; 2047 } 2048 if (oemDiagStr == "Telemetry") 2049 { 2050 return OEMDiagnosticType::telemetry; 2051 } 2052 2053 return OEMDiagnosticType::invalid; 2054 } 2055 2056 inline void requestRoutesCrashdumpCollect(App& app) 2057 { 2058 BMCWEB_ROUTE( 2059 app, 2060 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/") 2061 .privileges(redfish::privileges:: 2062 postLogServiceSubOverComputerSystemLogServiceCollection) 2063 .methods(boost::beast::http::verb::post)( 2064 [&app](const crow::Request& req, 2065 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2066 const std::string& systemName) { 2067 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2068 { 2069 return; 2070 } 2071 2072 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2073 { 2074 // Option currently returns no systems. TBD 2075 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2076 systemName); 2077 return; 2078 } 2079 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2080 { 2081 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2082 systemName); 2083 return; 2084 } 2085 2086 std::string diagnosticDataType; 2087 std::string oemDiagnosticDataType; 2088 if (!redfish::json_util::readJsonAction( // 2089 req, asyncResp->res, // 2090 "DiagnosticDataType", diagnosticDataType, // 2091 "OEMDiagnosticDataType", oemDiagnosticDataType // 2092 )) 2093 { 2094 return; 2095 } 2096 2097 if (diagnosticDataType != "OEM") 2098 { 2099 BMCWEB_LOG_ERROR( 2100 "Only OEM DiagnosticDataType supported for Crashdump"); 2101 messages::actionParameterValueFormatError( 2102 asyncResp->res, diagnosticDataType, 2103 "DiagnosticDataType", "CollectDiagnosticData"); 2104 return; 2105 } 2106 2107 OEMDiagnosticType oemDiagType = 2108 getOEMDiagnosticType(oemDiagnosticDataType); 2109 2110 std::string iface; 2111 std::string method; 2112 std::string taskMatchStr; 2113 if (oemDiagType == OEMDiagnosticType::onDemand) 2114 { 2115 iface = crashdumpOnDemandInterface; 2116 method = "GenerateOnDemandLog"; 2117 taskMatchStr = 2118 "type='signal'," 2119 "interface='org.freedesktop.DBus.Properties'," 2120 "member='PropertiesChanged'," 2121 "arg0namespace='com.intel.crashdump'"; 2122 } 2123 else if (oemDiagType == OEMDiagnosticType::telemetry) 2124 { 2125 iface = crashdumpTelemetryInterface; 2126 method = "GenerateTelemetryLog"; 2127 taskMatchStr = 2128 "type='signal'," 2129 "interface='org.freedesktop.DBus.Properties'," 2130 "member='PropertiesChanged'," 2131 "arg0namespace='com.intel.crashdump'"; 2132 } 2133 else 2134 { 2135 BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}", 2136 oemDiagnosticDataType); 2137 messages::actionParameterValueFormatError( 2138 asyncResp->res, oemDiagnosticDataType, 2139 "OEMDiagnosticDataType", "CollectDiagnosticData"); 2140 return; 2141 } 2142 2143 auto collectCrashdumpCallback = 2144 [asyncResp, payload(task::Payload(req)), 2145 taskMatchStr](const boost::system::error_code& ec, 2146 const std::string&) mutable { 2147 if (ec) 2148 { 2149 if (ec.value() == 2150 boost::system::errc::operation_not_supported) 2151 { 2152 messages::resourceInStandby(asyncResp->res); 2153 } 2154 else if (ec.value() == boost::system::errc:: 2155 device_or_resource_busy) 2156 { 2157 messages::serviceTemporarilyUnavailable( 2158 asyncResp->res, "60"); 2159 } 2160 else 2161 { 2162 messages::internalError(asyncResp->res); 2163 } 2164 return; 2165 } 2166 std::shared_ptr<task::TaskData> task = 2167 task::TaskData::createTask( 2168 [](const boost::system::error_code& ec2, 2169 sdbusplus::message_t&, 2170 const std::shared_ptr<task::TaskData>& 2171 taskData) { 2172 if (!ec2) 2173 { 2174 taskData->messages.emplace_back( 2175 messages::taskCompletedOK( 2176 std::to_string( 2177 taskData->index))); 2178 taskData->state = "Completed"; 2179 } 2180 return task::completed; 2181 }, 2182 taskMatchStr); 2183 2184 task->startTimer(std::chrono::minutes(5)); 2185 task->payload.emplace(std::move(payload)); 2186 task->populateResp(asyncResp->res); 2187 }; 2188 2189 dbus::utility::async_method_call( 2190 asyncResp, std::move(collectCrashdumpCallback), 2191 crashdumpObject, crashdumpPath, iface, method); 2192 }); 2193 } 2194 } // namespace redfish 2195