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