1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4 #pragma once 5 6 #include "bmcweb_config.h" 7 8 #include "app.hpp" 9 #include "async_resp.hpp" 10 #include "dbus_singleton.hpp" 11 #include "dbus_utility.hpp" 12 #include "error_messages.hpp" 13 #include "generated/enums/log_entry.hpp" 14 #include "generated/enums/log_service.hpp" 15 #include "http_body.hpp" 16 #include "http_request.hpp" 17 #include "http_response.hpp" 18 #include "http_utility.hpp" 19 #include "human_sort.hpp" 20 #include "logging.hpp" 21 #include "query.hpp" 22 #include "registries.hpp" 23 #include "registries/privilege_registry.hpp" 24 #include "str_utility.hpp" 25 #include "task.hpp" 26 #include "task_messages.hpp" 27 #include "utils/dbus_event_log_entry.hpp" 28 #include "utils/dbus_utils.hpp" 29 #include "utils/json_utils.hpp" 30 #include "utils/query_param.hpp" 31 #include "utils/time_utils.hpp" 32 33 #include <asm-generic/errno.h> 34 #include <systemd/sd-bus.h> 35 #include <tinyxml2.h> 36 #include <unistd.h> 37 38 #include <boost/beast/http/field.hpp> 39 #include <boost/beast/http/status.hpp> 40 #include <boost/beast/http/verb.hpp> 41 #include <boost/system/linux_error.hpp> 42 #include <boost/url/format.hpp> 43 #include <boost/url/url.hpp> 44 #include <sdbusplus/message.hpp> 45 #include <sdbusplus/message/native_types.hpp> 46 #include <sdbusplus/unpack_properties.hpp> 47 48 #include <algorithm> 49 #include <array> 50 #include <chrono> 51 #include <cstddef> 52 #include <cstdint> 53 #include <cstdio> 54 #include <ctime> 55 #include <filesystem> 56 #include <format> 57 #include <fstream> 58 #include <functional> 59 #include <iomanip> 60 #include <iterator> 61 #include <memory> 62 #include <optional> 63 #include <ranges> 64 #include <span> 65 #include <sstream> 66 #include <string> 67 #include <string_view> 68 #include <system_error> 69 #include <utility> 70 #include <variant> 71 #include <vector> 72 73 namespace redfish 74 { 75 76 constexpr const char* crashdumpObject = "com.intel.crashdump"; 77 constexpr const char* crashdumpPath = "/com/intel/crashdump"; 78 constexpr const char* crashdumpInterface = "com.intel.crashdump"; 79 constexpr const char* deleteAllInterface = 80 "xyz.openbmc_project.Collection.DeleteAll"; 81 constexpr const char* crashdumpOnDemandInterface = 82 "com.intel.crashdump.OnDemand"; 83 constexpr const char* crashdumpTelemetryInterface = 84 "com.intel.crashdump.Telemetry"; 85 86 enum class DumpCreationProgress 87 { 88 DUMP_CREATE_SUCCESS, 89 DUMP_CREATE_FAILED, 90 DUMP_CREATE_INPROGRESS 91 }; 92 93 inline std::string translateSeverityDbusToRedfish(const std::string& s) 94 { 95 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") || 96 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") || 97 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") || 98 (s == "xyz.openbmc_project.Logging.Entry.Level.Error")) 99 { 100 return "Critical"; 101 } 102 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") || 103 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") || 104 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")) 105 { 106 return "OK"; 107 } 108 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") 109 { 110 return "Warning"; 111 } 112 return ""; 113 } 114 115 inline std::optional<bool> getProviderNotifyAction(const std::string& notify) 116 { 117 std::optional<bool> notifyAction; 118 if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Notify") 119 { 120 notifyAction = true; 121 } 122 else if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Inhibit") 123 { 124 notifyAction = false; 125 } 126 127 return notifyAction; 128 } 129 130 inline std::string getDumpPath(std::string_view dumpType) 131 { 132 std::string dbusDumpPath = "/xyz/openbmc_project/dump/"; 133 std::ranges::transform(dumpType, std::back_inserter(dbusDumpPath), 134 bmcweb::asciiToLower); 135 136 return dbusDumpPath; 137 } 138 139 inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, 140 const bool firstEntry = true) 141 { 142 static time_t prevTs = 0; 143 static int index = 0; 144 if (firstEntry) 145 { 146 prevTs = 0; 147 } 148 149 // Get the entry timestamp 150 std::time_t curTs = 0; 151 std::tm timeStruct = {}; 152 std::istringstream entryStream(logEntry); 153 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 154 { 155 curTs = std::mktime(&timeStruct); 156 } 157 // If the timestamp isn't unique, increment the index 158 if (curTs == prevTs) 159 { 160 index++; 161 } 162 else 163 { 164 // Otherwise, reset it 165 index = 0; 166 } 167 // Save the timestamp 168 prevTs = curTs; 169 170 entryID = std::to_string(curTs); 171 if (index > 0) 172 { 173 entryID += "_" + std::to_string(index); 174 } 175 return true; 176 } 177 178 inline bool getRedfishLogFiles( 179 std::vector<std::filesystem::path>& redfishLogFiles) 180 { 181 static const std::filesystem::path redfishLogDir = "/var/log"; 182 static const std::string redfishLogFilename = "redfish"; 183 184 // Loop through the directory looking for redfish log files 185 for (const std::filesystem::directory_entry& dirEnt : 186 std::filesystem::directory_iterator(redfishLogDir)) 187 { 188 // If we find a redfish log file, save the path 189 std::string filename = dirEnt.path().filename(); 190 if (filename.starts_with(redfishLogFilename)) 191 { 192 redfishLogFiles.emplace_back(redfishLogDir / filename); 193 } 194 } 195 // As the log files rotate, they are appended with a ".#" that is higher for 196 // the older logs. Since we don't expect more than 10 log files, we 197 // can just sort the list to get them in order from newest to oldest 198 std::ranges::sort(redfishLogFiles); 199 200 return !redfishLogFiles.empty(); 201 } 202 203 inline log_entry::OriginatorTypes mapDbusOriginatorTypeToRedfish( 204 const std::string& originatorType) 205 { 206 if (originatorType == 207 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client") 208 { 209 return log_entry::OriginatorTypes::Client; 210 } 211 if (originatorType == 212 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Internal") 213 { 214 return log_entry::OriginatorTypes::Internal; 215 } 216 if (originatorType == 217 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.SupportingService") 218 { 219 return log_entry::OriginatorTypes::SupportingService; 220 } 221 return log_entry::OriginatorTypes::Invalid; 222 } 223 224 inline void parseDumpEntryFromDbusObject( 225 const dbus::utility::ManagedObjectType::value_type& object, 226 std::string& dumpStatus, uint64_t& size, uint64_t& timestampUs, 227 std::string& originatorId, log_entry::OriginatorTypes& originatorType, 228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 229 { 230 for (const auto& interfaceMap : object.second) 231 { 232 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress") 233 { 234 for (const auto& propertyMap : interfaceMap.second) 235 { 236 if (propertyMap.first == "Status") 237 { 238 const auto* status = 239 std::get_if<std::string>(&propertyMap.second); 240 if (status == nullptr) 241 { 242 messages::internalError(asyncResp->res); 243 break; 244 } 245 dumpStatus = *status; 246 } 247 } 248 } 249 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry") 250 { 251 for (const auto& propertyMap : interfaceMap.second) 252 { 253 if (propertyMap.first == "Size") 254 { 255 const auto* sizePtr = 256 std::get_if<uint64_t>(&propertyMap.second); 257 if (sizePtr == nullptr) 258 { 259 messages::internalError(asyncResp->res); 260 break; 261 } 262 size = *sizePtr; 263 break; 264 } 265 } 266 } 267 else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime") 268 { 269 for (const auto& propertyMap : interfaceMap.second) 270 { 271 if (propertyMap.first == "Elapsed") 272 { 273 const uint64_t* usecsTimeStamp = 274 std::get_if<uint64_t>(&propertyMap.second); 275 if (usecsTimeStamp == nullptr) 276 { 277 messages::internalError(asyncResp->res); 278 break; 279 } 280 timestampUs = *usecsTimeStamp; 281 break; 282 } 283 } 284 } 285 else if (interfaceMap.first == 286 "xyz.openbmc_project.Common.OriginatedBy") 287 { 288 for (const auto& propertyMap : interfaceMap.second) 289 { 290 if (propertyMap.first == "OriginatorId") 291 { 292 const std::string* id = 293 std::get_if<std::string>(&propertyMap.second); 294 if (id == nullptr) 295 { 296 messages::internalError(asyncResp->res); 297 break; 298 } 299 originatorId = *id; 300 } 301 302 if (propertyMap.first == "OriginatorType") 303 { 304 const std::string* type = 305 std::get_if<std::string>(&propertyMap.second); 306 if (type == nullptr) 307 { 308 messages::internalError(asyncResp->res); 309 break; 310 } 311 312 originatorType = mapDbusOriginatorTypeToRedfish(*type); 313 if (originatorType == log_entry::OriginatorTypes::Invalid) 314 { 315 messages::internalError(asyncResp->res); 316 break; 317 } 318 } 319 } 320 } 321 } 322 } 323 324 static std::string getDumpEntriesPath(const std::string& dumpType) 325 { 326 std::string entriesPath; 327 328 if (dumpType == "BMC") 329 { 330 entriesPath = 331 std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/", 332 BMCWEB_REDFISH_MANAGER_URI_NAME); 333 } 334 else if (dumpType == "FaultLog") 335 { 336 entriesPath = 337 std::format("/redfish/v1/Managers/{}/LogServices/FaultLog/Entries/", 338 BMCWEB_REDFISH_MANAGER_URI_NAME); 339 } 340 else if (dumpType == "System") 341 { 342 entriesPath = 343 std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/", 344 BMCWEB_REDFISH_SYSTEM_URI_NAME); 345 } 346 else 347 { 348 BMCWEB_LOG_ERROR("getDumpEntriesPath() invalid dump type: {}", 349 dumpType); 350 } 351 352 // Returns empty string on error 353 return entriesPath; 354 } 355 356 inline void getDumpEntryCollection( 357 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 358 const std::string& dumpType) 359 { 360 std::string entriesPath = getDumpEntriesPath(dumpType); 361 if (entriesPath.empty()) 362 { 363 messages::internalError(asyncResp->res); 364 return; 365 } 366 367 sdbusplus::message::object_path path("/xyz/openbmc_project/dump"); 368 dbus::utility::getManagedObjects( 369 "xyz.openbmc_project.Dump.Manager", path, 370 [asyncResp, entriesPath, 371 dumpType](const boost::system::error_code& ec, 372 const dbus::utility::ManagedObjectType& objects) { 373 if (ec) 374 { 375 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); 376 messages::internalError(asyncResp->res); 377 return; 378 } 379 380 // Remove ending slash 381 std::string odataIdStr = entriesPath; 382 if (!odataIdStr.empty()) 383 { 384 odataIdStr.pop_back(); 385 } 386 387 asyncResp->res.jsonValue["@odata.type"] = 388 "#LogEntryCollection.LogEntryCollection"; 389 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr); 390 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries"; 391 asyncResp->res.jsonValue["Description"] = 392 "Collection of " + dumpType + " Dump Entries"; 393 394 nlohmann::json::array_t entriesArray; 395 std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; 396 397 dbus::utility::ManagedObjectType resp(objects); 398 std::ranges::sort(resp, [](const auto& l, const auto& r) { 399 return AlphanumLess<std::string>()(l.first.filename(), 400 r.first.filename()); 401 }); 402 403 for (auto& object : resp) 404 { 405 if (object.first.str.find(dumpEntryPath) == std::string::npos) 406 { 407 continue; 408 } 409 uint64_t timestampUs = 0; 410 uint64_t size = 0; 411 std::string dumpStatus; 412 std::string originatorId; 413 log_entry::OriginatorTypes originatorType = 414 log_entry::OriginatorTypes::Internal; 415 nlohmann::json::object_t thisEntry; 416 417 std::string entryID = object.first.filename(); 418 if (entryID.empty()) 419 { 420 continue; 421 } 422 423 parseDumpEntryFromDbusObject(object, dumpStatus, size, 424 timestampUs, originatorId, 425 originatorType, asyncResp); 426 427 if (dumpStatus != 428 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && 429 !dumpStatus.empty()) 430 { 431 // Dump status is not Complete, no need to enumerate 432 continue; 433 } 434 435 thisEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; 436 thisEntry["@odata.id"] = entriesPath + entryID; 437 thisEntry["Id"] = entryID; 438 thisEntry["EntryType"] = "Event"; 439 thisEntry["Name"] = dumpType + " Dump Entry"; 440 thisEntry["Created"] = 441 redfish::time_utils::getDateTimeUintUs(timestampUs); 442 443 if (!originatorId.empty()) 444 { 445 thisEntry["Originator"] = originatorId; 446 thisEntry["OriginatorType"] = originatorType; 447 } 448 449 if (dumpType == "BMC") 450 { 451 thisEntry["DiagnosticDataType"] = "Manager"; 452 thisEntry["AdditionalDataURI"] = 453 entriesPath + entryID + "/attachment"; 454 thisEntry["AdditionalDataSizeBytes"] = size; 455 } 456 else if (dumpType == "System") 457 { 458 thisEntry["DiagnosticDataType"] = "OEM"; 459 thisEntry["OEMDiagnosticDataType"] = "System"; 460 thisEntry["AdditionalDataURI"] = 461 entriesPath + entryID + "/attachment"; 462 thisEntry["AdditionalDataSizeBytes"] = size; 463 } 464 entriesArray.emplace_back(std::move(thisEntry)); 465 } 466 asyncResp->res.jsonValue["Members@odata.count"] = 467 entriesArray.size(); 468 asyncResp->res.jsonValue["Members"] = std::move(entriesArray); 469 }); 470 } 471 472 inline void getDumpEntryById( 473 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 474 const std::string& entryID, const std::string& dumpType) 475 { 476 std::string entriesPath = getDumpEntriesPath(dumpType); 477 if (entriesPath.empty()) 478 { 479 messages::internalError(asyncResp->res); 480 return; 481 } 482 483 sdbusplus::message::object_path path("/xyz/openbmc_project/dump"); 484 dbus::utility::getManagedObjects( 485 "xyz.openbmc_project.Dump.Manager", path, 486 [asyncResp, entryID, dumpType, 487 entriesPath](const boost::system::error_code& ec, 488 const dbus::utility::ManagedObjectType& resp) { 489 if (ec) 490 { 491 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); 492 messages::internalError(asyncResp->res); 493 return; 494 } 495 496 bool foundDumpEntry = false; 497 std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; 498 499 for (const auto& objectPath : resp) 500 { 501 if (objectPath.first.str != dumpEntryPath + entryID) 502 { 503 continue; 504 } 505 506 foundDumpEntry = true; 507 uint64_t timestampUs = 0; 508 uint64_t size = 0; 509 std::string dumpStatus; 510 std::string originatorId; 511 log_entry::OriginatorTypes originatorType = 512 log_entry::OriginatorTypes::Internal; 513 514 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size, 515 timestampUs, originatorId, 516 originatorType, asyncResp); 517 518 if (dumpStatus != 519 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && 520 !dumpStatus.empty()) 521 { 522 // Dump status is not Complete 523 // return not found until status is changed to Completed 524 messages::resourceNotFound(asyncResp->res, 525 dumpType + " dump", entryID); 526 return; 527 } 528 529 asyncResp->res.jsonValue["@odata.type"] = 530 "#LogEntry.v1_11_0.LogEntry"; 531 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID; 532 asyncResp->res.jsonValue["Id"] = entryID; 533 asyncResp->res.jsonValue["EntryType"] = "Event"; 534 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry"; 535 asyncResp->res.jsonValue["Created"] = 536 redfish::time_utils::getDateTimeUintUs(timestampUs); 537 538 if (!originatorId.empty()) 539 { 540 asyncResp->res.jsonValue["Originator"] = originatorId; 541 asyncResp->res.jsonValue["OriginatorType"] = originatorType; 542 } 543 544 if (dumpType == "BMC") 545 { 546 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager"; 547 asyncResp->res.jsonValue["AdditionalDataURI"] = 548 entriesPath + entryID + "/attachment"; 549 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; 550 } 551 else if (dumpType == "System") 552 { 553 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM"; 554 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = 555 "System"; 556 asyncResp->res.jsonValue["AdditionalDataURI"] = 557 entriesPath + entryID + "/attachment"; 558 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; 559 } 560 } 561 if (!foundDumpEntry) 562 { 563 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); 564 messages::resourceNotFound(asyncResp->res, dumpType + " dump", 565 entryID); 566 return; 567 } 568 }); 569 } 570 571 inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 572 const std::string& entryID, 573 const std::string& dumpType) 574 { 575 auto respHandler = [asyncResp, 576 entryID](const boost::system::error_code& ec) { 577 BMCWEB_LOG_DEBUG("Dump Entry doDelete callback: Done"); 578 if (ec) 579 { 580 if (ec.value() == EBADR) 581 { 582 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 583 return; 584 } 585 BMCWEB_LOG_ERROR( 586 "Dump (DBus) doDelete respHandler got error {} entryID={}", ec, 587 entryID); 588 messages::internalError(asyncResp->res); 589 return; 590 } 591 }; 592 593 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 downloadDumpEntry( 689 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 690 const std::string& entryID, const std::string& dumpType) 691 { 692 if (dumpType != "BMC") 693 { 694 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); 695 messages::resourceNotFound(asyncResp->res, dumpType + " dump", entryID); 696 return; 697 } 698 699 std::string dumpEntryPath = 700 std::format("{}/entry/{}", getDumpPath(dumpType), entryID); 701 702 auto downloadDumpEntryHandler = 703 [asyncResp, entryID, 704 dumpType](const boost::system::error_code& ec, 705 const sdbusplus::message::unix_fd& unixfd) { 706 downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); 707 }; 708 709 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 mapDbusStatusToDumpProgress( 750 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 getDumpCompletionStatus( 767 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 bool 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 false; 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 return true; 1476 } 1477 1478 inline void afterLogEntriesGetManagedObjects( 1479 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1480 const boost::system::error_code& ec, 1481 const dbus::utility::ManagedObjectType& resp) 1482 { 1483 if (ec) 1484 { 1485 // TODO Handle for specific error code 1486 BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}", 1487 ec); 1488 messages::internalError(asyncResp->res); 1489 return; 1490 } 1491 nlohmann::json::array_t entriesArray; 1492 for (const auto& objectPath : resp) 1493 { 1494 dbus::utility::DBusPropertiesMap propsFlattened; 1495 auto isEntry = 1496 std::ranges::find_if(objectPath.second, [](const auto& object) { 1497 return object.first == "xyz.openbmc_project.Logging.Entry"; 1498 }); 1499 if (isEntry == objectPath.second.end()) 1500 { 1501 continue; 1502 } 1503 for (const auto& interfaceMap : objectPath.second) 1504 { 1505 for (const auto& propertyMap : interfaceMap.second) 1506 { 1507 propsFlattened.emplace_back(propertyMap.first, 1508 propertyMap.second); 1509 } 1510 } 1511 bool success = fillEventLogLogEntryFromPropertyMap( 1512 asyncResp, propsFlattened, entriesArray.emplace_back()); 1513 if (!success) 1514 { 1515 return; 1516 } 1517 } 1518 1519 redfish::json_util::sortJsonArrayByKey(entriesArray, "Id"); 1520 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size(); 1521 asyncResp->res.jsonValue["Members"] = std::move(entriesArray); 1522 } 1523 1524 inline void handleSystemsLogServiceEventLogLogEntryCollection( 1525 App& app, const crow::Request& req, 1526 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1527 const std::string& systemName) 1528 { 1529 query_param::QueryCapabilities capabilities = { 1530 .canDelegateTop = true, 1531 .canDelegateSkip = true, 1532 }; 1533 query_param::Query delegatedQuery; 1534 if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, 1535 delegatedQuery, capabilities)) 1536 { 1537 return; 1538 } 1539 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1540 { 1541 // Option currently returns no systems. TBD 1542 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1543 systemName); 1544 return; 1545 } 1546 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1547 { 1548 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1549 systemName); 1550 return; 1551 } 1552 1553 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); 1554 size_t skip = delegatedQuery.skip.value_or(0); 1555 1556 // Collections don't include the static data added by SubRoute 1557 // because it has a duplicate entry for members 1558 asyncResp->res.jsonValue["@odata.type"] = 1559 "#LogEntryCollection.LogEntryCollection"; 1560 asyncResp->res.jsonValue["@odata.id"] = 1561 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", 1562 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1563 asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 1564 asyncResp->res.jsonValue["Description"] = 1565 "Collection of System Event Log Entries"; 1566 1567 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; 1568 logEntryArray = nlohmann::json::array(); 1569 // Go through the log files and create a unique ID for each 1570 // entry 1571 std::vector<std::filesystem::path> redfishLogFiles; 1572 getRedfishLogFiles(redfishLogFiles); 1573 uint64_t entryCount = 0; 1574 std::string logEntry; 1575 1576 // Oldest logs are in the last file, so start there and loop 1577 // backwards 1578 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) 1579 { 1580 std::ifstream logStream(*it); 1581 if (!logStream.is_open()) 1582 { 1583 continue; 1584 } 1585 1586 // Reset the unique ID on the first entry 1587 bool firstEntry = true; 1588 while (std::getline(logStream, logEntry)) 1589 { 1590 std::string idStr; 1591 if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 1592 { 1593 continue; 1594 } 1595 firstEntry = false; 1596 1597 nlohmann::json::object_t bmcLogEntry; 1598 LogParseError status = 1599 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); 1600 if (status == LogParseError::messageIdNotInRegistry) 1601 { 1602 continue; 1603 } 1604 if (status != LogParseError::success) 1605 { 1606 messages::internalError(asyncResp->res); 1607 return; 1608 } 1609 1610 entryCount++; 1611 // Handle paging using skip (number of entries to skip from the 1612 // start) and top (number of entries to display) 1613 if (entryCount <= skip || entryCount > skip + top) 1614 { 1615 continue; 1616 } 1617 1618 logEntryArray.emplace_back(std::move(bmcLogEntry)); 1619 } 1620 } 1621 asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1622 if (skip + top < entryCount) 1623 { 1624 asyncResp->res.jsonValue["Members@odata.nextLink"] = 1625 boost::urls::format( 1626 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries?$skip={}", 1627 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(skip + top)); 1628 } 1629 } 1630 1631 inline void requestRoutesJournalEventLogEntryCollection(App& app) 1632 { 1633 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/") 1634 .privileges(redfish::privileges::getLogEntryCollection) 1635 .methods(boost::beast::http::verb::get)(std::bind_front( 1636 handleSystemsLogServiceEventLogLogEntryCollection, std::ref(app))); 1637 } 1638 1639 inline void handleSystemsLogServiceEventLogEntriesGet( 1640 App& app, const crow::Request& req, 1641 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1642 const std::string& systemName, const std::string& param) 1643 { 1644 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1645 { 1646 return; 1647 } 1648 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1649 { 1650 // Option currently returns no systems. TBD 1651 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1652 systemName); 1653 return; 1654 } 1655 1656 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1657 { 1658 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1659 systemName); 1660 return; 1661 } 1662 1663 const std::string& targetID = param; 1664 1665 // Go through the log files and check the unique ID for each 1666 // entry to find the target entry 1667 std::vector<std::filesystem::path> redfishLogFiles; 1668 getRedfishLogFiles(redfishLogFiles); 1669 std::string logEntry; 1670 1671 // Oldest logs are in the last file, so start there and loop 1672 // backwards 1673 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) 1674 { 1675 std::ifstream logStream(*it); 1676 if (!logStream.is_open()) 1677 { 1678 continue; 1679 } 1680 1681 // Reset the unique ID on the first entry 1682 bool firstEntry = true; 1683 while (std::getline(logStream, logEntry)) 1684 { 1685 std::string idStr; 1686 if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 1687 { 1688 continue; 1689 } 1690 firstEntry = false; 1691 1692 if (idStr == targetID) 1693 { 1694 nlohmann::json::object_t bmcLogEntry; 1695 LogParseError status = 1696 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); 1697 if (status != LogParseError::success) 1698 { 1699 messages::internalError(asyncResp->res); 1700 return; 1701 } 1702 asyncResp->res.jsonValue.update(bmcLogEntry); 1703 return; 1704 } 1705 } 1706 } 1707 // Requested ID was not found 1708 messages::resourceNotFound(asyncResp->res, "LogEntry", targetID); 1709 } 1710 1711 inline void requestRoutesJournalEventLogEntry(App& app) 1712 { 1713 BMCWEB_ROUTE( 1714 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1715 .privileges(redfish::privileges::getLogEntry) 1716 .methods(boost::beast::http::verb::get)(std::bind_front( 1717 handleSystemsLogServiceEventLogEntriesGet, std::ref(app))); 1718 } 1719 1720 inline void dBusEventLogEntryCollection( 1721 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1722 { 1723 // Collections don't include the static data added by SubRoute 1724 // because it has a duplicate entry for members 1725 asyncResp->res.jsonValue["@odata.type"] = 1726 "#LogEntryCollection.LogEntryCollection"; 1727 asyncResp->res.jsonValue["@odata.id"] = 1728 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", 1729 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1730 asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 1731 asyncResp->res.jsonValue["Description"] = 1732 "Collection of System Event Log Entries"; 1733 1734 // DBus implementation of EventLog/Entries 1735 // Make call to Logging Service to find all log entry objects 1736 sdbusplus::message::object_path path("/xyz/openbmc_project/logging"); 1737 dbus::utility::getManagedObjects( 1738 "xyz.openbmc_project.Logging", path, 1739 [asyncResp](const boost::system::error_code& ec, 1740 const dbus::utility::ManagedObjectType& resp) { 1741 afterLogEntriesGetManagedObjects(asyncResp, ec, resp); 1742 }); 1743 } 1744 1745 inline void requestRoutesDBusEventLogEntryCollection(App& app) 1746 { 1747 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/") 1748 .privileges(redfish::privileges::getLogEntryCollection) 1749 .methods(boost::beast::http::verb::get)( 1750 [&app](const crow::Request& req, 1751 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1752 const std::string& systemName) { 1753 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1754 { 1755 return; 1756 } 1757 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1758 { 1759 // Option currently returns no systems. TBD 1760 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1761 systemName); 1762 return; 1763 } 1764 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1765 { 1766 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1767 systemName); 1768 return; 1769 } 1770 dBusEventLogEntryCollection(asyncResp); 1771 }); 1772 } 1773 1774 inline void dBusEventLogEntryGet( 1775 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID) 1776 { 1777 dbus::utility::escapePathForDbus(entryID); 1778 1779 // DBus implementation of EventLog/Entries 1780 // Make call to Logging Service to find all log entry objects 1781 dbus::utility::getAllProperties( 1782 "xyz.openbmc_project.Logging", 1783 "/xyz/openbmc_project/logging/entry/" + entryID, "", 1784 [asyncResp, entryID](const boost::system::error_code& ec, 1785 const dbus::utility::DBusPropertiesMap& resp) { 1786 if (ec.value() == EBADR) 1787 { 1788 messages::resourceNotFound(asyncResp->res, "EventLogEntry", 1789 entryID); 1790 return; 1791 } 1792 if (ec) 1793 { 1794 BMCWEB_LOG_ERROR( 1795 "EventLogEntry (DBus) resp_handler got error {}", ec); 1796 messages::internalError(asyncResp->res); 1797 return; 1798 } 1799 1800 fillEventLogLogEntryFromPropertyMap(asyncResp, resp, 1801 asyncResp->res.jsonValue); 1802 }); 1803 } 1804 1805 inline void dBusEventLogEntryPatch( 1806 const crow::Request& req, 1807 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1808 const std::string& entryId) 1809 { 1810 std::optional<bool> resolved; 1811 1812 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved)) 1813 { 1814 return; 1815 } 1816 BMCWEB_LOG_DEBUG("Set Resolved"); 1817 1818 setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging", 1819 "/xyz/openbmc_project/logging/entry/" + entryId, 1820 "xyz.openbmc_project.Logging.Entry", "Resolved", 1821 resolved.value_or(false)); 1822 } 1823 1824 inline void dBusEventLogEntryDelete( 1825 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID) 1826 { 1827 BMCWEB_LOG_DEBUG("Do delete single event entries."); 1828 1829 dbus::utility::escapePathForDbus(entryID); 1830 1831 // Process response from Logging service. 1832 auto respHandler = [asyncResp, 1833 entryID](const boost::system::error_code& ec) { 1834 BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done"); 1835 if (ec) 1836 { 1837 if (ec.value() == EBADR) 1838 { 1839 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); 1840 return; 1841 } 1842 // TODO Handle for specific error code 1843 BMCWEB_LOG_ERROR( 1844 "EventLogEntry (DBus) doDelete respHandler got error {}", ec); 1845 asyncResp->res.result( 1846 boost::beast::http::status::internal_server_error); 1847 return; 1848 } 1849 1850 asyncResp->res.result(boost::beast::http::status::ok); 1851 }; 1852 1853 // Make call to Logging service to request Delete Log 1854 crow::connections::systemBus->async_method_call( 1855 respHandler, "xyz.openbmc_project.Logging", 1856 "/xyz/openbmc_project/logging/entry/" + entryID, 1857 "xyz.openbmc_project.Object.Delete", "Delete"); 1858 } 1859 1860 inline void requestRoutesDBusEventLogEntry(App& app) 1861 { 1862 BMCWEB_ROUTE( 1863 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1864 .privileges(redfish::privileges::getLogEntry) 1865 .methods(boost::beast::http::verb::get)( 1866 [&app](const crow::Request& req, 1867 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1868 const std::string& systemName, const std::string& entryId) { 1869 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1870 { 1871 return; 1872 } 1873 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1874 { 1875 // Option currently returns no systems. TBD 1876 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1877 systemName); 1878 return; 1879 } 1880 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1881 { 1882 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1883 systemName); 1884 return; 1885 } 1886 1887 dBusEventLogEntryGet(asyncResp, entryId); 1888 }); 1889 1890 BMCWEB_ROUTE( 1891 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1892 .privileges(redfish::privileges::patchLogEntry) 1893 .methods(boost::beast::http::verb::patch)( 1894 [&app](const crow::Request& req, 1895 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1896 const std::string& systemName, const std::string& entryId) { 1897 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1898 { 1899 return; 1900 } 1901 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1902 { 1903 // Option currently returns no systems. TBD 1904 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1905 systemName); 1906 return; 1907 } 1908 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1909 { 1910 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1911 systemName); 1912 return; 1913 } 1914 1915 dBusEventLogEntryPatch(req, asyncResp, entryId); 1916 }); 1917 1918 BMCWEB_ROUTE( 1919 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") 1920 .privileges(redfish::privileges::deleteLogEntry) 1921 1922 .methods(boost::beast::http::verb::delete_)( 1923 [&app](const crow::Request& req, 1924 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1925 const std::string& systemName, const std::string& param) { 1926 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1927 { 1928 return; 1929 } 1930 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1931 { 1932 // Option currently returns no systems. TBD 1933 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1934 systemName); 1935 return; 1936 } 1937 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1938 { 1939 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1940 systemName); 1941 return; 1942 } 1943 dBusEventLogEntryDelete(asyncResp, param); 1944 }); 1945 } 1946 1947 inline void handleBMCLogServicesCollectionGet( 1948 crow::App& app, const crow::Request& req, 1949 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1950 const std::string& managerId) 1951 { 1952 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1953 { 1954 return; 1955 } 1956 1957 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1958 { 1959 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1960 return; 1961 } 1962 1963 // Collections don't include the static data added by SubRoute 1964 // because it has a duplicate entry for members 1965 asyncResp->res.jsonValue["@odata.type"] = 1966 "#LogServiceCollection.LogServiceCollection"; 1967 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1968 "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME); 1969 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 1970 asyncResp->res.jsonValue["Description"] = 1971 "Collection of LogServices for this Manager"; 1972 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"]; 1973 logServiceArray = nlohmann::json::array(); 1974 1975 if constexpr (BMCWEB_REDFISH_BMC_JOURNAL) 1976 { 1977 nlohmann::json::object_t journal; 1978 journal["@odata.id"] = 1979 boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal", 1980 BMCWEB_REDFISH_MANAGER_URI_NAME); 1981 logServiceArray.emplace_back(std::move(journal)); 1982 } 1983 1984 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size(); 1985 1986 if constexpr (BMCWEB_REDFISH_DUMP_LOG) 1987 { 1988 constexpr std::array<std::string_view, 1> interfaces = { 1989 "xyz.openbmc_project.Collection.DeleteAll"}; 1990 dbus::utility::getSubTreePaths( 1991 "/xyz/openbmc_project/dump", 0, interfaces, 1992 [asyncResp](const boost::system::error_code& ec, 1993 const dbus::utility::MapperGetSubTreePathsResponse& 1994 subTreePaths) { 1995 if (ec) 1996 { 1997 BMCWEB_LOG_ERROR( 1998 "handleBMCLogServicesCollectionGet respHandler got error {}", 1999 ec); 2000 // Assume that getting an error simply means there are no 2001 // dump LogServices. Return without adding any error 2002 // response. 2003 return; 2004 } 2005 2006 nlohmann::json& logServiceArrayLocal = 2007 asyncResp->res.jsonValue["Members"]; 2008 2009 for (const std::string& path : subTreePaths) 2010 { 2011 if (path == "/xyz/openbmc_project/dump/bmc") 2012 { 2013 nlohmann::json::object_t member; 2014 member["@odata.id"] = boost::urls::format( 2015 "/redfish/v1/Managers/{}/LogServices/Dump", 2016 BMCWEB_REDFISH_MANAGER_URI_NAME); 2017 logServiceArrayLocal.emplace_back(std::move(member)); 2018 } 2019 else if (path == "/xyz/openbmc_project/dump/faultlog") 2020 { 2021 nlohmann::json::object_t member; 2022 member["@odata.id"] = boost::urls::format( 2023 "/redfish/v1/Managers/{}/LogServices/FaultLog", 2024 BMCWEB_REDFISH_MANAGER_URI_NAME); 2025 logServiceArrayLocal.emplace_back(std::move(member)); 2026 } 2027 } 2028 2029 asyncResp->res.jsonValue["Members@odata.count"] = 2030 logServiceArrayLocal.size(); 2031 }); 2032 } 2033 } 2034 2035 inline void requestRoutesBMCLogServiceCollection(App& app) 2036 { 2037 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/") 2038 .privileges(redfish::privileges::getLogServiceCollection) 2039 .methods(boost::beast::http::verb::get)( 2040 std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app))); 2041 } 2042 2043 inline void getDumpServiceInfo( 2044 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2045 const std::string& dumpType) 2046 { 2047 std::string dumpPath; 2048 log_service::OverWritePolicy overWritePolicy = 2049 log_service::OverWritePolicy::Invalid; 2050 bool collectDiagnosticDataSupported = false; 2051 2052 if (dumpType == "BMC") 2053 { 2054 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump", 2055 BMCWEB_REDFISH_MANAGER_URI_NAME); 2056 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; 2057 collectDiagnosticDataSupported = true; 2058 } 2059 else if (dumpType == "FaultLog") 2060 { 2061 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog", 2062 BMCWEB_REDFISH_MANAGER_URI_NAME); 2063 overWritePolicy = log_service::OverWritePolicy::Unknown; 2064 collectDiagnosticDataSupported = false; 2065 } 2066 else if (dumpType == "System") 2067 { 2068 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump", 2069 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2070 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; 2071 collectDiagnosticDataSupported = true; 2072 } 2073 else 2074 { 2075 BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}", 2076 dumpType); 2077 messages::internalError(asyncResp->res); 2078 return; 2079 } 2080 2081 asyncResp->res.jsonValue["@odata.id"] = dumpPath; 2082 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 2083 asyncResp->res.jsonValue["Name"] = "Dump LogService"; 2084 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService"; 2085 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename(); 2086 asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy; 2087 2088 std::pair<std::string, std::string> redfishDateTimeOffset = 2089 redfish::time_utils::getDateTimeOffsetNow(); 2090 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 2091 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 2092 redfishDateTimeOffset.second; 2093 2094 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries"; 2095 2096 if (collectDiagnosticDataSupported) 2097 { 2098 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"] 2099 ["target"] = 2100 dumpPath + "/Actions/LogService.CollectDiagnosticData"; 2101 } 2102 2103 constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface}; 2104 dbus::utility::getSubTreePaths( 2105 "/xyz/openbmc_project/dump", 0, interfaces, 2106 [asyncResp, dumpType, dumpPath]( 2107 const boost::system::error_code& ec, 2108 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) { 2109 if (ec) 2110 { 2111 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}", 2112 ec); 2113 // Assume that getting an error simply means there are no dump 2114 // LogServices. Return without adding any error response. 2115 return; 2116 } 2117 std::string dbusDumpPath = getDumpPath(dumpType); 2118 for (const std::string& path : subTreePaths) 2119 { 2120 if (path == dbusDumpPath) 2121 { 2122 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 2123 ["target"] = 2124 dumpPath + "/Actions/LogService.ClearLog"; 2125 break; 2126 } 2127 } 2128 }); 2129 } 2130 2131 inline void handleLogServicesDumpServiceGet( 2132 crow::App& app, const std::string& dumpType, const crow::Request& req, 2133 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2134 const std::string& managerId) 2135 { 2136 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2137 { 2138 return; 2139 } 2140 2141 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2142 { 2143 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2144 return; 2145 } 2146 2147 getDumpServiceInfo(asyncResp, dumpType); 2148 } 2149 2150 inline void handleLogServicesDumpServiceComputerSystemGet( 2151 crow::App& app, const crow::Request& req, 2152 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2153 const std::string& chassisId) 2154 { 2155 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2156 { 2157 return; 2158 } 2159 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2160 { 2161 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2162 return; 2163 } 2164 getDumpServiceInfo(asyncResp, "System"); 2165 } 2166 2167 inline void handleLogServicesDumpEntriesCollectionGet( 2168 crow::App& app, const std::string& dumpType, const crow::Request& req, 2169 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2170 const std::string& managerId) 2171 { 2172 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2173 { 2174 return; 2175 } 2176 2177 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2178 { 2179 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2180 return; 2181 } 2182 getDumpEntryCollection(asyncResp, dumpType); 2183 } 2184 2185 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet( 2186 crow::App& app, const crow::Request& req, 2187 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2188 const std::string& chassisId) 2189 { 2190 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2191 { 2192 return; 2193 } 2194 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2195 { 2196 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2197 return; 2198 } 2199 getDumpEntryCollection(asyncResp, "System"); 2200 } 2201 2202 inline void handleLogServicesDumpEntryGet( 2203 crow::App& app, const std::string& dumpType, const crow::Request& req, 2204 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2205 const std::string& managerId, const std::string& dumpId) 2206 { 2207 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2208 { 2209 return; 2210 } 2211 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2212 { 2213 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2214 return; 2215 } 2216 getDumpEntryById(asyncResp, dumpId, dumpType); 2217 } 2218 2219 inline void handleLogServicesDumpEntryComputerSystemGet( 2220 crow::App& app, const crow::Request& req, 2221 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2222 const std::string& chassisId, const std::string& dumpId) 2223 { 2224 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2225 { 2226 return; 2227 } 2228 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2229 { 2230 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2231 return; 2232 } 2233 getDumpEntryById(asyncResp, dumpId, "System"); 2234 } 2235 2236 inline void handleLogServicesDumpEntryDelete( 2237 crow::App& app, const std::string& dumpType, const crow::Request& req, 2238 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2239 const std::string& managerId, const std::string& dumpId) 2240 { 2241 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2242 { 2243 return; 2244 } 2245 2246 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2247 { 2248 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2249 return; 2250 } 2251 deleteDumpEntry(asyncResp, dumpId, dumpType); 2252 } 2253 2254 inline void handleLogServicesDumpEntryComputerSystemDelete( 2255 crow::App& app, const crow::Request& req, 2256 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2257 const std::string& chassisId, const std::string& dumpId) 2258 { 2259 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2260 { 2261 return; 2262 } 2263 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2264 { 2265 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId); 2266 return; 2267 } 2268 deleteDumpEntry(asyncResp, dumpId, "System"); 2269 } 2270 2271 inline void handleLogServicesDumpEntryDownloadGet( 2272 crow::App& app, const std::string& dumpType, const crow::Request& req, 2273 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2274 const std::string& managerId, const std::string& dumpId) 2275 { 2276 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2277 { 2278 return; 2279 } 2280 2281 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2282 { 2283 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2284 return; 2285 } 2286 downloadDumpEntry(asyncResp, dumpId, dumpType); 2287 } 2288 2289 inline void handleDBusEventLogEntryDownloadGet( 2290 crow::App& app, const std::string& dumpType, const crow::Request& req, 2291 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2292 const std::string& systemName, const std::string& entryID) 2293 { 2294 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2295 { 2296 return; 2297 } 2298 if (!http_helpers::isContentTypeAllowed( 2299 req.getHeaderValue("Accept"), 2300 http_helpers::ContentType::OctetStream, true)) 2301 { 2302 asyncResp->res.result(boost::beast::http::status::bad_request); 2303 return; 2304 } 2305 downloadEventLogEntry(asyncResp, systemName, entryID, dumpType); 2306 } 2307 2308 inline void handleLogServicesDumpCollectDiagnosticDataPost( 2309 crow::App& app, const std::string& dumpType, const crow::Request& req, 2310 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2311 const std::string& managerId) 2312 { 2313 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2314 { 2315 return; 2316 } 2317 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2318 { 2319 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2320 return; 2321 } 2322 2323 createDump(asyncResp, req, dumpType); 2324 } 2325 2326 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost( 2327 crow::App& app, const crow::Request& req, 2328 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2329 const std::string& systemName) 2330 { 2331 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2332 { 2333 return; 2334 } 2335 2336 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2337 { 2338 // Option currently returns no systems. TBD 2339 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2340 systemName); 2341 return; 2342 } 2343 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2344 { 2345 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2346 systemName); 2347 return; 2348 } 2349 createDump(asyncResp, req, "System"); 2350 } 2351 2352 inline void handleLogServicesDumpClearLogPost( 2353 crow::App& app, const std::string& dumpType, const crow::Request& req, 2354 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2355 const std::string& managerId) 2356 { 2357 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2358 { 2359 return; 2360 } 2361 2362 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 2363 { 2364 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 2365 return; 2366 } 2367 clearDump(asyncResp, dumpType); 2368 } 2369 2370 inline void handleLogServicesDumpClearLogComputerSystemPost( 2371 crow::App& app, const crow::Request& req, 2372 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2373 const std::string& systemName) 2374 { 2375 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2376 { 2377 return; 2378 } 2379 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2380 { 2381 // Option currently returns no systems. TBD 2382 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2383 systemName); 2384 return; 2385 } 2386 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2387 { 2388 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2389 systemName); 2390 return; 2391 } 2392 clearDump(asyncResp, "System"); 2393 } 2394 2395 inline void requestRoutesBMCDumpService(App& app) 2396 { 2397 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/") 2398 .privileges(redfish::privileges::getLogService) 2399 .methods(boost::beast::http::verb::get)(std::bind_front( 2400 handleLogServicesDumpServiceGet, std::ref(app), "BMC")); 2401 } 2402 2403 inline void requestRoutesBMCDumpEntryCollection(App& app) 2404 { 2405 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/") 2406 .privileges(redfish::privileges::getLogEntryCollection) 2407 .methods(boost::beast::http::verb::get)(std::bind_front( 2408 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC")); 2409 } 2410 2411 inline void requestRoutesBMCDumpEntry(App& app) 2412 { 2413 BMCWEB_ROUTE(app, 2414 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/") 2415 .privileges(redfish::privileges::getLogEntry) 2416 .methods(boost::beast::http::verb::get)(std::bind_front( 2417 handleLogServicesDumpEntryGet, std::ref(app), "BMC")); 2418 2419 BMCWEB_ROUTE(app, 2420 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/") 2421 .privileges(redfish::privileges::deleteLogEntry) 2422 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2423 handleLogServicesDumpEntryDelete, std::ref(app), "BMC")); 2424 } 2425 2426 inline void requestRoutesBMCDumpEntryDownload(App& app) 2427 { 2428 BMCWEB_ROUTE( 2429 app, 2430 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/") 2431 .privileges(redfish::privileges::getLogEntry) 2432 .methods(boost::beast::http::verb::get)(std::bind_front( 2433 handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC")); 2434 } 2435 2436 inline void requestRoutesBMCDumpCreate(App& app) 2437 { 2438 BMCWEB_ROUTE( 2439 app, 2440 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/") 2441 .privileges(redfish::privileges::postLogService) 2442 .methods(boost::beast::http::verb::post)( 2443 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost, 2444 std::ref(app), "BMC")); 2445 } 2446 2447 inline void requestRoutesBMCDumpClear(App& app) 2448 { 2449 BMCWEB_ROUTE( 2450 app, 2451 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/") 2452 .privileges(redfish::privileges::postLogService) 2453 .methods(boost::beast::http::verb::post)(std::bind_front( 2454 handleLogServicesDumpClearLogPost, std::ref(app), "BMC")); 2455 } 2456 2457 inline void requestRoutesDBusEventLogEntryDownload(App& app) 2458 { 2459 BMCWEB_ROUTE( 2460 app, 2461 "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment/") 2462 .privileges(redfish::privileges::getLogEntry) 2463 .methods(boost::beast::http::verb::get)(std::bind_front( 2464 handleDBusEventLogEntryDownloadGet, std::ref(app), "System")); 2465 } 2466 2467 inline void requestRoutesFaultLogDumpService(App& app) 2468 { 2469 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/") 2470 .privileges(redfish::privileges::getLogService) 2471 .methods(boost::beast::http::verb::get)(std::bind_front( 2472 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog")); 2473 } 2474 2475 inline void requestRoutesFaultLogDumpEntryCollection(App& app) 2476 { 2477 BMCWEB_ROUTE(app, 2478 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/") 2479 .privileges(redfish::privileges::getLogEntryCollection) 2480 .methods(boost::beast::http::verb::get)( 2481 std::bind_front(handleLogServicesDumpEntriesCollectionGet, 2482 std::ref(app), "FaultLog")); 2483 } 2484 2485 inline void requestRoutesFaultLogDumpEntry(App& app) 2486 { 2487 BMCWEB_ROUTE( 2488 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/") 2489 .privileges(redfish::privileges::getLogEntry) 2490 .methods(boost::beast::http::verb::get)(std::bind_front( 2491 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog")); 2492 2493 BMCWEB_ROUTE( 2494 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/") 2495 .privileges(redfish::privileges::deleteLogEntry) 2496 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2497 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog")); 2498 } 2499 2500 inline void requestRoutesFaultLogDumpClear(App& app) 2501 { 2502 BMCWEB_ROUTE( 2503 app, 2504 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/") 2505 .privileges(redfish::privileges::postLogService) 2506 .methods(boost::beast::http::verb::post)(std::bind_front( 2507 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog")); 2508 } 2509 2510 inline void requestRoutesSystemDumpService(App& app) 2511 { 2512 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/") 2513 .privileges(redfish::privileges::getLogService) 2514 .methods(boost::beast::http::verb::get)(std::bind_front( 2515 handleLogServicesDumpServiceComputerSystemGet, std::ref(app))); 2516 } 2517 2518 inline void requestRoutesSystemDumpEntryCollection(App& app) 2519 { 2520 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/") 2521 .privileges(redfish::privileges::getLogEntryCollection) 2522 .methods(boost::beast::http::verb::get)(std::bind_front( 2523 handleLogServicesDumpEntriesCollectionComputerSystemGet, 2524 std::ref(app))); 2525 } 2526 2527 inline void requestRoutesSystemDumpEntry(App& app) 2528 { 2529 BMCWEB_ROUTE(app, 2530 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/") 2531 .privileges(redfish::privileges::getLogEntry) 2532 .methods(boost::beast::http::verb::get)(std::bind_front( 2533 handleLogServicesDumpEntryComputerSystemGet, std::ref(app))); 2534 2535 BMCWEB_ROUTE(app, 2536 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/") 2537 .privileges(redfish::privileges::deleteLogEntry) 2538 .methods(boost::beast::http::verb::delete_)(std::bind_front( 2539 handleLogServicesDumpEntryComputerSystemDelete, std::ref(app))); 2540 } 2541 2542 inline void requestRoutesSystemDumpCreate(App& app) 2543 { 2544 BMCWEB_ROUTE( 2545 app, 2546 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/") 2547 .privileges(redfish::privileges::postLogService) 2548 .methods(boost::beast::http::verb::post)(std::bind_front( 2549 handleLogServicesDumpCollectDiagnosticDataComputerSystemPost, 2550 std::ref(app))); 2551 } 2552 2553 inline void requestRoutesSystemDumpClear(App& app) 2554 { 2555 BMCWEB_ROUTE( 2556 app, 2557 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/") 2558 .privileges(redfish::privileges::postLogService) 2559 .methods(boost::beast::http::verb::post)(std::bind_front( 2560 handleLogServicesDumpClearLogComputerSystemPost, std::ref(app))); 2561 } 2562 2563 inline void requestRoutesCrashdumpService(App& app) 2564 { 2565 // Note: Deviated from redfish privilege registry for GET & HEAD 2566 // method for security reasons. 2567 /** 2568 * Functions triggers appropriate requests on DBus 2569 */ 2570 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/") 2571 // This is incorrect, should be: 2572 //.privileges(redfish::privileges::getLogService) 2573 .privileges({{"ConfigureManager"}}) 2574 .methods( 2575 boost::beast::http::verb:: 2576 get)([&app](const crow::Request& req, 2577 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2578 const std::string& systemName) { 2579 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2580 { 2581 return; 2582 } 2583 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2584 { 2585 // Option currently returns no systems. TBD 2586 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2587 systemName); 2588 return; 2589 } 2590 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2591 { 2592 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2593 systemName); 2594 return; 2595 } 2596 2597 // Copy over the static data to include the entries added by 2598 // SubRoute 2599 asyncResp->res.jsonValue["@odata.id"] = 2600 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", 2601 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2602 asyncResp->res.jsonValue["@odata.type"] = 2603 "#LogService.v1_2_0.LogService"; 2604 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service"; 2605 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service"; 2606 asyncResp->res.jsonValue["Id"] = "Crashdump"; 2607 asyncResp->res.jsonValue["OverWritePolicy"] = 2608 log_service::OverWritePolicy::WrapsWhenFull; 2609 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 2610 2611 std::pair<std::string, std::string> redfishDateTimeOffset = 2612 redfish::time_utils::getDateTimeOffsetNow(); 2613 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 2614 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 2615 redfishDateTimeOffset.second; 2616 2617 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( 2618 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", 2619 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2620 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 2621 ["target"] = std::format( 2622 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog", 2623 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2624 asyncResp->res 2625 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"] 2626 ["target"] = std::format( 2627 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData", 2628 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2629 }); 2630 } 2631 2632 void inline requestRoutesCrashdumpClear(App& app) 2633 { 2634 BMCWEB_ROUTE( 2635 app, 2636 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/") 2637 // This is incorrect, should be: 2638 //.privileges(redfish::privileges::postLogService) 2639 .privileges({{"ConfigureComponents"}}) 2640 .methods(boost::beast::http::verb::post)( 2641 [&app](const crow::Request& req, 2642 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2643 const std::string& systemName) { 2644 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2645 { 2646 return; 2647 } 2648 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2649 { 2650 // Option currently returns no systems. TBD 2651 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2652 systemName); 2653 return; 2654 } 2655 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2656 { 2657 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2658 systemName); 2659 return; 2660 } 2661 crow::connections::systemBus->async_method_call( 2662 [asyncResp](const boost::system::error_code& ec, 2663 const std::string&) { 2664 if (ec) 2665 { 2666 messages::internalError(asyncResp->res); 2667 return; 2668 } 2669 messages::success(asyncResp->res); 2670 }, 2671 crashdumpObject, crashdumpPath, deleteAllInterface, 2672 "DeleteAll"); 2673 }); 2674 } 2675 2676 inline void logCrashdumpEntry( 2677 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2678 const std::string& logID, nlohmann::json& logEntryJson) 2679 { 2680 auto getStoredLogCallback = 2681 [asyncResp, logID, 2682 &logEntryJson](const boost::system::error_code& ec, 2683 const dbus::utility::DBusPropertiesMap& params) { 2684 if (ec) 2685 { 2686 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message()); 2687 if (ec.value() == 2688 boost::system::linux_error::bad_request_descriptor) 2689 { 2690 messages::resourceNotFound(asyncResp->res, "LogEntry", 2691 logID); 2692 } 2693 else 2694 { 2695 messages::internalError(asyncResp->res); 2696 } 2697 return; 2698 } 2699 2700 std::string timestamp{}; 2701 std::string filename{}; 2702 std::string logfile{}; 2703 parseCrashdumpParameters(params, filename, timestamp, logfile); 2704 2705 if (filename.empty() || timestamp.empty()) 2706 { 2707 messages::resourceNotFound(asyncResp->res, "LogEntry", logID); 2708 return; 2709 } 2710 2711 std::string crashdumpURI = 2712 std::format( 2713 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/", 2714 BMCWEB_REDFISH_SYSTEM_URI_NAME) + 2715 logID + "/" + filename; 2716 nlohmann::json::object_t logEntry; 2717 logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 2718 logEntry["@odata.id"] = boost::urls::format( 2719 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}", 2720 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID); 2721 logEntry["Name"] = "CPU Crashdump"; 2722 logEntry["Id"] = logID; 2723 logEntry["EntryType"] = log_entry::LogEntryType::Oem; 2724 logEntry["AdditionalDataURI"] = std::move(crashdumpURI); 2725 logEntry["DiagnosticDataType"] = "OEM"; 2726 logEntry["OEMDiagnosticDataType"] = "PECICrashdump"; 2727 logEntry["Created"] = std::move(timestamp); 2728 2729 // If logEntryJson references an array of LogEntry resources 2730 // ('Members' list), then push this as a new entry, otherwise set it 2731 // directly 2732 if (logEntryJson.is_array()) 2733 { 2734 logEntryJson.push_back(logEntry); 2735 asyncResp->res.jsonValue["Members@odata.count"] = 2736 logEntryJson.size(); 2737 } 2738 else 2739 { 2740 logEntryJson.update(logEntry); 2741 } 2742 }; 2743 dbus::utility::getAllProperties( 2744 crashdumpObject, crashdumpPath + std::string("/") + logID, 2745 crashdumpInterface, std::move(getStoredLogCallback)); 2746 } 2747 2748 inline void requestRoutesCrashdumpEntryCollection(App& app) 2749 { 2750 // Note: Deviated from redfish privilege registry for GET & HEAD 2751 // method for security reasons. 2752 /** 2753 * Functions triggers appropriate requests on DBus 2754 */ 2755 BMCWEB_ROUTE(app, 2756 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/") 2757 // This is incorrect, should be. 2758 //.privileges(redfish::privileges::postLogEntryCollection) 2759 .privileges({{"ConfigureComponents"}}) 2760 .methods( 2761 boost::beast::http::verb:: 2762 get)([&app](const crow::Request& req, 2763 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2764 const std::string& systemName) { 2765 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2766 { 2767 return; 2768 } 2769 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2770 { 2771 // Option currently returns no systems. TBD 2772 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2773 systemName); 2774 return; 2775 } 2776 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2777 { 2778 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2779 systemName); 2780 return; 2781 } 2782 2783 constexpr std::array<std::string_view, 1> interfaces = { 2784 crashdumpInterface}; 2785 dbus::utility::getSubTreePaths( 2786 "/", 0, interfaces, 2787 [asyncResp](const boost::system::error_code& ec, 2788 const std::vector<std::string>& resp) { 2789 if (ec) 2790 { 2791 if (ec.value() != 2792 boost::system::errc::no_such_file_or_directory) 2793 { 2794 BMCWEB_LOG_DEBUG("failed to get entries ec: {}", 2795 ec.message()); 2796 messages::internalError(asyncResp->res); 2797 return; 2798 } 2799 } 2800 asyncResp->res.jsonValue["@odata.type"] = 2801 "#LogEntryCollection.LogEntryCollection"; 2802 asyncResp->res.jsonValue["@odata.id"] = std::format( 2803 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", 2804 BMCWEB_REDFISH_SYSTEM_URI_NAME); 2805 asyncResp->res.jsonValue["Name"] = 2806 "Open BMC Crashdump Entries"; 2807 asyncResp->res.jsonValue["Description"] = 2808 "Collection of Crashdump Entries"; 2809 asyncResp->res.jsonValue["Members"] = 2810 nlohmann::json::array(); 2811 asyncResp->res.jsonValue["Members@odata.count"] = 0; 2812 2813 for (const std::string& path : resp) 2814 { 2815 const sdbusplus::message::object_path objPath(path); 2816 // Get the log ID 2817 std::string logID = objPath.filename(); 2818 if (logID.empty()) 2819 { 2820 continue; 2821 } 2822 // Add the log entry to the array 2823 logCrashdumpEntry(asyncResp, logID, 2824 asyncResp->res.jsonValue["Members"]); 2825 } 2826 }); 2827 }); 2828 } 2829 2830 inline void requestRoutesCrashdumpEntry(App& app) 2831 { 2832 // Note: Deviated from redfish privilege registry for GET & HEAD 2833 // method for security reasons. 2834 2835 BMCWEB_ROUTE( 2836 app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/") 2837 // this is incorrect, should be 2838 // .privileges(redfish::privileges::getLogEntry) 2839 .privileges({{"ConfigureComponents"}}) 2840 .methods(boost::beast::http::verb::get)( 2841 [&app](const crow::Request& req, 2842 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2843 const std::string& systemName, const std::string& param) { 2844 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2845 { 2846 return; 2847 } 2848 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2849 { 2850 // Option currently returns no systems. TBD 2851 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2852 systemName); 2853 return; 2854 } 2855 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2856 { 2857 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2858 systemName); 2859 return; 2860 } 2861 const std::string& logID = param; 2862 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); 2863 }); 2864 } 2865 2866 inline void requestRoutesCrashdumpFile(App& app) 2867 { 2868 // Note: Deviated from redfish privilege registry for GET & HEAD 2869 // method for security reasons. 2870 BMCWEB_ROUTE( 2871 app, 2872 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/") 2873 .privileges(redfish::privileges::getLogEntry) 2874 .methods(boost::beast::http::verb::get)( 2875 [](const crow::Request& req, 2876 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2877 const std::string& systemName, const std::string& logID, 2878 const std::string& fileName) { 2879 // Do not call getRedfishRoute here since the crashdump file is 2880 // not a Redfish resource. 2881 2882 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2883 { 2884 // Option currently returns no systems. TBD 2885 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2886 systemName); 2887 return; 2888 } 2889 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 2890 { 2891 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2892 systemName); 2893 return; 2894 } 2895 2896 auto getStoredLogCallback = 2897 [asyncResp, logID, fileName, 2898 url(boost::urls::url(req.url()))]( 2899 const boost::system::error_code& ec, 2900 const std::vector<std::pair< 2901 std::string, dbus::utility::DbusVariantType>>& 2902 resp) { 2903 if (ec) 2904 { 2905 BMCWEB_LOG_DEBUG("failed to get log ec: {}", 2906 ec.message()); 2907 messages::internalError(asyncResp->res); 2908 return; 2909 } 2910 2911 std::string dbusFilename{}; 2912 std::string dbusTimestamp{}; 2913 std::string dbusFilepath{}; 2914 2915 parseCrashdumpParameters(resp, dbusFilename, 2916 dbusTimestamp, dbusFilepath); 2917 2918 if (dbusFilename.empty() || dbusTimestamp.empty() || 2919 dbusFilepath.empty()) 2920 { 2921 messages::resourceNotFound(asyncResp->res, 2922 "LogEntry", logID); 2923 return; 2924 } 2925 2926 // Verify the file name parameter is correct 2927 if (fileName != dbusFilename) 2928 { 2929 messages::resourceNotFound(asyncResp->res, 2930 "LogEntry", logID); 2931 return; 2932 } 2933 2934 if (asyncResp->res.openFile(dbusFilepath) != 2935 crow::OpenCode::Success) 2936 { 2937 messages::resourceNotFound(asyncResp->res, 2938 "LogEntry", logID); 2939 return; 2940 } 2941 2942 // Configure this to be a file download when accessed 2943 // from a browser 2944 asyncResp->res.addHeader( 2945 boost::beast::http::field::content_disposition, 2946 "attachment"); 2947 }; 2948 dbus::utility::getAllProperties( 2949 *crow::connections::systemBus, crashdumpObject, 2950 crashdumpPath + std::string("/") + logID, 2951 crashdumpInterface, std::move(getStoredLogCallback)); 2952 }); 2953 } 2954 2955 enum class OEMDiagnosticType 2956 { 2957 onDemand, 2958 telemetry, 2959 invalid, 2960 }; 2961 2962 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr) 2963 { 2964 if (oemDiagStr == "OnDemand") 2965 { 2966 return OEMDiagnosticType::onDemand; 2967 } 2968 if (oemDiagStr == "Telemetry") 2969 { 2970 return OEMDiagnosticType::telemetry; 2971 } 2972 2973 return OEMDiagnosticType::invalid; 2974 } 2975 2976 inline void requestRoutesCrashdumpCollect(App& app) 2977 { 2978 // Note: Deviated from redfish privilege registry for GET & HEAD 2979 // method for security reasons. 2980 BMCWEB_ROUTE( 2981 app, 2982 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/") 2983 // The below is incorrect; Should be ConfigureManager 2984 //.privileges(redfish::privileges::postLogService) 2985 .privileges({{"ConfigureComponents"}}) 2986 .methods(boost::beast::http::verb::post)( 2987 [&app](const crow::Request& req, 2988 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2989 const std::string& systemName) { 2990 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2991 { 2992 return; 2993 } 2994 2995 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 2996 { 2997 // Option currently returns no systems. TBD 2998 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2999 systemName); 3000 return; 3001 } 3002 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 3003 { 3004 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3005 systemName); 3006 return; 3007 } 3008 3009 std::string diagnosticDataType; 3010 std::string oemDiagnosticDataType; 3011 if (!redfish::json_util::readJsonAction( // 3012 req, asyncResp->res, // 3013 "DiagnosticDataType", diagnosticDataType, // 3014 "OEMDiagnosticDataType", oemDiagnosticDataType // 3015 )) 3016 { 3017 return; 3018 } 3019 3020 if (diagnosticDataType != "OEM") 3021 { 3022 BMCWEB_LOG_ERROR( 3023 "Only OEM DiagnosticDataType supported for Crashdump"); 3024 messages::actionParameterValueFormatError( 3025 asyncResp->res, diagnosticDataType, 3026 "DiagnosticDataType", "CollectDiagnosticData"); 3027 return; 3028 } 3029 3030 OEMDiagnosticType oemDiagType = 3031 getOEMDiagnosticType(oemDiagnosticDataType); 3032 3033 std::string iface; 3034 std::string method; 3035 std::string taskMatchStr; 3036 if (oemDiagType == OEMDiagnosticType::onDemand) 3037 { 3038 iface = crashdumpOnDemandInterface; 3039 method = "GenerateOnDemandLog"; 3040 taskMatchStr = 3041 "type='signal'," 3042 "interface='org.freedesktop.DBus.Properties'," 3043 "member='PropertiesChanged'," 3044 "arg0namespace='com.intel.crashdump'"; 3045 } 3046 else if (oemDiagType == OEMDiagnosticType::telemetry) 3047 { 3048 iface = crashdumpTelemetryInterface; 3049 method = "GenerateTelemetryLog"; 3050 taskMatchStr = 3051 "type='signal'," 3052 "interface='org.freedesktop.DBus.Properties'," 3053 "member='PropertiesChanged'," 3054 "arg0namespace='com.intel.crashdump'"; 3055 } 3056 else 3057 { 3058 BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}", 3059 oemDiagnosticDataType); 3060 messages::actionParameterValueFormatError( 3061 asyncResp->res, oemDiagnosticDataType, 3062 "OEMDiagnosticDataType", "CollectDiagnosticData"); 3063 return; 3064 } 3065 3066 auto collectCrashdumpCallback = 3067 [asyncResp, payload(task::Payload(req)), 3068 taskMatchStr](const boost::system::error_code& ec, 3069 const std::string&) mutable { 3070 if (ec) 3071 { 3072 if (ec.value() == 3073 boost::system::errc::operation_not_supported) 3074 { 3075 messages::resourceInStandby(asyncResp->res); 3076 } 3077 else if (ec.value() == boost::system::errc:: 3078 device_or_resource_busy) 3079 { 3080 messages::serviceTemporarilyUnavailable( 3081 asyncResp->res, "60"); 3082 } 3083 else 3084 { 3085 messages::internalError(asyncResp->res); 3086 } 3087 return; 3088 } 3089 std::shared_ptr<task::TaskData> task = 3090 task::TaskData::createTask( 3091 [](const boost::system::error_code& ec2, 3092 sdbusplus::message_t&, 3093 const std::shared_ptr<task::TaskData>& 3094 taskData) { 3095 if (!ec2) 3096 { 3097 taskData->messages.emplace_back( 3098 messages::taskCompletedOK( 3099 std::to_string( 3100 taskData->index))); 3101 taskData->state = "Completed"; 3102 } 3103 return task::completed; 3104 }, 3105 taskMatchStr); 3106 3107 task->startTimer(std::chrono::minutes(5)); 3108 task->populateResp(asyncResp->res); 3109 task->payload.emplace(std::move(payload)); 3110 }; 3111 3112 crow::connections::systemBus->async_method_call( 3113 std::move(collectCrashdumpCallback), crashdumpObject, 3114 crashdumpPath, iface, method); 3115 }); 3116 } 3117 3118 inline void dBusLogServiceActionsClear( 3119 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 3120 { 3121 BMCWEB_LOG_DEBUG("Do delete all entries."); 3122 3123 // Process response from Logging service. 3124 auto respHandler = [asyncResp](const boost::system::error_code& ec) { 3125 BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done"); 3126 if (ec) 3127 { 3128 // TODO Handle for specific error code 3129 BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec); 3130 asyncResp->res.result( 3131 boost::beast::http::status::internal_server_error); 3132 return; 3133 } 3134 3135 asyncResp->res.result(boost::beast::http::status::no_content); 3136 }; 3137 3138 // Make call to Logging service to request Clear Log 3139 crow::connections::systemBus->async_method_call( 3140 respHandler, "xyz.openbmc_project.Logging", 3141 "/xyz/openbmc_project/logging", 3142 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 3143 } 3144 3145 /** 3146 * DBusLogServiceActionsClear class supports POST method for ClearLog action. 3147 */ 3148 inline void requestRoutesDBusLogServiceActionsClear(App& app) 3149 { 3150 /** 3151 * Function handles POST method request. 3152 * The Clear Log actions does not require any parameter.The action deletes 3153 * all entries found in the Entries collection for this Log Service. 3154 */ 3155 3156 BMCWEB_ROUTE( 3157 app, 3158 "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/") 3159 .privileges(redfish::privileges::postLogService) 3160 .methods(boost::beast::http::verb::post)( 3161 [&app](const crow::Request& req, 3162 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3163 const std::string& systemName) { 3164 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3165 { 3166 return; 3167 } 3168 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 3169 { 3170 // Option currently returns no systems. TBD 3171 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3172 systemName); 3173 return; 3174 } 3175 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 3176 { 3177 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3178 systemName); 3179 return; 3180 } 3181 dBusLogServiceActionsClear(asyncResp); 3182 }); 3183 } 3184 3185 } // namespace redfish 3186