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