1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "bmcweb_config.h" 6 7 #include "app.hpp" 8 #include "async_resp.hpp" 9 #include "dbus_utility.hpp" 10 #include "error_messages.hpp" 11 #include "generated/enums/log_service.hpp" 12 #include "http_request.hpp" 13 #include "http_utility.hpp" 14 #include "logging.hpp" 15 #include "query.hpp" 16 #include "registries.hpp" 17 #include "registries/privilege_registry.hpp" 18 #include "str_utility.hpp" 19 #include "utility.hpp" 20 #include "utils/etag_utils.hpp" 21 #include "utils/hex_utils.hpp" 22 #include "utils/query_param.hpp" 23 #include "utils/time_utils.hpp" 24 25 #include <asm-generic/errno.h> 26 27 #include <boost/beast/http/field.hpp> 28 #include <boost/beast/http/status.hpp> 29 #include <boost/beast/http/verb.hpp> 30 #include <boost/container/flat_map.hpp> 31 #include <boost/url/format.hpp> 32 33 #include <algorithm> 34 #include <array> 35 #include <charconv> 36 #include <cstddef> 37 #include <cstdint> 38 #include <format> 39 #include <functional> 40 #include <iomanip> 41 #include <ios> 42 #include <memory> 43 #include <sstream> 44 #include <string> 45 #include <string_view> 46 #include <system_error> 47 #include <tuple> 48 #include <utility> 49 #include <vector> 50 51 namespace redfish 52 { 53 54 inline void handleSystemsLogServicesPostCodesGet( 55 App& app, const crow::Request& req, 56 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 57 const std::string& systemName) 58 { 59 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 60 { 61 return; 62 } 63 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 64 { 65 // Option currently returns no systems. TBD 66 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 67 systemName); 68 return; 69 } 70 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 71 { 72 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 73 systemName); 74 return; 75 } 76 asyncResp->res.jsonValue["@odata.id"] = 77 std::format("/redfish/v1/Systems/{}/LogServices/PostCodes", 78 BMCWEB_REDFISH_SYSTEM_URI_NAME); 79 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; 80 asyncResp->res.jsonValue["Name"] = "POST Code Log Service"; 81 asyncResp->res.jsonValue["Description"] = "POST Code Log Service"; 82 asyncResp->res.jsonValue["Id"] = "PostCodes"; 83 asyncResp->res.jsonValue["OverWritePolicy"] = 84 log_service::OverWritePolicy::WrapsWhenFull; 85 asyncResp->res.jsonValue["Entries"]["@odata.id"] = 86 std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", 87 BMCWEB_REDFISH_SYSTEM_URI_NAME); 88 89 std::pair<std::string, std::string> redfishDateTimeOffset = 90 redfish::time_utils::getDateTimeOffsetNow(); 91 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 92 asyncResp->res.jsonValue["DateTimeLocalOffset"] = 93 redfishDateTimeOffset.second; 94 95 asyncResp->res 96 .jsonValue["Actions"]["#LogService.ClearLog"]["target"] = std::format( 97 "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog", 98 BMCWEB_REDFISH_SYSTEM_URI_NAME); 99 100 etag_utils::setEtagOmitDateTimeHandler(asyncResp); 101 } 102 103 inline void handleSystemsLogServicesPostCodesPost( 104 App& app, const crow::Request& req, 105 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 106 const std::string& systemName) 107 { 108 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 109 { 110 return; 111 } 112 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 113 { 114 // Option currently returns no systems. TBD 115 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 116 systemName); 117 return; 118 } 119 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 120 { 121 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 122 systemName); 123 return; 124 } 125 BMCWEB_LOG_DEBUG("Do delete all postcodes entries."); 126 127 // Make call to post-code service to request clear all 128 dbus::utility::async_method_call( 129 asyncResp, 130 [asyncResp](const boost::system::error_code& ec) { 131 if (ec) 132 { 133 // TODO Handle for specific error code 134 BMCWEB_LOG_ERROR("doClearPostCodes resp_handler got error {}", 135 ec); 136 asyncResp->res.result( 137 boost::beast::http::status::internal_server_error); 138 messages::internalError(asyncResp->res); 139 return; 140 } 141 messages::success(asyncResp->res); 142 }, 143 "xyz.openbmc_project.State.Boot.PostCode0", 144 "/xyz/openbmc_project/State/Boot/PostCode0", 145 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 146 } 147 148 /** 149 * @brief Parse post code ID and get the current value and index value 150 * eg: postCodeID=B1-2, currentValue=1, index=2 151 * 152 * @param[in] postCodeID Post Code ID 153 * @param[out] currentValue Current value 154 * @param[out] index Index value 155 * 156 * @return bool true if the parsing is successful, false the parsing fails 157 */ 158 inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue, 159 uint16_t& index) 160 { 161 std::vector<std::string> split; 162 bmcweb::split(split, postCodeID, '-'); 163 if (split.size() != 2) 164 { 165 return false; 166 } 167 std::string_view postCodeNumber = split[0]; 168 if (postCodeNumber.size() < 2) 169 { 170 return false; 171 } 172 if (postCodeNumber[0] != 'B') 173 { 174 return false; 175 } 176 postCodeNumber.remove_prefix(1); 177 auto [ptrIndex, ecIndex] = 178 std::from_chars(postCodeNumber.begin(), postCodeNumber.end(), index); 179 if (ptrIndex != postCodeNumber.end() || ecIndex != std::errc()) 180 { 181 return false; 182 } 183 184 std::string_view postCodeIndex = split[1]; 185 186 auto [ptrValue, ecValue] = std::from_chars( 187 postCodeIndex.begin(), postCodeIndex.end(), currentValue); 188 189 return ptrValue == postCodeIndex.end() && ecValue == std::errc(); 190 } 191 192 static bool fillPostCodeEntry( 193 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 194 const boost::container::flat_map< 195 uint64_t, std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>>& 196 postcode, 197 const uint16_t bootIndex, const uint64_t codeIndex = 0, 198 const uint64_t skip = 0, const uint64_t top = 0) 199 { 200 // Get the Message from the MessageRegistry 201 const registries::Message* message = 202 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode"); 203 if (message == nullptr) 204 { 205 BMCWEB_LOG_ERROR("Couldn't find known message?"); 206 return false; 207 } 208 uint64_t currentCodeIndex = 0; 209 uint64_t firstCodeTimeUs = 0; 210 for (const std::pair<uint64_t, std::tuple<std::vector<uint8_t>, 211 std::vector<uint8_t>>>& code : 212 postcode) 213 { 214 currentCodeIndex++; 215 std::string postcodeEntryID = 216 "B" + std::to_string(bootIndex) + "-" + 217 std::to_string(currentCodeIndex); // 1 based index in EntryID string 218 219 uint64_t usecSinceEpoch = code.first; 220 uint64_t usTimeOffset = 0; 221 222 if (1 == currentCodeIndex) 223 { // already incremented 224 firstCodeTimeUs = code.first; 225 } 226 else 227 { 228 usTimeOffset = code.first - firstCodeTimeUs; 229 } 230 231 // skip if no specific codeIndex is specified and currentCodeIndex does 232 // not fall between top and skip 233 if ((codeIndex == 0) && 234 (currentCodeIndex <= skip || currentCodeIndex > top)) 235 { 236 continue; 237 } 238 239 // skip if a specific codeIndex is specified and does not match the 240 // currentIndex 241 if ((codeIndex > 0) && (currentCodeIndex != codeIndex)) 242 { 243 // This is done for simplicity. 1st entry is needed to calculate 244 // time offset. To improve efficiency, one can get to the entry 245 // directly (possibly with flatmap's nth method) 246 continue; 247 } 248 249 // currentCodeIndex is within top and skip or equal to specified code 250 // index 251 252 // Get the Created time from the timestamp 253 std::string entryTimeStr; 254 entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch); 255 256 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex) 257 std::ostringstream timeOffsetStr; 258 // Set Fixed -Point Notation 259 timeOffsetStr << std::fixed; 260 // Set precision to 4 digits 261 timeOffsetStr << std::setprecision(4); 262 // Add double to stream 263 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000; 264 265 std::string bootIndexStr = std::to_string(bootIndex); 266 std::string timeOffsetString = timeOffsetStr.str(); 267 std::string hexCodeStr = 268 "0x" + bytesToHexString(std::get<0>(code.second)); 269 270 std::array<std::string_view, 3> messageArgs = { 271 bootIndexStr, timeOffsetString, hexCodeStr}; 272 273 std::string msg = 274 redfish::registries::fillMessageArgs(messageArgs, message->message); 275 if (msg.empty()) 276 { 277 messages::internalError(asyncResp->res); 278 return false; 279 } 280 281 // Get Severity template from message registry 282 std::string severity; 283 if (message != nullptr) 284 { 285 severity = message->messageSeverity; 286 } 287 288 // Format entry 289 nlohmann::json::object_t bmcLogEntry; 290 bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 291 bmcLogEntry["@odata.id"] = boost::urls::format( 292 "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}", 293 BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID); 294 bmcLogEntry["Name"] = "POST Code Log Entry"; 295 bmcLogEntry["Id"] = postcodeEntryID; 296 bmcLogEntry["Message"] = std::move(msg); 297 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode"; 298 bmcLogEntry["MessageArgs"] = messageArgs; 299 bmcLogEntry["EntryType"] = "Event"; 300 bmcLogEntry["Severity"] = std::move(severity); 301 bmcLogEntry["Created"] = entryTimeStr; 302 if (!std::get<1>(code.second).empty()) 303 { 304 bmcLogEntry["AdditionalDataURI"] = 305 std::format( 306 "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/", 307 BMCWEB_REDFISH_SYSTEM_URI_NAME) + 308 postcodeEntryID + "/attachment"; 309 } 310 311 // codeIndex is only specified when querying single entry, return only 312 // that entry in this case 313 if (codeIndex != 0) 314 { 315 asyncResp->res.jsonValue.update(bmcLogEntry); 316 return true; 317 } 318 319 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; 320 logEntryArray.emplace_back(std::move(bmcLogEntry)); 321 } 322 323 // Return value is always false when querying multiple entries 324 return false; 325 } 326 327 inline void getPostCodeForEntry( 328 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 329 const std::string& entryId) 330 { 331 uint16_t bootIndex = 0; 332 uint64_t codeIndex = 0; 333 if (!parsePostCode(entryId, codeIndex, bootIndex)) 334 { 335 // Requested ID was not found 336 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); 337 return; 338 } 339 340 if (bootIndex == 0 || codeIndex == 0) 341 { 342 // 0 is an invalid index 343 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); 344 return; 345 } 346 347 dbus::utility::async_method_call( 348 asyncResp, 349 [asyncResp, entryId, bootIndex, 350 codeIndex](const boost::system::error_code& ec, 351 const boost::container::flat_map< 352 uint64_t, std::tuple<std::vector<uint8_t>, 353 std::vector<uint8_t>>>& postcode) { 354 if (ec) 355 { 356 BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error"); 357 messages::internalError(asyncResp->res); 358 return; 359 } 360 361 if (postcode.empty()) 362 { 363 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); 364 return; 365 } 366 367 if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex)) 368 { 369 messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); 370 return; 371 } 372 }, 373 "xyz.openbmc_project.State.Boot.PostCode0", 374 "/xyz/openbmc_project/State/Boot/PostCode0", 375 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", 376 bootIndex); 377 } 378 379 inline void getPostCodeForBoot( 380 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 381 const uint16_t bootIndex, const uint16_t bootCount, 382 const uint64_t entryCount, size_t skip, size_t top) 383 { 384 dbus::utility::async_method_call( 385 asyncResp, 386 [asyncResp, bootIndex, bootCount, entryCount, skip, 387 top](const boost::system::error_code& ec, 388 const boost::container::flat_map< 389 uint64_t, std::tuple<std::vector<uint8_t>, 390 std::vector<uint8_t>>>& postcode) { 391 if (ec) 392 { 393 BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error"); 394 messages::internalError(asyncResp->res); 395 return; 396 } 397 398 uint64_t endCount = entryCount; 399 if (!postcode.empty()) 400 { 401 endCount = entryCount + postcode.size(); 402 if (skip < endCount && (top + skip) > entryCount) 403 { 404 uint64_t thisBootSkip = 405 std::max(static_cast<uint64_t>(skip), entryCount) - 406 entryCount; 407 uint64_t thisBootTop = 408 std::min(static_cast<uint64_t>(top + skip), endCount) - 409 entryCount; 410 411 fillPostCodeEntry(asyncResp, postcode, bootIndex, 0, 412 thisBootSkip, thisBootTop); 413 } 414 asyncResp->res.jsonValue["Members@odata.count"] = endCount; 415 } 416 417 // continue to previous bootIndex 418 if (bootIndex < bootCount) 419 { 420 getPostCodeForBoot(asyncResp, 421 static_cast<uint16_t>(bootIndex + 1), 422 bootCount, endCount, skip, top); 423 } 424 else if (skip + top < endCount) 425 { 426 asyncResp->res.jsonValue["Members@odata.nextLink"] = 427 std::format( 428 "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=", 429 BMCWEB_REDFISH_SYSTEM_URI_NAME) + 430 std::to_string(skip + top); 431 } 432 }, 433 "xyz.openbmc_project.State.Boot.PostCode0", 434 "/xyz/openbmc_project/State/Boot/PostCode0", 435 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", 436 bootIndex); 437 } 438 439 inline void getCurrentBootNumber( 440 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, size_t skip, 441 size_t top) 442 { 443 uint64_t entryCount = 0; 444 dbus::utility::getProperty<uint16_t>( 445 "xyz.openbmc_project.State.Boot.PostCode0", 446 "/xyz/openbmc_project/State/Boot/PostCode0", 447 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount", 448 [asyncResp, entryCount, skip, 449 top](const boost::system::error_code& ec, const uint16_t bootCount) { 450 if (ec) 451 { 452 BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 453 messages::internalError(asyncResp->res); 454 return; 455 } 456 getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top); 457 }); 458 } 459 460 inline void handleSystemsLogServicesPostCodesEntriesGet( 461 App& app, const crow::Request& req, 462 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 463 const std::string& systemName) 464 { 465 query_param::QueryCapabilities capabilities = { 466 .canDelegateTop = true, 467 .canDelegateSkip = true, 468 }; 469 query_param::Query delegatedQuery; 470 if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, 471 delegatedQuery, capabilities)) 472 { 473 return; 474 } 475 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 476 { 477 // Option currently returns no systems. TBD 478 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 479 systemName); 480 return; 481 } 482 483 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 484 { 485 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 486 systemName); 487 return; 488 } 489 asyncResp->res.jsonValue["@odata.type"] = 490 "#LogEntryCollection.LogEntryCollection"; 491 asyncResp->res.jsonValue["@odata.id"] = 492 std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", 493 BMCWEB_REDFISH_SYSTEM_URI_NAME); 494 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries"; 495 asyncResp->res.jsonValue["Description"] = 496 "Collection of POST Code Log Entries"; 497 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 498 asyncResp->res.jsonValue["Members@odata.count"] = 0; 499 size_t skip = delegatedQuery.skip.value_or(0); 500 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); 501 getCurrentBootNumber(asyncResp, skip, top); 502 } 503 504 inline void handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet( 505 App& app, const crow::Request& req, 506 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 507 const std::string& systemName, const std::string& postCodeID) 508 { 509 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 510 { 511 return; 512 } 513 if (!http_helpers::isContentTypeAllowed( 514 req.getHeaderValue("Accept"), 515 http_helpers::ContentType::OctetStream, true)) 516 { 517 asyncResp->res.result(boost::beast::http::status::bad_request); 518 return; 519 } 520 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 521 { 522 // Option currently returns no systems. TBD 523 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 524 systemName); 525 return; 526 } 527 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 528 { 529 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 530 systemName); 531 return; 532 } 533 534 uint64_t currentValue = 0; 535 uint16_t index = 0; 536 if (!parsePostCode(postCodeID, currentValue, index)) 537 { 538 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID); 539 return; 540 } 541 542 dbus::utility::async_method_call( 543 asyncResp, 544 [asyncResp, postCodeID, currentValue]( 545 const boost::system::error_code& ec, 546 const std::vector<std::tuple<std::vector<uint8_t>, 547 std::vector<uint8_t>>>& postcodes) { 548 if (ec.value() == EBADR) 549 { 550 messages::resourceNotFound(asyncResp->res, "LogEntry", 551 postCodeID); 552 return; 553 } 554 if (ec) 555 { 556 BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 557 messages::internalError(asyncResp->res); 558 return; 559 } 560 561 size_t value = static_cast<size_t>(currentValue) - 1; 562 if (value == std::string::npos || postcodes.size() < currentValue) 563 { 564 BMCWEB_LOG_WARNING("Wrong currentValue value"); 565 messages::resourceNotFound(asyncResp->res, "LogEntry", 566 postCodeID); 567 return; 568 } 569 570 const auto& [tID, c] = postcodes[value]; 571 if (c.empty()) 572 { 573 BMCWEB_LOG_WARNING("No found post code data"); 574 messages::resourceNotFound(asyncResp->res, "LogEntry", 575 postCodeID); 576 return; 577 } 578 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 579 const char* d = reinterpret_cast<const char*>(c.data()); 580 std::string_view strData(d, c.size()); 581 582 asyncResp->res.addHeader(boost::beast::http::field::content_type, 583 "application/octet-stream"); 584 asyncResp->res.addHeader( 585 boost::beast::http::field::content_transfer_encoding, "Base64"); 586 asyncResp->res.write(crow::utility::base64encode(strData)); 587 }, 588 "xyz.openbmc_project.State.Boot.PostCode0", 589 "/xyz/openbmc_project/State/Boot/PostCode0", 590 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index); 591 } 592 593 inline void handleSystemsLogServicesPostCodesEntriesEntryGet( 594 App& app, const crow::Request& req, 595 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 596 const std::string& systemName, const std::string& targetID) 597 { 598 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 599 { 600 return; 601 } 602 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 603 { 604 // Option currently returns no systems. TBD 605 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 606 systemName); 607 return; 608 } 609 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 610 { 611 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 612 systemName); 613 return; 614 } 615 616 getPostCodeForEntry(asyncResp, targetID); 617 } 618 619 inline void requestRoutesSystemsLogServicesPostCode(App& app) 620 { 621 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/") 622 .privileges(redfish::privileges::getLogService) 623 .methods(boost::beast::http::verb::get)(std::bind_front( 624 handleSystemsLogServicesPostCodesGet, std::ref(app))); 625 626 BMCWEB_ROUTE( 627 app, 628 "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/") 629 .privileges(redfish::privileges:: 630 postLogServiceSubOverComputerSystemLogServiceCollection) 631 .methods(boost::beast::http::verb::post)(std::bind_front( 632 handleSystemsLogServicesPostCodesPost, std::ref(app))); 633 634 BMCWEB_ROUTE(app, 635 "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/") 636 .privileges(redfish::privileges::getLogEntryCollection) 637 .methods(boost::beast::http::verb::get)(std::bind_front( 638 handleSystemsLogServicesPostCodesEntriesGet, std::ref(app))); 639 640 BMCWEB_ROUTE( 641 app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/") 642 .privileges(redfish::privileges::getLogEntry) 643 .methods(boost::beast::http::verb::get)(std::bind_front( 644 handleSystemsLogServicesPostCodesEntriesEntryGet, std::ref(app))); 645 646 BMCWEB_ROUTE( 647 app, 648 "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/") 649 .privileges(redfish::privileges::getLogEntry) 650 .methods(boost::beast::http::verb::get)(std::bind_front( 651 handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet, 652 std::ref(app))); 653 } 654 } // namespace redfish 655