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