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