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->populateResp(asyncResp->res); 938 task->payload.emplace(payload); 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({{"ConfigureComponents"}}) 1348 .methods(boost::beast::http::verb::post)(std::bind_front( 1349 handleSystemsLogServicesEventLogActionsClearPost, std::ref(app))); 1350 } 1351 1352 enum class LogParseError 1353 { 1354 success, 1355 parseFailed, 1356 messageIdNotInRegistry, 1357 }; 1358 1359 static LogParseError fillEventLogEntryJson( 1360 const std::string& logEntryID, const std::string& logEntry, 1361 nlohmann::json::object_t& logEntryJson) 1362 { 1363 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 1364 // First get the Timestamp 1365 size_t space = logEntry.find_first_of(' '); 1366 if (space == std::string::npos) 1367 { 1368 return LogParseError::parseFailed; 1369 } 1370 std::string timestamp = logEntry.substr(0, space); 1371 // Then get the log contents 1372 size_t entryStart = logEntry.find_first_not_of(' ', space); 1373 if (entryStart == std::string::npos) 1374 { 1375 return LogParseError::parseFailed; 1376 } 1377 std::string_view entry(logEntry); 1378 entry.remove_prefix(entryStart); 1379 // Use split to separate the entry into its fields 1380 std::vector<std::string> logEntryFields; 1381 bmcweb::split(logEntryFields, entry, ','); 1382 // We need at least a MessageId to be valid 1383 auto logEntryIter = logEntryFields.begin(); 1384 if (logEntryIter == logEntryFields.end()) 1385 { 1386 return LogParseError::parseFailed; 1387 } 1388 std::string& messageID = *logEntryIter; 1389 // Get the Message from the MessageRegistry 1390 const registries::Message* message = registries::getMessage(messageID); 1391 1392 logEntryIter++; 1393 if (message == nullptr) 1394 { 1395 BMCWEB_LOG_WARNING("Log entry not found in registry: {}", logEntry); 1396 return LogParseError::messageIdNotInRegistry; 1397 } 1398 1399 std::vector<std::string_view> messageArgs(logEntryIter, 1400 logEntryFields.end()); 1401 messageArgs.resize(message->numberOfArgs); 1402 1403 std::string msg = 1404 redfish::registries::fillMessageArgs(messageArgs, message->message); 1405 if (msg.empty()) 1406 { 1407 return LogParseError::parseFailed; 1408 } 1409 1410 // Get the Created time from the timestamp. The log timestamp is in RFC3339 1411 // format which matches the Redfish format except for the fractional seconds 1412 // between the '.' and the '+', so just remove them. 1413 std::size_t dot = timestamp.find_first_of('.'); 1414 std::size_t plus = timestamp.find_first_of('+'); 1415 if (dot != std::string::npos && plus != std::string::npos) 1416 { 1417 timestamp.erase(dot, plus - dot); 1418 } 1419 1420 // Fill in the log entry with the gathered data 1421 logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 1422 logEntryJson["@odata.id"] = boost::urls::format( 1423 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}", 1424 BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID); 1425 logEntryJson["Name"] = "System Event Log Entry"; 1426 logEntryJson["Id"] = logEntryID; 1427 logEntryJson["Message"] = std::move(msg); 1428 logEntryJson["MessageId"] = std::move(messageID); 1429 logEntryJson["MessageArgs"] = messageArgs; 1430 logEntryJson["EntryType"] = "Event"; 1431 logEntryJson["Severity"] = message->messageSeverity; 1432 logEntryJson["Created"] = std::move(timestamp); 1433 return LogParseError::success; 1434 } 1435 1436 inline bool fillEventLogLogEntryFromPropertyMap( 1437 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1438 const dbus::utility::DBusPropertiesMap& resp, 1439 nlohmann::json& objectToFillOut) 1440 { 1441 std::optional<DbusEventLogEntry> optEntry = 1442 fillDbusEventLogEntryFromPropertyMap(resp); 1443 1444 if (!optEntry.has_value()) 1445 { 1446 messages::internalError(asyncResp->res); 1447 return false; 1448 } 1449 DbusEventLogEntry entry = optEntry.value(); 1450 1451 objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 1452 objectToFillOut["@odata.id"] = boost::urls::format( 1453 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}", 1454 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id)); 1455 objectToFillOut["Name"] = "System Event Log Entry"; 1456 objectToFillOut["Id"] = std::to_string(entry.Id); 1457 objectToFillOut["Message"] = entry.Message; 1458 objectToFillOut["Resolved"] = entry.Resolved; 1459 std::optional<bool> notifyAction = 1460 getProviderNotifyAction(entry.ServiceProviderNotify); 1461 if (notifyAction) 1462 { 1463 objectToFillOut["ServiceProviderNotified"] = *notifyAction; 1464 } 1465 if ((entry.Resolution != nullptr) && !entry.Resolution->empty()) 1466 { 1467 objectToFillOut["Resolution"] = *entry.Resolution; 1468 } 1469 objectToFillOut["EntryType"] = "Event"; 1470 objectToFillOut["Severity"] = 1471 translateSeverityDbusToRedfish(entry.Severity); 1472 objectToFillOut["Created"] = 1473 redfish::time_utils::getDateTimeUintMs(entry.Timestamp); 1474 objectToFillOut["Modified"] = 1475 redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp); 1476 if (entry.Path != nullptr) 1477 { 1478 objectToFillOut["AdditionalDataURI"] = boost::urls::format( 1479 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}/attachment", 1480 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id)); 1481 } 1482 return true; 1483 } 1484 1485 inline void afterLogEntriesGetManagedObjects( 1486 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1487 const boost::system::error_code& ec, 1488 const dbus::utility::ManagedObjectType& resp) 1489 { 1490 if (ec) 1491 { 1492 // TODO Handle for specific error code 1493 BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}", 1494 ec); 1495 messages::internalError(asyncResp->res); 1496 return; 1497 } 1498 nlohmann::json::array_t entriesArray; 1499 for (const auto& objectPath : resp) 1500 { 1501 dbus::utility::DBusPropertiesMap propsFlattened; 1502 auto isEntry = 1503 std::ranges::find_if(objectPath.second, [](const auto& object) { 1504 return object.first == "xyz.openbmc_project.Logging.Entry"; 1505 }); 1506 if (isEntry == objectPath.second.end()) 1507 { 1508 continue; 1509 } 1510 for (const auto& interfaceMap : objectPath.second) 1511 { 1512 for (const auto& propertyMap : interfaceMap.second) 1513 { 1514 propsFlattened.emplace_back(propertyMap.first, 1515 propertyMap.second); 1516 } 1517 } 1518 bool success = fillEventLogLogEntryFromPropertyMap( 1519 asyncResp, propsFlattened, entriesArray.emplace_back()); 1520 if (!success) 1521 { 1522 return; 1523 } 1524 } 1525 1526 redfish::json_util::sortJsonArrayByKey(entriesArray, "Id"); 1527 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size(); 1528 asyncResp->res.jsonValue["Members"] = std::move(entriesArray); 1529 } 1530 1531 inline void handleSystemsLogServiceEventLogLogEntryCollection( 1532 App& app, const crow::Request& req, 1533 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1534 const std::string& systemName) 1535 { 1536 query_param::QueryCapabilities capabilities = { 1537 .canDelegateTop = true, 1538 .canDelegateSkip = true, 1539 }; 1540 query_param::Query delegatedQuery; 1541 if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, 1542 delegatedQuery, capabilities)) 1543 { 1544 return; 1545 } 1546 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1547 { 1548 // Option currently returns no systems. TBD 1549 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1550 systemName); 1551 return; 1552 } 1553 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1554 { 1555 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1556 systemName); 1557 return; 1558 } 1559 1560 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); 1561 size_t skip = delegatedQuery.skip.value_or(0); 1562 1563 // Collections don't include the static data added by SubRoute 1564 // because it has a duplicate entry for members 1565 asyncResp->res.jsonValue["@odata.type"] = 1566 "#LogEntryCollection.LogEntryCollection"; 1567 asyncResp->res.jsonValue["@odata.id"] = 1568 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", 1569 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1570 asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 1571 asyncResp->res.jsonValue["Description"] = 1572 "Collection of System Event Log Entries"; 1573 1574 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; 1575 logEntryArray = nlohmann::json::array(); 1576 // Go through the log files and create a unique ID for each 1577 // entry 1578 std::vector<std::filesystem::path> redfishLogFiles; 1579 getRedfishLogFiles(redfishLogFiles); 1580 uint64_t entryCount = 0; 1581 std::string logEntry; 1582 1583 // Oldest logs are in the last file, so start there and loop 1584 // backwards 1585 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) 1586 { 1587 std::ifstream logStream(*it); 1588 if (!logStream.is_open()) 1589 { 1590 continue; 1591 } 1592 1593 // Reset the unique ID on the first entry 1594 bool firstEntry = true; 1595 while (std::getline(logStream, logEntry)) 1596 { 1597 std::string idStr; 1598 if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 1599 { 1600 continue; 1601 } 1602 firstEntry = false; 1603 1604 nlohmann::json::object_t bmcLogEntry; 1605 LogParseError status = 1606 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); 1607 if (status == LogParseError::messageIdNotInRegistry) 1608 { 1609 continue; 1610 } 1611 if (status != LogParseError::success) 1612 { 1613 messages::internalError(asyncResp->res); 1614 return; 1615 } 1616 1617 entryCount++; 1618 // Handle paging using skip (number of entries to skip from the 1619 // start) and top (number of entries to display) 1620 if (entryCount <= skip || entryCount > skip + top) 1621 { 1622 continue; 1623 } 1624 1625 logEntryArray.emplace_back(std::move(bmcLogEntry)); 1626 } 1627 } 1628 asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1629 if (skip + top < entryCount) 1630 { 1631 asyncResp->res.jsonValue["Members@odata.nextLink"] = 1632 boost::urls::format( 1633 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries?$skip={}", 1634 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(skip + top)); 1635 } 1636 } 1637 1638 inline void requestRoutesJournalEventLogEntryCollection(App& app) 1639 { 1640 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/") 1641 .privileges(redfish::privileges::getLogEntryCollection) 1642 .methods(boost::beast::http::verb::get)(std::bind_front( 1643 handleSystemsLogServiceEventLogLogEntryCollection, std::ref(app))); 1644 } 1645 1646 inline void handleSystemsLogServiceEventLogEntriesGet( 1647 App& app, const crow::Request& req, 1648 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1649 const std::string& systemName, const std::string& param) 1650 { 1651 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1652 { 1653 return; 1654 } 1655 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1656 { 1657 // Option currently returns no systems. TBD 1658 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1659 systemName); 1660 return; 1661 } 1662 1663 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1664 { 1665 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1666 systemName); 1667 return; 1668 } 1669 1670 const std::string& targetID = param; 1671 1672 // Go through the log files and check the unique ID for each 1673 // entry to find the target entry 1674 std::vector<std::filesystem::path> redfishLogFiles; 1675 getRedfishLogFiles(redfishLogFiles); 1676 std::string logEntry; 1677 1678 // Oldest logs are in the last file, so start there and loop 1679 // backwards 1680 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) 1681 { 1682 std::ifstream logStream(*it); 1683 if (!logStream.is_open()) 1684 { 1685 continue; 1686 } 1687 1688 // Reset the unique ID on the first entry 1689 bool firstEntry = true; 1690 while (std::getline(logStream, logEntry)) 1691 { 1692 std::string idStr; 1693 if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 1694 { 1695 continue; 1696 } 1697 firstEntry = false; 1698 1699 if (idStr == targetID) 1700 { 1701 nlohmann::json::object_t bmcLogEntry; 1702 LogParseError status = 1703 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); 1704 if (status != LogParseError::success) 1705 { 1706 messages::internalError(asyncResp->res); 1707 return; 1708 } 1709 asyncResp->res.jsonValue.update(bmcLogEntry); 1710 return; 1711 } 1712 } 1713 } 1714 // Requested ID was not found 1715 messages::resourceNotFound(asyncResp->res, "LogEntry", targetID); 1716 } 1717 1718 inline void requestRoutesJournalEventLogEntry(App& app) 1719 { 1720 BMCWEB_ROUTE( 1721 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1722 .privileges(redfish::privileges::getLogEntry) 1723 .methods(boost::beast::http::verb::get)(std::bind_front( 1724 handleSystemsLogServiceEventLogEntriesGet, std::ref(app))); 1725 } 1726 1727 inline void dBusEventLogEntryCollection( 1728 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1729 { 1730 // Collections don't include the static data added by SubRoute 1731 // because it has a duplicate entry for members 1732 asyncResp->res.jsonValue["@odata.type"] = 1733 "#LogEntryCollection.LogEntryCollection"; 1734 asyncResp->res.jsonValue["@odata.id"] = 1735 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", 1736 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1737 asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 1738 asyncResp->res.jsonValue["Description"] = 1739 "Collection of System Event Log Entries"; 1740 1741 // DBus implementation of EventLog/Entries 1742 // Make call to Logging Service to find all log entry objects 1743 sdbusplus::message::object_path path("/xyz/openbmc_project/logging"); 1744 dbus::utility::getManagedObjects( 1745 "xyz.openbmc_project.Logging", path, 1746 [asyncResp](const boost::system::error_code& ec, 1747 const dbus::utility::ManagedObjectType& resp) { 1748 afterLogEntriesGetManagedObjects(asyncResp, ec, resp); 1749 }); 1750 } 1751 1752 inline void requestRoutesDBusEventLogEntryCollection(App& app) 1753 { 1754 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/") 1755 .privileges(redfish::privileges::getLogEntryCollection) 1756 .methods(boost::beast::http::verb::get)( 1757 [&app](const crow::Request& req, 1758 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1759 const std::string& systemName) { 1760 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1761 { 1762 return; 1763 } 1764 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1765 { 1766 // Option currently returns no systems. TBD 1767 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1768 systemName); 1769 return; 1770 } 1771 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1772 { 1773 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1774 systemName); 1775 return; 1776 } 1777 dBusEventLogEntryCollection(asyncResp); 1778 }); 1779 } 1780 1781 inline void dBusEventLogEntryGet( 1782 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID) 1783 { 1784 dbus::utility::escapePathForDbus(entryID); 1785 1786 // DBus implementation of EventLog/Entries 1787 // Make call to Logging Service to find all log entry objects 1788 dbus::utility::getAllProperties( 1789 "xyz.openbmc_project.Logging", 1790 "/xyz/openbmc_project/logging/entry/" + entryID, "", 1791 [asyncResp, entryID](const boost::system::error_code& ec, 1792 const dbus::utility::DBusPropertiesMap& resp) { 1793 if (ec.value() == EBADR) 1794 { 1795 messages::resourceNotFound(asyncResp->res, "EventLogEntry", 1796 entryID); 1797 return; 1798 } 1799 if (ec) 1800 { 1801 BMCWEB_LOG_ERROR( 1802 "EventLogEntry (DBus) resp_handler got error {}", ec); 1803 messages::internalError(asyncResp->res); 1804 return; 1805 } 1806 1807 fillEventLogLogEntryFromPropertyMap(asyncResp, resp, 1808 asyncResp->res.jsonValue); 1809 }); 1810 } 1811 1812 inline void dBusEventLogEntryPatch( 1813 const crow::Request& req, 1814 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1815 const std::string& entryId) 1816 { 1817 std::optional<bool> resolved; 1818 1819 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved)) 1820 { 1821 return; 1822 } 1823 BMCWEB_LOG_DEBUG("Set Resolved"); 1824 1825 setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging", 1826 "/xyz/openbmc_project/logging/entry/" + entryId, 1827 "xyz.openbmc_project.Logging.Entry", "Resolved", 1828 resolved.value_or(false)); 1829 } 1830 1831 inline void dBusEventLogEntryDelete( 1832 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID) 1833 { 1834 BMCWEB_LOG_DEBUG("Do delete single event entries."); 1835 1836 dbus::utility::escapePathForDbus(entryID); 1837 1838 // Process response from Logging service. 1839 auto respHandler = [asyncResp, 1840 entryID](const boost::system::error_code& ec) { 1841 BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done"); 1842 if (ec) 1843 { 1844 if (ec.value() == EBADR) 1845 { 1846 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 1847 return; 1848 } 1849 // TODO Handle for specific error code 1850 BMCWEB_LOG_ERROR( 1851 "EventLogEntry (DBus) doDelete respHandler got error {}", ec); 1852 asyncResp->res.result( 1853 boost::beast::http::status::internal_server_error); 1854 return; 1855 } 1856 1857 asyncResp->res.result(boost::beast::http::status::ok); 1858 }; 1859 1860 // Make call to Logging service to request Delete Log 1861 dbus::utility::async_method_call( 1862 asyncResp, respHandler, "xyz.openbmc_project.Logging", 1863 "/xyz/openbmc_project/logging/entry/" + entryID, 1864 "xyz.openbmc_project.Object.Delete", "Delete"); 1865 } 1866 1867 inline void requestRoutesDBusEventLogEntry(App& app) 1868 { 1869 BMCWEB_ROUTE( 1870 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1871 .privileges(redfish::privileges::getLogEntry) 1872 .methods(boost::beast::http::verb::get)( 1873 [&app](const crow::Request& req, 1874 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1875 const std::string& systemName, const std::string& entryId) { 1876 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1877 { 1878 return; 1879 } 1880 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1881 { 1882 // Option currently returns no systems. TBD 1883 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1884 systemName); 1885 return; 1886 } 1887 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1888 { 1889 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1890 systemName); 1891 return; 1892 } 1893 1894 dBusEventLogEntryGet(asyncResp, entryId); 1895 }); 1896 1897 BMCWEB_ROUTE( 1898 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1899 .privileges(redfish::privileges::patchLogEntry) 1900 .methods(boost::beast::http::verb::patch)( 1901 [&app](const crow::Request& req, 1902 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1903 const std::string& systemName, const std::string& entryId) { 1904 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1905 { 1906 return; 1907 } 1908 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1909 { 1910 // Option currently returns no systems. TBD 1911 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1912 systemName); 1913 return; 1914 } 1915 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1916 { 1917 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1918 systemName); 1919 return; 1920 } 1921 1922 dBusEventLogEntryPatch(req, asyncResp, entryId); 1923 }); 1924 1925 BMCWEB_ROUTE( 1926 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1927 .privileges(redfish::privileges::deleteLogEntry) 1928 1929 .methods(boost::beast::http::verb::delete_)( 1930 [&app](const crow::Request& req, 1931 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1932 const std::string& systemName, const std::string& param) { 1933 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1934 { 1935 return; 1936 } 1937 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1938 { 1939 // Option currently returns no systems. TBD 1940 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1941 systemName); 1942 return; 1943 } 1944 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1945 { 1946 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1947 systemName); 1948 return; 1949 } 1950 dBusEventLogEntryDelete(asyncResp, param); 1951 }); 1952 } 1953 1954 inline void handleBMCLogServicesCollectionGet( 1955 crow::App& app, const crow::Request& req, 1956 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1957 const std::string& managerId) 1958 { 1959 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1960 { 1961 return; 1962 } 1963 1964 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1965 { 1966 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1967 return; 1968 } 1969 1970 // Collections don't include the static data added by SubRoute 1971 // because it has a duplicate entry for members 1972 asyncResp->res.jsonValue["@odata.type"] = 1973 "#LogServiceCollection.LogServiceCollection"; 1974 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1975 "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME); 1976 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 1977 asyncResp->res.jsonValue["Description"] = 1978 "Collection of LogServices for this Manager"; 1979 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"]; 1980 logServiceArray = nlohmann::json::array(); 1981 1982 if constexpr (BMCWEB_REDFISH_BMC_JOURNAL) 1983 { 1984 nlohmann::json::object_t journal; 1985 journal["@odata.id"] = 1986 boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal", 1987 BMCWEB_REDFISH_MANAGER_URI_NAME); 1988 logServiceArray.emplace_back(std::move(journal)); 1989 } 1990 1991 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size(); 1992 1993 if constexpr (BMCWEB_REDFISH_DUMP_LOG) 1994 { 1995 constexpr std::array<std::string_view, 1> interfaces = { 1996 "xyz.openbmc_project.Collection.DeleteAll"}; 1997 dbus::utility::getSubTreePaths( 1998 "/xyz/openbmc_project/dump", 0, interfaces, 1999 [asyncResp](const boost::system::error_code& ec, 2000 const dbus::utility::MapperGetSubTreePathsResponse& 2001 subTreePaths) { 2002 if (ec) 2003 { 2004 BMCWEB_LOG_ERROR( 2005 "handleBMCLogServicesCollectionGet respHandler got error {}", 2006 ec); 2007 // Assume that getting an error simply means there are no 2008 // dump LogServices. Return without adding any error 2009 // response. 2010 return; 2011 } 2012 2013 nlohmann::json& logServiceArrayLocal = 2014 asyncResp->res.jsonValue["Members"]; 2015 2016 for (const std::string& path : subTreePaths) 2017 { 2018 if (path == "/xyz/openbmc_project/dump/bmc") 2019 { 2020 nlohmann::json::object_t member; 2021 member["@odata.id"] = boost::urls::format( 2022 "/redfish/v1/Managers/{}/LogServices/Dump", 2023 BMCWEB_REDFISH_MANAGER_URI_NAME); 2024 logServiceArrayLocal.emplace_back(std::move(member)); 2025 } 2026 else if (path == "/xyz/openbmc_project/dump/faultlog") 2027 { 2028 nlohmann::json::object_t member; 2029 member["@odata.id"] = boost::urls::format( 2030 "/redfish/v1/Managers/{}/LogServices/FaultLog", 2031 BMCWEB_REDFISH_MANAGER_URI_NAME); 2032 logServiceArrayLocal.emplace_back(std::move(member)); 2033 } 2034 } 2035 2036 asyncResp->res.jsonValue["Members@odata.count"] = 2037 logServiceArrayLocal.size(); 2038 }); 2039 } 2040 } 2041 2042 inline void requestRoutesBMCLogServiceCollection(App& app) 2043 { 2044 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/") 2045 .privileges(redfish::privileges::getLogServiceCollection) 2046 .methods(boost::beast::http::verb::get)( 2047 std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app))); 2048 } 2049 2050 inline void getDumpServiceInfo( 2051 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2052 const std::string& dumpType) 2053 { 2054 std::string dumpPath; 2055 log_service::OverWritePolicy overWritePolicy = 2056 log_service::OverWritePolicy::Invalid; 2057 bool collectDiagnosticDataSupported = false; 2058 2059 if (dumpType == "BMC") 2060 { 2061 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump", 2062 BMCWEB_REDFISH_MANAGER_URI_NAME); 2063 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; 2064 collectDiagnosticDataSupported = true; 2065 } 2066 else if (dumpType == "FaultLog") 2067 { 2068 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog", 2069 BMCWEB_REDFISH_MANAGER_URI_NAME); 2070 overWritePolicy = log_service::OverWritePolicy::Unknown; 2071 collectDiagnosticDataSupported = false; 2072 } 2073 else if (dumpType == "System") 2074 { 2075 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump", 2076 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2077 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; 2078 collectDiagnosticDataSupported = true; 2079 } 2080 else 2081 { 2082 BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}", 2083 dumpType); 2084 messages::internalError(asyncResp->res); 2085 return; 2086 } 2087 2088 asyncResp->res.jsonValue["@odata.id"] = dumpPath; 2089 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 2090 asyncResp->res.jsonValue["Name"] = "Dump LogService"; 2091 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService"; 2092 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename(); 2093 asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy; 2094 2095 std::pair<std::string, std::string> redfishDateTimeOffset = 2096 redfish::time_utils::getDateTimeOffsetNow(); 2097 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 2098 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 2099 redfishDateTimeOffset.second; 2100 2101 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries"; 2102 2103 if (collectDiagnosticDataSupported) 2104 { 2105 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"] 2106 ["target"] = 2107 dumpPath + "/Actions/LogService.CollectDiagnosticData"; 2108 } 2109 2110 constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface}; 2111 dbus::utility::getSubTreePaths( 2112 "/xyz/openbmc_project/dump", 0, interfaces, 2113 [asyncResp, dumpType, dumpPath]( 2114 const boost::system::error_code& ec, 2115 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) { 2116 if (ec) 2117 { 2118 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}", 2119 ec); 2120 // Assume that getting an error simply means there are no dump 2121 // LogServices. Return without adding any error response. 2122 return; 2123 } 2124 std::string dbusDumpPath = getDumpPath(dumpType); 2125 for (const std::string& path : subTreePaths) 2126 { 2127 if (path == dbusDumpPath) 2128 { 2129 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 2130 ["target"] = 2131 dumpPath + "/Actions/LogService.ClearLog"; 2132 break; 2133 } 2134 } 2135 }); 2136 } 2137 2138 inline void handleLogServicesDumpServiceGet( 2139 crow::App& app, const std::string& dumpType, const crow::Request& req, 2140 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2141 const std::string& managerId) 2142 { 2143 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2144 { 2145 return; 2146 } 2147 2148 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2149 { 2150 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2151 return; 2152 } 2153 2154 getDumpServiceInfo(asyncResp, dumpType); 2155 } 2156 2157 inline void handleLogServicesDumpServiceComputerSystemGet( 2158 crow::App& app, const crow::Request& req, 2159 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2160 const std::string& chassisId) 2161 { 2162 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2163 { 2164 return; 2165 } 2166 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2167 { 2168 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2169 return; 2170 } 2171 getDumpServiceInfo(asyncResp, "System"); 2172 } 2173 2174 inline void handleLogServicesDumpEntriesCollectionGet( 2175 crow::App& app, const std::string& dumpType, const crow::Request& req, 2176 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2177 const std::string& managerId) 2178 { 2179 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2180 { 2181 return; 2182 } 2183 2184 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2185 { 2186 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2187 return; 2188 } 2189 getDumpEntryCollection(asyncResp, dumpType); 2190 } 2191 2192 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet( 2193 crow::App& app, const crow::Request& req, 2194 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2195 const std::string& chassisId) 2196 { 2197 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2198 { 2199 return; 2200 } 2201 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2202 { 2203 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2204 return; 2205 } 2206 getDumpEntryCollection(asyncResp, "System"); 2207 } 2208 2209 inline void handleLogServicesDumpEntryGet( 2210 crow::App& app, const std::string& dumpType, const crow::Request& req, 2211 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2212 const std::string& managerId, const std::string& dumpId) 2213 { 2214 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2215 { 2216 return; 2217 } 2218 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2219 { 2220 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2221 return; 2222 } 2223 getDumpEntryById(asyncResp, dumpId, dumpType); 2224 } 2225 2226 inline void handleLogServicesDumpEntryComputerSystemGet( 2227 crow::App& app, const crow::Request& req, 2228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2229 const std::string& chassisId, const std::string& dumpId) 2230 { 2231 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2232 { 2233 return; 2234 } 2235 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2236 { 2237 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2238 return; 2239 } 2240 getDumpEntryById(asyncResp, dumpId, "System"); 2241 } 2242 2243 inline void handleLogServicesDumpEntryDelete( 2244 crow::App& app, const std::string& dumpType, const crow::Request& req, 2245 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2246 const std::string& managerId, const std::string& dumpId) 2247 { 2248 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2249 { 2250 return; 2251 } 2252 2253 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2254 { 2255 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2256 return; 2257 } 2258 deleteDumpEntry(asyncResp, dumpId, dumpType); 2259 } 2260 2261 inline void handleLogServicesDumpEntryComputerSystemDelete( 2262 crow::App& app, const crow::Request& req, 2263 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2264 const std::string& chassisId, const std::string& dumpId) 2265 { 2266 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2267 { 2268 return; 2269 } 2270 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2271 { 2272 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2273 return; 2274 } 2275 deleteDumpEntry(asyncResp, dumpId, "System"); 2276 } 2277 2278 inline void handleLogServicesDumpEntryDownloadGet( 2279 crow::App& app, const std::string& dumpType, const crow::Request& req, 2280 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2281 const std::string& managerId, const std::string& dumpId) 2282 { 2283 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2284 { 2285 return; 2286 } 2287 2288 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2289 { 2290 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2291 return; 2292 } 2293 downloadDumpEntry(asyncResp, dumpId, dumpType); 2294 } 2295 2296 inline void handleDBusEventLogEntryDownloadGet( 2297 crow::App& app, const std::string& dumpType, const crow::Request& req, 2298 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2299 const std::string& systemName, const std::string& entryID) 2300 { 2301 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2302 { 2303 return; 2304 } 2305 if (!http_helpers::isContentTypeAllowed( 2306 req.getHeaderValue("Accept"), 2307 http_helpers::ContentType::OctetStream, true)) 2308 { 2309 asyncResp->res.result(boost::beast::http::status::bad_request); 2310 return; 2311 } 2312 downloadEventLogEntry(asyncResp, systemName, entryID, dumpType); 2313 } 2314 2315 inline void handleLogServicesDumpCollectDiagnosticDataPost( 2316 crow::App& app, const std::string& dumpType, const crow::Request& req, 2317 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2318 const std::string& managerId) 2319 { 2320 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2321 { 2322 return; 2323 } 2324 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2325 { 2326 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2327 return; 2328 } 2329 2330 createDump(asyncResp, req, dumpType); 2331 } 2332 2333 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost( 2334 crow::App& app, const crow::Request& req, 2335 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2336 const std::string& systemName) 2337 { 2338 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2339 { 2340 return; 2341 } 2342 2343 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2344 { 2345 // Option currently returns no systems. TBD 2346 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2347 systemName); 2348 return; 2349 } 2350 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2351 { 2352 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2353 systemName); 2354 return; 2355 } 2356 createDump(asyncResp, req, "System"); 2357 } 2358 2359 inline void handleLogServicesDumpClearLogPost( 2360 crow::App& app, const std::string& dumpType, const crow::Request& req, 2361 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2362 const std::string& managerId) 2363 { 2364 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2365 { 2366 return; 2367 } 2368 2369 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2370 { 2371 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2372 return; 2373 } 2374 clearDump(asyncResp, dumpType); 2375 } 2376 2377 inline void handleLogServicesDumpClearLogComputerSystemPost( 2378 crow::App& app, const crow::Request& req, 2379 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2380 const std::string& systemName) 2381 { 2382 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2383 { 2384 return; 2385 } 2386 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2387 { 2388 // Option currently returns no systems. TBD 2389 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2390 systemName); 2391 return; 2392 } 2393 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2394 { 2395 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2396 systemName); 2397 return; 2398 } 2399 clearDump(asyncResp, "System"); 2400 } 2401 2402 inline void requestRoutesBMCDumpService(App& app) 2403 { 2404 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/") 2405 .privileges(redfish::privileges::getLogService) 2406 .methods(boost::beast::http::verb::get)(std::bind_front( 2407 handleLogServicesDumpServiceGet, std::ref(app), "BMC")); 2408 } 2409 2410 inline void requestRoutesBMCDumpEntryCollection(App& app) 2411 { 2412 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/") 2413 .privileges(redfish::privileges::getLogEntryCollection) 2414 .methods(boost::beast::http::verb::get)(std::bind_front( 2415 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC")); 2416 } 2417 2418 inline void requestRoutesBMCDumpEntry(App& app) 2419 { 2420 BMCWEB_ROUTE(app, 2421 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/") 2422 .privileges(redfish::privileges::getLogEntry) 2423 .methods(boost::beast::http::verb::get)(std::bind_front( 2424 handleLogServicesDumpEntryGet, std::ref(app), "BMC")); 2425 2426 BMCWEB_ROUTE(app, 2427 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/") 2428 .privileges(redfish::privileges::deleteLogEntry) 2429 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2430 handleLogServicesDumpEntryDelete, std::ref(app), "BMC")); 2431 } 2432 2433 inline void requestRoutesBMCDumpEntryDownload(App& app) 2434 { 2435 BMCWEB_ROUTE( 2436 app, 2437 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/") 2438 .privileges(redfish::privileges::getLogEntry) 2439 .methods(boost::beast::http::verb::get)(std::bind_front( 2440 handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC")); 2441 } 2442 2443 inline void requestRoutesBMCDumpCreate(App& app) 2444 { 2445 BMCWEB_ROUTE( 2446 app, 2447 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/") 2448 .privileges(redfish::privileges::postLogService) 2449 .methods(boost::beast::http::verb::post)( 2450 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost, 2451 std::ref(app), "BMC")); 2452 } 2453 2454 inline void requestRoutesBMCDumpClear(App& app) 2455 { 2456 BMCWEB_ROUTE( 2457 app, 2458 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/") 2459 .privileges(redfish::privileges::postLogService) 2460 .methods(boost::beast::http::verb::post)(std::bind_front( 2461 handleLogServicesDumpClearLogPost, std::ref(app), "BMC")); 2462 } 2463 2464 inline void requestRoutesDBusEventLogEntryDownload(App& app) 2465 { 2466 BMCWEB_ROUTE( 2467 app, 2468 "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment/") 2469 .privileges(redfish::privileges::getLogEntry) 2470 .methods(boost::beast::http::verb::get)(std::bind_front( 2471 handleDBusEventLogEntryDownloadGet, std::ref(app), "System")); 2472 } 2473 2474 inline void requestRoutesFaultLogDumpService(App& app) 2475 { 2476 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/") 2477 .privileges(redfish::privileges::getLogService) 2478 .methods(boost::beast::http::verb::get)(std::bind_front( 2479 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog")); 2480 } 2481 2482 inline void requestRoutesFaultLogDumpEntryCollection(App& app) 2483 { 2484 BMCWEB_ROUTE(app, 2485 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/") 2486 .privileges(redfish::privileges::getLogEntryCollection) 2487 .methods(boost::beast::http::verb::get)( 2488 std::bind_front(handleLogServicesDumpEntriesCollectionGet, 2489 std::ref(app), "FaultLog")); 2490 } 2491 2492 inline void requestRoutesFaultLogDumpEntry(App& app) 2493 { 2494 BMCWEB_ROUTE( 2495 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/") 2496 .privileges(redfish::privileges::getLogEntry) 2497 .methods(boost::beast::http::verb::get)(std::bind_front( 2498 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog")); 2499 2500 BMCWEB_ROUTE( 2501 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/") 2502 .privileges(redfish::privileges::deleteLogEntry) 2503 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2504 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog")); 2505 } 2506 2507 inline void requestRoutesFaultLogDumpClear(App& app) 2508 { 2509 BMCWEB_ROUTE( 2510 app, 2511 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/") 2512 .privileges(redfish::privileges::postLogService) 2513 .methods(boost::beast::http::verb::post)(std::bind_front( 2514 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog")); 2515 } 2516 2517 inline void requestRoutesSystemDumpService(App& app) 2518 { 2519 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/") 2520 .privileges(redfish::privileges::getLogService) 2521 .methods(boost::beast::http::verb::get)(std::bind_front( 2522 handleLogServicesDumpServiceComputerSystemGet, std::ref(app))); 2523 } 2524 2525 inline void requestRoutesSystemDumpEntryCollection(App& app) 2526 { 2527 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/") 2528 .privileges(redfish::privileges::getLogEntryCollection) 2529 .methods(boost::beast::http::verb::get)(std::bind_front( 2530 handleLogServicesDumpEntriesCollectionComputerSystemGet, 2531 std::ref(app))); 2532 } 2533 2534 inline void requestRoutesSystemDumpEntry(App& app) 2535 { 2536 BMCWEB_ROUTE(app, 2537 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/") 2538 .privileges(redfish::privileges::getLogEntry) 2539 .methods(boost::beast::http::verb::get)(std::bind_front( 2540 handleLogServicesDumpEntryComputerSystemGet, std::ref(app))); 2541 2542 BMCWEB_ROUTE(app, 2543 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/") 2544 .privileges(redfish::privileges::deleteLogEntry) 2545 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2546 handleLogServicesDumpEntryComputerSystemDelete, std::ref(app))); 2547 } 2548 2549 inline void requestRoutesSystemDumpCreate(App& app) 2550 { 2551 BMCWEB_ROUTE( 2552 app, 2553 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/") 2554 .privileges(redfish::privileges::postLogService) 2555 .methods(boost::beast::http::verb::post)(std::bind_front( 2556 handleLogServicesDumpCollectDiagnosticDataComputerSystemPost, 2557 std::ref(app))); 2558 } 2559 2560 inline void requestRoutesSystemDumpClear(App& app) 2561 { 2562 BMCWEB_ROUTE( 2563 app, 2564 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/") 2565 .privileges(redfish::privileges::postLogService) 2566 .methods(boost::beast::http::verb::post)(std::bind_front( 2567 handleLogServicesDumpClearLogComputerSystemPost, std::ref(app))); 2568 } 2569 2570 inline void requestRoutesCrashdumpService(App& app) 2571 { 2572 // Note: Deviated from redfish privilege registry for GET & HEAD 2573 // method for security reasons. 2574 /** 2575 * Functions triggers appropriate requests on DBus 2576 */ 2577 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/") 2578 // This is incorrect, should be: 2579 //.privileges(redfish::privileges::getLogService) 2580 .privileges({{"ConfigureManager"}}) 2581 .methods( 2582 boost::beast::http::verb:: 2583 get)([&app](const crow::Request& req, 2584 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2585 const std::string& systemName) { 2586 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2587 { 2588 return; 2589 } 2590 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2591 { 2592 // Option currently returns no systems. TBD 2593 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2594 systemName); 2595 return; 2596 } 2597 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2598 { 2599 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2600 systemName); 2601 return; 2602 } 2603 2604 // Copy over the static data to include the entries added by 2605 // SubRoute 2606 asyncResp->res.jsonValue["@odata.id"] = 2607 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", 2608 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2609 asyncResp->res.jsonValue["@odata.type"] = 2610 "#LogService.v1_2_0.LogService"; 2611 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service"; 2612 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service"; 2613 asyncResp->res.jsonValue["Id"] = "Crashdump"; 2614 asyncResp->res.jsonValue["OverWritePolicy"] = 2615 log_service::OverWritePolicy::WrapsWhenFull; 2616 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 2617 2618 std::pair<std::string, std::string> redfishDateTimeOffset = 2619 redfish::time_utils::getDateTimeOffsetNow(); 2620 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 2621 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 2622 redfishDateTimeOffset.second; 2623 2624 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( 2625 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", 2626 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2627 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 2628 ["target"] = std::format( 2629 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog", 2630 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2631 asyncResp->res 2632 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"] 2633 ["target"] = std::format( 2634 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData", 2635 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2636 }); 2637 } 2638 2639 void inline requestRoutesCrashdumpClear(App& app) 2640 { 2641 BMCWEB_ROUTE( 2642 app, 2643 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/") 2644 // This is incorrect, should be: 2645 //.privileges(redfish::privileges::postLogService) 2646 .privileges({{"ConfigureComponents"}}) 2647 .methods(boost::beast::http::verb::post)( 2648 [&app](const crow::Request& req, 2649 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2650 const std::string& systemName) { 2651 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2652 { 2653 return; 2654 } 2655 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2656 { 2657 // Option currently returns no systems. TBD 2658 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2659 systemName); 2660 return; 2661 } 2662 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2663 { 2664 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2665 systemName); 2666 return; 2667 } 2668 dbus::utility::async_method_call( 2669 asyncResp, 2670 [asyncResp](const boost::system::error_code& ec, 2671 const std::string&) { 2672 if (ec) 2673 { 2674 messages::internalError(asyncResp->res); 2675 return; 2676 } 2677 messages::success(asyncResp->res); 2678 }, 2679 crashdumpObject, crashdumpPath, deleteAllInterface, 2680 "DeleteAll"); 2681 }); 2682 } 2683 2684 inline void logCrashdumpEntry( 2685 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2686 const std::string& logID, nlohmann::json& logEntryJson) 2687 { 2688 auto getStoredLogCallback = 2689 [asyncResp, logID, 2690 &logEntryJson](const boost::system::error_code& ec, 2691 const dbus::utility::DBusPropertiesMap& params) { 2692 if (ec) 2693 { 2694 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message()); 2695 if (ec.value() == 2696 boost::system::linux_error::bad_request_descriptor) 2697 { 2698 messages::resourceNotFound(asyncResp->res, "LogEntry", 2699 logID); 2700 } 2701 else 2702 { 2703 messages::internalError(asyncResp->res); 2704 } 2705 return; 2706 } 2707 2708 std::string timestamp{}; 2709 std::string filename{}; 2710 std::string logfile{}; 2711 parseCrashdumpParameters(params, filename, timestamp, logfile); 2712 2713 if (filename.empty() || timestamp.empty()) 2714 { 2715 messages::resourceNotFound(asyncResp->res, "LogEntry", logID); 2716 return; 2717 } 2718 2719 std::string crashdumpURI = 2720 std::format( 2721 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/", 2722 BMCWEB_REDFISH_SYSTEM_URI_NAME) + 2723 logID + "/" + filename; 2724 nlohmann::json::object_t logEntry; 2725 logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 2726 logEntry["@odata.id"] = boost::urls::format( 2727 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}", 2728 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID); 2729 logEntry["Name"] = "CPU Crashdump"; 2730 logEntry["Id"] = logID; 2731 logEntry["EntryType"] = log_entry::LogEntryType::Oem; 2732 logEntry["AdditionalDataURI"] = std::move(crashdumpURI); 2733 logEntry["DiagnosticDataType"] = "OEM"; 2734 logEntry["OEMDiagnosticDataType"] = "PECICrashdump"; 2735 logEntry["Created"] = std::move(timestamp); 2736 2737 // If logEntryJson references an array of LogEntry resources 2738 // ('Members' list), then push this as a new entry, otherwise set it 2739 // directly 2740 if (logEntryJson.is_array()) 2741 { 2742 logEntryJson.push_back(logEntry); 2743 asyncResp->res.jsonValue["Members@odata.count"] = 2744 logEntryJson.size(); 2745 } 2746 else 2747 { 2748 logEntryJson.update(logEntry); 2749 } 2750 }; 2751 dbus::utility::getAllProperties( 2752 crashdumpObject, crashdumpPath + std::string("/") + logID, 2753 crashdumpInterface, std::move(getStoredLogCallback)); 2754 } 2755 2756 inline void requestRoutesCrashdumpEntryCollection(App& app) 2757 { 2758 // Note: Deviated from redfish privilege registry for GET & HEAD 2759 // method for security reasons. 2760 /** 2761 * Functions triggers appropriate requests on DBus 2762 */ 2763 BMCWEB_ROUTE(app, 2764 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/") 2765 // This is incorrect, should be. 2766 //.privileges(redfish::privileges::postLogEntryCollection) 2767 .privileges({{"ConfigureComponents"}}) 2768 .methods( 2769 boost::beast::http::verb:: 2770 get)([&app](const crow::Request& req, 2771 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2772 const std::string& systemName) { 2773 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2774 { 2775 return; 2776 } 2777 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2778 { 2779 // Option currently returns no systems. TBD 2780 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2781 systemName); 2782 return; 2783 } 2784 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2785 { 2786 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2787 systemName); 2788 return; 2789 } 2790 2791 constexpr std::array<std::string_view, 1> interfaces = { 2792 crashdumpInterface}; 2793 dbus::utility::getSubTreePaths( 2794 "/", 0, interfaces, 2795 [asyncResp](const boost::system::error_code& ec, 2796 const std::vector<std::string>& resp) { 2797 if (ec) 2798 { 2799 if (ec.value() != 2800 boost::system::errc::no_such_file_or_directory) 2801 { 2802 BMCWEB_LOG_DEBUG("failed to get entries ec: {}", 2803 ec.message()); 2804 messages::internalError(asyncResp->res); 2805 return; 2806 } 2807 } 2808 asyncResp->res.jsonValue["@odata.type"] = 2809 "#LogEntryCollection.LogEntryCollection"; 2810 asyncResp->res.jsonValue["@odata.id"] = std::format( 2811 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", 2812 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2813 asyncResp->res.jsonValue["Name"] = 2814 "Open BMC Crashdump Entries"; 2815 asyncResp->res.jsonValue["Description"] = 2816 "Collection of Crashdump Entries"; 2817 asyncResp->res.jsonValue["Members"] = 2818 nlohmann::json::array(); 2819 asyncResp->res.jsonValue["Members@odata.count"] = 0; 2820 2821 for (const std::string& path : resp) 2822 { 2823 const sdbusplus::message::object_path objPath(path); 2824 // Get the log ID 2825 std::string logID = objPath.filename(); 2826 if (logID.empty()) 2827 { 2828 continue; 2829 } 2830 // Add the log entry to the array 2831 logCrashdumpEntry(asyncResp, logID, 2832 asyncResp->res.jsonValue["Members"]); 2833 } 2834 }); 2835 }); 2836 } 2837 2838 inline void requestRoutesCrashdumpEntry(App& app) 2839 { 2840 // Note: Deviated from redfish privilege registry for GET & HEAD 2841 // method for security reasons. 2842 2843 BMCWEB_ROUTE( 2844 app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/") 2845 // this is incorrect, should be 2846 // .privileges(redfish::privileges::getLogEntry) 2847 .privileges({{"ConfigureComponents"}}) 2848 .methods(boost::beast::http::verb::get)( 2849 [&app](const crow::Request& req, 2850 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2851 const std::string& systemName, const std::string& param) { 2852 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2853 { 2854 return; 2855 } 2856 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2857 { 2858 // Option currently returns no systems. TBD 2859 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2860 systemName); 2861 return; 2862 } 2863 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2864 { 2865 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2866 systemName); 2867 return; 2868 } 2869 const std::string& logID = param; 2870 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); 2871 }); 2872 } 2873 2874 inline void requestRoutesCrashdumpFile(App& app) 2875 { 2876 // Note: Deviated from redfish privilege registry for GET & HEAD 2877 // method for security reasons. 2878 BMCWEB_ROUTE( 2879 app, 2880 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/") 2881 .privileges(redfish::privileges::getLogEntry) 2882 .methods(boost::beast::http::verb::get)( 2883 [](const crow::Request& req, 2884 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2885 const std::string& systemName, const std::string& logID, 2886 const std::string& fileName) { 2887 // Do not call getRedfishRoute here since the crashdump file is 2888 // not a Redfish resource. 2889 2890 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2891 { 2892 // Option currently returns no systems. TBD 2893 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2894 systemName); 2895 return; 2896 } 2897 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2898 { 2899 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2900 systemName); 2901 return; 2902 } 2903 2904 auto getStoredLogCallback = 2905 [asyncResp, logID, fileName, 2906 url(boost::urls::url(req.url()))]( 2907 const boost::system::error_code& ec, 2908 const std::vector<std::pair< 2909 std::string, dbus::utility::DbusVariantType>>& 2910 resp) { 2911 if (ec) 2912 { 2913 BMCWEB_LOG_DEBUG("failed to get log ec: {}", 2914 ec.message()); 2915 messages::internalError(asyncResp->res); 2916 return; 2917 } 2918 2919 std::string dbusFilename{}; 2920 std::string dbusTimestamp{}; 2921 std::string dbusFilepath{}; 2922 2923 parseCrashdumpParameters(resp, dbusFilename, 2924 dbusTimestamp, dbusFilepath); 2925 2926 if (dbusFilename.empty() || dbusTimestamp.empty() || 2927 dbusFilepath.empty()) 2928 { 2929 messages::resourceNotFound(asyncResp->res, 2930 "LogEntry", logID); 2931 return; 2932 } 2933 2934 // Verify the file name parameter is correct 2935 if (fileName != dbusFilename) 2936 { 2937 messages::resourceNotFound(asyncResp->res, 2938 "LogEntry", logID); 2939 return; 2940 } 2941 2942 if (asyncResp->res.openFile(dbusFilepath) != 2943 crow::OpenCode::Success) 2944 { 2945 messages::resourceNotFound(asyncResp->res, 2946 "LogEntry", logID); 2947 return; 2948 } 2949 2950 // Configure this to be a file download when accessed 2951 // from a browser 2952 asyncResp->res.addHeader( 2953 boost::beast::http::field::content_disposition, 2954 "attachment"); 2955 }; 2956 dbus::utility::getAllProperties( 2957 *crow::connections::systemBus, crashdumpObject, 2958 crashdumpPath + std::string("/") + logID, 2959 crashdumpInterface, std::move(getStoredLogCallback)); 2960 }); 2961 } 2962 2963 enum class OEMDiagnosticType 2964 { 2965 onDemand, 2966 telemetry, 2967 invalid, 2968 }; 2969 2970 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr) 2971 { 2972 if (oemDiagStr == "OnDemand") 2973 { 2974 return OEMDiagnosticType::onDemand; 2975 } 2976 if (oemDiagStr == "Telemetry") 2977 { 2978 return OEMDiagnosticType::telemetry; 2979 } 2980 2981 return OEMDiagnosticType::invalid; 2982 } 2983 2984 inline void requestRoutesCrashdumpCollect(App& app) 2985 { 2986 // Note: Deviated from redfish privilege registry for GET & HEAD 2987 // method for security reasons. 2988 BMCWEB_ROUTE( 2989 app, 2990 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/") 2991 // The below is incorrect; Should be ConfigureManager 2992 //.privileges(redfish::privileges::postLogService) 2993 .privileges({{"ConfigureComponents"}}) 2994 .methods(boost::beast::http::verb::post)( 2995 [&app](const crow::Request& req, 2996 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2997 const std::string& systemName) { 2998 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2999 { 3000 return; 3001 } 3002 3003 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 3004 { 3005 // Option currently returns no systems. TBD 3006 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3007 systemName); 3008 return; 3009 } 3010 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 3011 { 3012 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3013 systemName); 3014 return; 3015 } 3016 3017 std::string diagnosticDataType; 3018 std::string oemDiagnosticDataType; 3019 if (!redfish::json_util::readJsonAction( // 3020 req, asyncResp->res, // 3021 "DiagnosticDataType", diagnosticDataType, // 3022 "OEMDiagnosticDataType", oemDiagnosticDataType // 3023 )) 3024 { 3025 return; 3026 } 3027 3028 if (diagnosticDataType != "OEM") 3029 { 3030 BMCWEB_LOG_ERROR( 3031 "Only OEM DiagnosticDataType supported for Crashdump"); 3032 messages::actionParameterValueFormatError( 3033 asyncResp->res, diagnosticDataType, 3034 "DiagnosticDataType", "CollectDiagnosticData"); 3035 return; 3036 } 3037 3038 OEMDiagnosticType oemDiagType = 3039 getOEMDiagnosticType(oemDiagnosticDataType); 3040 3041 std::string iface; 3042 std::string method; 3043 std::string taskMatchStr; 3044 if (oemDiagType == OEMDiagnosticType::onDemand) 3045 { 3046 iface = crashdumpOnDemandInterface; 3047 method = "GenerateOnDemandLog"; 3048 taskMatchStr = 3049 "type='signal'," 3050 "interface='org.freedesktop.DBus.Properties'," 3051 "member='PropertiesChanged'," 3052 "arg0namespace='com.intel.crashdump'"; 3053 } 3054 else if (oemDiagType == OEMDiagnosticType::telemetry) 3055 { 3056 iface = crashdumpTelemetryInterface; 3057 method = "GenerateTelemetryLog"; 3058 taskMatchStr = 3059 "type='signal'," 3060 "interface='org.freedesktop.DBus.Properties'," 3061 "member='PropertiesChanged'," 3062 "arg0namespace='com.intel.crashdump'"; 3063 } 3064 else 3065 { 3066 BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}", 3067 oemDiagnosticDataType); 3068 messages::actionParameterValueFormatError( 3069 asyncResp->res, oemDiagnosticDataType, 3070 "OEMDiagnosticDataType", "CollectDiagnosticData"); 3071 return; 3072 } 3073 3074 auto collectCrashdumpCallback = 3075 [asyncResp, payload(task::Payload(req)), 3076 taskMatchStr](const boost::system::error_code& ec, 3077 const std::string&) mutable { 3078 if (ec) 3079 { 3080 if (ec.value() == 3081 boost::system::errc::operation_not_supported) 3082 { 3083 messages::resourceInStandby(asyncResp->res); 3084 } 3085 else if (ec.value() == boost::system::errc:: 3086 device_or_resource_busy) 3087 { 3088 messages::serviceTemporarilyUnavailable( 3089 asyncResp->res, "60"); 3090 } 3091 else 3092 { 3093 messages::internalError(asyncResp->res); 3094 } 3095 return; 3096 } 3097 std::shared_ptr<task::TaskData> task = 3098 task::TaskData::createTask( 3099 [](const boost::system::error_code& ec2, 3100 sdbusplus::message_t&, 3101 const std::shared_ptr<task::TaskData>& 3102 taskData) { 3103 if (!ec2) 3104 { 3105 taskData->messages.emplace_back( 3106 messages::taskCompletedOK( 3107 std::to_string( 3108 taskData->index))); 3109 taskData->state = "Completed"; 3110 } 3111 return task::completed; 3112 }, 3113 taskMatchStr); 3114 3115 task->startTimer(std::chrono::minutes(5)); 3116 task->populateResp(asyncResp->res); 3117 task->payload.emplace(std::move(payload)); 3118 }; 3119 3120 dbus::utility::async_method_call( 3121 asyncResp, std::move(collectCrashdumpCallback), 3122 crashdumpObject, crashdumpPath, iface, method); 3123 }); 3124 } 3125 3126 inline void dBusLogServiceActionsClear( 3127 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 3128 { 3129 BMCWEB_LOG_DEBUG("Do delete all entries."); 3130 3131 // Process response from Logging service. 3132 auto respHandler = [asyncResp](const boost::system::error_code& ec) { 3133 BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done"); 3134 if (ec) 3135 { 3136 // TODO Handle for specific error code 3137 BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec); 3138 asyncResp->res.result( 3139 boost::beast::http::status::internal_server_error); 3140 return; 3141 } 3142 3143 messages::success(asyncResp->res); 3144 }; 3145 3146 // Make call to Logging service to request Clear Log 3147 dbus::utility::async_method_call( 3148 asyncResp, respHandler, "xyz.openbmc_project.Logging", 3149 "/xyz/openbmc_project/logging", 3150 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 3151 } 3152 3153 /** 3154 * DBusLogServiceActionsClear class supports POST method for ClearLog action. 3155 */ 3156 inline void requestRoutesDBusLogServiceActionsClear(App& app) 3157 { 3158 /** 3159 * Function handles POST method request. 3160 * The Clear Log actions does not require any parameter.The action deletes 3161 * all entries found in the Entries collection for this Log Service. 3162 */ 3163 3164 BMCWEB_ROUTE( 3165 app, 3166 "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/") 3167 .privileges(redfish::privileges::postLogService) 3168 .methods(boost::beast::http::verb::post)( 3169 [&app](const crow::Request& req, 3170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3171 const std::string& systemName) { 3172 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3173 { 3174 return; 3175 } 3176 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 3177 { 3178 // Option currently returns no systems. TBD 3179 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3180 systemName); 3181 return; 3182 } 3183 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 3184 { 3185 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3186 systemName); 3187 return; 3188 } 3189 dBusLogServiceActionsClear(asyncResp); 3190 }); 3191 } 3192 3193 } // namespace redfish 3194