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