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