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