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