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