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