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