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