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