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::message& 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)),
902          dumpPath](const boost::system::error_code ec,
903                    const sdbusplus::message::message& msg,
904                    const sdbusplus::message::object_path& objPath) mutable {
905         if (ec)
906         {
907             BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
908             const sd_bus_error* dbusError = msg.get_error();
909             if (dbusError == nullptr)
910             {
911                 messages::internalError(asyncResp->res);
912                 return;
913             }
914 
915             BMCWEB_LOG_ERROR << "CreateDump DBus error: " << dbusError->name
916                              << " and error msg: " << dbusError->message;
917             if (std::string_view(
918                     "xyz.openbmc_project.Common.Error.NotAllowed") ==
919                 dbusError->name)
920             {
921                 messages::resourceInStandby(asyncResp->res);
922                 return;
923             }
924             if (std::string_view(
925                     "xyz.openbmc_project.Dump.Create.Error.Disabled") ==
926                 dbusError->name)
927             {
928                 messages::serviceDisabled(asyncResp->res, dumpPath);
929                 return;
930             }
931             if (std::string_view(
932                     "xyz.openbmc_project.Common.Error.Unavailable") ==
933                 dbusError->name)
934             {
935                 messages::resourceInUse(asyncResp->res);
936                 return;
937             }
938             // Other Dbus errors such as:
939             // xyz.openbmc_project.Common.Error.InvalidArgument &
940             // org.freedesktop.DBus.Error.InvalidArgs are all related to
941             // the dbus call that is made here in the bmcweb
942             // implementation and has nothing to do with the client's
943             // input in the request. Hence, returning internal error
944             // back to the client.
945             messages::internalError(asyncResp->res);
946             return;
947         }
948         BMCWEB_LOG_DEBUG << "Dump Created. Path: " << objPath.str;
949         createDumpTaskCallback(std::move(payload), asyncResp, objPath);
950         },
951         "xyz.openbmc_project.Dump.Manager",
952         "/xyz/openbmc_project/dump/" +
953             std::string(boost::algorithm::to_lower_copy(dumpType)),
954         "xyz.openbmc_project.Dump.Create", "CreateDump", createDumpParamVec);
955 }
956 
957 inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
958                       const std::string& dumpType)
959 {
960     std::string dumpTypeLowerCopy =
961         std::string(boost::algorithm::to_lower_copy(dumpType));
962 
963     crow::connections::systemBus->async_method_call(
964         [asyncResp, dumpType](
965             const boost::system::error_code ec,
966             const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
967         if (ec)
968         {
969             BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
970             messages::internalError(asyncResp->res);
971             return;
972         }
973 
974         for (const std::string& path : subTreePaths)
975         {
976             sdbusplus::message::object_path objPath(path);
977             std::string logID = objPath.filename();
978             if (logID.empty())
979             {
980                 continue;
981             }
982             deleteDumpEntry(asyncResp, logID, dumpType);
983         }
984         },
985         "xyz.openbmc_project.ObjectMapper",
986         "/xyz/openbmc_project/object_mapper",
987         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
988         "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
989         std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
990                                    dumpType});
991 }
992 
993 inline static void
994     parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
995                              std::string& filename, std::string& timestamp,
996                              std::string& logfile)
997 {
998     const std::string* filenamePtr = nullptr;
999     const std::string* timestampPtr = nullptr;
1000     const std::string* logfilePtr = nullptr;
1001 
1002     const bool success = sdbusplus::unpackPropertiesNoThrow(
1003         dbus_utils::UnpackErrorPrinter(), params, "Timestamp", timestampPtr,
1004         "Filename", filenamePtr, "Log", logfilePtr);
1005 
1006     if (!success)
1007     {
1008         return;
1009     }
1010 
1011     if (filenamePtr != nullptr)
1012     {
1013         filename = *filenamePtr;
1014     }
1015 
1016     if (timestampPtr != nullptr)
1017     {
1018         timestamp = *timestampPtr;
1019     }
1020 
1021     if (logfilePtr != nullptr)
1022     {
1023         logfile = *logfilePtr;
1024     }
1025 }
1026 
1027 constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
1028 inline void requestRoutesSystemLogServiceCollection(App& app)
1029 {
1030     /**
1031      * Functions triggers appropriate requests on DBus
1032      */
1033     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/")
1034         .privileges(redfish::privileges::getLogServiceCollection)
1035         .methods(boost::beast::http::verb::get)(
1036             [&app](const crow::Request& req,
1037                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1038                    const std::string& systemName) {
1039         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1040         {
1041             return;
1042         }
1043         if (systemName != "system")
1044         {
1045             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1046                                        systemName);
1047             return;
1048         }
1049 
1050         // Collections don't include the static data added by SubRoute
1051         // because it has a duplicate entry for members
1052         asyncResp->res.jsonValue["@odata.type"] =
1053             "#LogServiceCollection.LogServiceCollection";
1054         asyncResp->res.jsonValue["@odata.id"] =
1055             "/redfish/v1/Systems/system/LogServices";
1056         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
1057         asyncResp->res.jsonValue["Description"] =
1058             "Collection of LogServices for this Computer System";
1059         nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
1060         logServiceArray = nlohmann::json::array();
1061         nlohmann::json::object_t eventLog;
1062         eventLog["@odata.id"] =
1063             "/redfish/v1/Systems/system/LogServices/EventLog";
1064         logServiceArray.push_back(std::move(eventLog));
1065 #ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1066         nlohmann::json::object_t dumpLog;
1067         dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
1068         logServiceArray.push_back(std::move(dumpLog));
1069 #endif
1070 
1071 #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
1072         nlohmann::json::object_t crashdump;
1073         crashdump["@odata.id"] =
1074             "/redfish/v1/Systems/system/LogServices/Crashdump";
1075         logServiceArray.push_back(std::move(crashdump));
1076 #endif
1077 
1078 #ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
1079         nlohmann::json::object_t hostlogger;
1080         hostlogger["@odata.id"] =
1081             "/redfish/v1/Systems/system/LogServices/HostLogger";
1082         logServiceArray.push_back(std::move(hostlogger));
1083 #endif
1084         asyncResp->res.jsonValue["Members@odata.count"] =
1085             logServiceArray.size();
1086 
1087         crow::connections::systemBus->async_method_call(
1088             [asyncResp](const boost::system::error_code ec,
1089                         const dbus::utility::MapperGetSubTreePathsResponse&
1090                             subtreePath) {
1091             if (ec)
1092             {
1093                 BMCWEB_LOG_ERROR << ec;
1094                 return;
1095             }
1096 
1097             for (const auto& pathStr : subtreePath)
1098             {
1099                 if (pathStr.find("PostCode") != std::string::npos)
1100                 {
1101                     nlohmann::json& logServiceArrayLocal =
1102                         asyncResp->res.jsonValue["Members"];
1103                     nlohmann::json::object_t member;
1104                     member["@odata.id"] =
1105                         "/redfish/v1/Systems/system/LogServices/PostCodes";
1106 
1107                     logServiceArrayLocal.push_back(std::move(member));
1108 
1109                     asyncResp->res.jsonValue["Members@odata.count"] =
1110                         logServiceArrayLocal.size();
1111                     return;
1112                 }
1113             }
1114             },
1115             "xyz.openbmc_project.ObjectMapper",
1116             "/xyz/openbmc_project/object_mapper",
1117             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
1118             std::array<const char*, 1>{postCodeIface});
1119         });
1120 }
1121 
1122 inline void requestRoutesEventLogService(App& app)
1123 {
1124     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/")
1125         .privileges(redfish::privileges::getLogService)
1126         .methods(boost::beast::http::verb::get)(
1127             [&app](const crow::Request& req,
1128                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1129                    const std::string& systemName) {
1130         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1131         {
1132             return;
1133         }
1134         if (systemName != "system")
1135         {
1136             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1137                                        systemName);
1138             return;
1139         }
1140         asyncResp->res.jsonValue["@odata.id"] =
1141             "/redfish/v1/Systems/system/LogServices/EventLog";
1142         asyncResp->res.jsonValue["@odata.type"] =
1143             "#LogService.v1_1_0.LogService";
1144         asyncResp->res.jsonValue["Name"] = "Event Log Service";
1145         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
1146         asyncResp->res.jsonValue["Id"] = "EventLog";
1147         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1148 
1149         std::pair<std::string, std::string> redfishDateTimeOffset =
1150             redfish::time_utils::getDateTimeOffsetNow();
1151 
1152         asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1153         asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1154             redfishDateTimeOffset.second;
1155 
1156         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1157             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1158         asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1159 
1160             {"target",
1161              "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
1162         });
1163 }
1164 
1165 inline void requestRoutesJournalEventLogClear(App& app)
1166 {
1167     BMCWEB_ROUTE(
1168         app,
1169         "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
1170         .privileges({{"ConfigureComponents"}})
1171         .methods(boost::beast::http::verb::post)(
1172             [&app](const crow::Request& req,
1173                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1174                    const std::string& systemName) {
1175         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1176         {
1177             return;
1178         }
1179         if (systemName != "system")
1180         {
1181             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1182                                        systemName);
1183             return;
1184         }
1185         // Clear the EventLog by deleting the log files
1186         std::vector<std::filesystem::path> redfishLogFiles;
1187         if (getRedfishLogFiles(redfishLogFiles))
1188         {
1189             for (const std::filesystem::path& file : redfishLogFiles)
1190             {
1191                 std::error_code ec;
1192                 std::filesystem::remove(file, ec);
1193             }
1194         }
1195 
1196         // Reload rsyslog so it knows to start new log files
1197         crow::connections::systemBus->async_method_call(
1198             [asyncResp](const boost::system::error_code ec) {
1199             if (ec)
1200             {
1201                 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1202                 messages::internalError(asyncResp->res);
1203                 return;
1204             }
1205 
1206             messages::success(asyncResp->res);
1207             },
1208             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1209             "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1210             "replace");
1211         });
1212 }
1213 
1214 enum class LogParseError
1215 {
1216     success,
1217     parseFailed,
1218     messageIdNotInRegistry,
1219 };
1220 
1221 static LogParseError
1222     fillEventLogEntryJson(const std::string& logEntryID,
1223                           const std::string& logEntry,
1224                           nlohmann::json::object_t& logEntryJson)
1225 {
1226     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
1227     // First get the Timestamp
1228     size_t space = logEntry.find_first_of(' ');
1229     if (space == std::string::npos)
1230     {
1231         return LogParseError::parseFailed;
1232     }
1233     std::string timestamp = logEntry.substr(0, space);
1234     // Then get the log contents
1235     size_t entryStart = logEntry.find_first_not_of(' ', space);
1236     if (entryStart == std::string::npos)
1237     {
1238         return LogParseError::parseFailed;
1239     }
1240     std::string_view entry(logEntry);
1241     entry.remove_prefix(entryStart);
1242     // Use split to separate the entry into its fields
1243     std::vector<std::string> logEntryFields;
1244     boost::split(logEntryFields, entry, boost::is_any_of(","),
1245                  boost::token_compress_on);
1246     // We need at least a MessageId to be valid
1247     if (logEntryFields.empty())
1248     {
1249         return LogParseError::parseFailed;
1250     }
1251     std::string& messageID = logEntryFields[0];
1252 
1253     // Get the Message from the MessageRegistry
1254     const registries::Message* message = registries::getMessage(messageID);
1255 
1256     if (message == nullptr)
1257     {
1258         BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
1259         return LogParseError::messageIdNotInRegistry;
1260     }
1261 
1262     std::string msg = message->message;
1263 
1264     // Get the MessageArgs from the log if there are any
1265     std::span<std::string> messageArgs;
1266     if (logEntryFields.size() > 1)
1267     {
1268         std::string& messageArgsStart = logEntryFields[1];
1269         // If the first string is empty, assume there are no MessageArgs
1270         std::size_t messageArgsSize = 0;
1271         if (!messageArgsStart.empty())
1272         {
1273             messageArgsSize = logEntryFields.size() - 1;
1274         }
1275 
1276         messageArgs = {&messageArgsStart, messageArgsSize};
1277 
1278         // Fill the MessageArgs into the Message
1279         int i = 0;
1280         for (const std::string& messageArg : messageArgs)
1281         {
1282             std::string argStr = "%" + std::to_string(++i);
1283             size_t argPos = msg.find(argStr);
1284             if (argPos != std::string::npos)
1285             {
1286                 msg.replace(argPos, argStr.length(), messageArg);
1287             }
1288         }
1289     }
1290 
1291     // Get the Created time from the timestamp. The log timestamp is in RFC3339
1292     // format which matches the Redfish format except for the fractional seconds
1293     // between the '.' and the '+', so just remove them.
1294     std::size_t dot = timestamp.find_first_of('.');
1295     std::size_t plus = timestamp.find_first_of('+');
1296     if (dot != std::string::npos && plus != std::string::npos)
1297     {
1298         timestamp.erase(dot, plus - dot);
1299     }
1300 
1301     // Fill in the log entry with the gathered data
1302     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1303     logEntryJson["@odata.id"] =
1304         "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1305     logEntryJson["Name"] = "System Event Log Entry";
1306     logEntryJson["Id"] = logEntryID;
1307     logEntryJson["Message"] = std::move(msg);
1308     logEntryJson["MessageId"] = std::move(messageID);
1309     logEntryJson["MessageArgs"] = messageArgs;
1310     logEntryJson["EntryType"] = "Event";
1311     logEntryJson["Severity"] = message->messageSeverity;
1312     logEntryJson["Created"] = std::move(timestamp);
1313     return LogParseError::success;
1314 }
1315 
1316 inline void requestRoutesJournalEventLogEntryCollection(App& app)
1317 {
1318     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1319         .privileges(redfish::privileges::getLogEntryCollection)
1320         .methods(boost::beast::http::verb::get)(
1321             [&app](const crow::Request& req,
1322                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1323                    const std::string& systemName) {
1324         query_param::QueryCapabilities capabilities = {
1325             .canDelegateTop = true,
1326             .canDelegateSkip = true,
1327         };
1328         query_param::Query delegatedQuery;
1329         if (!redfish::setUpRedfishRouteWithDelegation(
1330                 app, req, asyncResp, delegatedQuery, capabilities))
1331         {
1332             return;
1333         }
1334         if (systemName != "system")
1335         {
1336             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1337                                        systemName);
1338             return;
1339         }
1340 
1341         size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
1342         size_t skip = delegatedQuery.skip.value_or(0);
1343 
1344         // Collections don't include the static data added by SubRoute
1345         // because it has a duplicate entry for members
1346         asyncResp->res.jsonValue["@odata.type"] =
1347             "#LogEntryCollection.LogEntryCollection";
1348         asyncResp->res.jsonValue["@odata.id"] =
1349             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1350         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1351         asyncResp->res.jsonValue["Description"] =
1352             "Collection of System Event Log Entries";
1353 
1354         nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1355         logEntryArray = nlohmann::json::array();
1356         // Go through the log files and create a unique ID for each
1357         // entry
1358         std::vector<std::filesystem::path> redfishLogFiles;
1359         getRedfishLogFiles(redfishLogFiles);
1360         uint64_t entryCount = 0;
1361         std::string logEntry;
1362 
1363         // Oldest logs are in the last file, so start there and loop
1364         // backwards
1365         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1366              it++)
1367         {
1368             std::ifstream logStream(*it);
1369             if (!logStream.is_open())
1370             {
1371                 continue;
1372             }
1373 
1374             // Reset the unique ID on the first entry
1375             bool firstEntry = true;
1376             while (std::getline(logStream, logEntry))
1377             {
1378                 std::string idStr;
1379                 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1380                 {
1381                     continue;
1382                 }
1383                 firstEntry = false;
1384 
1385                 nlohmann::json::object_t bmcLogEntry;
1386                 LogParseError status =
1387                     fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1388                 if (status == LogParseError::messageIdNotInRegistry)
1389                 {
1390                     continue;
1391                 }
1392                 if (status != LogParseError::success)
1393                 {
1394                     messages::internalError(asyncResp->res);
1395                     return;
1396                 }
1397 
1398                 entryCount++;
1399                 // Handle paging using skip (number of entries to skip from the
1400                 // start) and top (number of entries to display)
1401                 if (entryCount <= skip || entryCount > skip + top)
1402                 {
1403                     continue;
1404                 }
1405 
1406                 logEntryArray.push_back(std::move(bmcLogEntry));
1407             }
1408         }
1409         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1410         if (skip + top < entryCount)
1411         {
1412             asyncResp->res.jsonValue["Members@odata.nextLink"] =
1413                 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
1414                 std::to_string(skip + top);
1415         }
1416         });
1417 }
1418 
1419 inline void requestRoutesJournalEventLogEntry(App& app)
1420 {
1421     BMCWEB_ROUTE(
1422         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1423         .privileges(redfish::privileges::getLogEntry)
1424         .methods(boost::beast::http::verb::get)(
1425             [&app](const crow::Request& req,
1426                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1427                    const std::string& systemName, const std::string& param) {
1428         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1429         {
1430             return;
1431         }
1432 
1433         if (systemName != "system")
1434         {
1435             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1436                                        systemName);
1437             return;
1438         }
1439 
1440         const std::string& targetID = param;
1441 
1442         // Go through the log files and check the unique ID for each
1443         // entry to find the target entry
1444         std::vector<std::filesystem::path> redfishLogFiles;
1445         getRedfishLogFiles(redfishLogFiles);
1446         std::string logEntry;
1447 
1448         // Oldest logs are in the last file, so start there and loop
1449         // backwards
1450         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1451              it++)
1452         {
1453             std::ifstream logStream(*it);
1454             if (!logStream.is_open())
1455             {
1456                 continue;
1457             }
1458 
1459             // Reset the unique ID on the first entry
1460             bool firstEntry = true;
1461             while (std::getline(logStream, logEntry))
1462             {
1463                 std::string idStr;
1464                 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1465                 {
1466                     continue;
1467                 }
1468                 firstEntry = false;
1469 
1470                 if (idStr == targetID)
1471                 {
1472                     nlohmann::json::object_t bmcLogEntry;
1473                     LogParseError status =
1474                         fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1475                     if (status != LogParseError::success)
1476                     {
1477                         messages::internalError(asyncResp->res);
1478                         return;
1479                     }
1480                     asyncResp->res.jsonValue.update(bmcLogEntry);
1481                     return;
1482                 }
1483             }
1484         }
1485         // Requested ID was not found
1486         messages::resourceNotFound(asyncResp->res, "LogEntry", targetID);
1487         });
1488 }
1489 
1490 inline void requestRoutesDBusEventLogEntryCollection(App& app)
1491 {
1492     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1493         .privileges(redfish::privileges::getLogEntryCollection)
1494         .methods(boost::beast::http::verb::get)(
1495             [&app](const crow::Request& req,
1496                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1497                    const std::string& systemName) {
1498         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1499         {
1500             return;
1501         }
1502         if (systemName != "system")
1503         {
1504             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1505                                        systemName);
1506             return;
1507         }
1508 
1509         // Collections don't include the static data added by SubRoute
1510         // because it has a duplicate entry for members
1511         asyncResp->res.jsonValue["@odata.type"] =
1512             "#LogEntryCollection.LogEntryCollection";
1513         asyncResp->res.jsonValue["@odata.id"] =
1514             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1515         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1516         asyncResp->res.jsonValue["Description"] =
1517             "Collection of System Event Log Entries";
1518 
1519         // DBus implementation of EventLog/Entries
1520         // Make call to Logging Service to find all log entry objects
1521         crow::connections::systemBus->async_method_call(
1522             [asyncResp](const boost::system::error_code ec,
1523                         const dbus::utility::ManagedObjectType& resp) {
1524             if (ec)
1525             {
1526                 // TODO Handle for specific error code
1527                 BMCWEB_LOG_ERROR
1528                     << "getLogEntriesIfaceData resp_handler got error " << ec;
1529                 messages::internalError(asyncResp->res);
1530                 return;
1531             }
1532             nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1533             entriesArray = nlohmann::json::array();
1534             for (const auto& objectPath : resp)
1535             {
1536                 const uint32_t* id = nullptr;
1537                 const uint64_t* timestamp = nullptr;
1538                 const uint64_t* updateTimestamp = nullptr;
1539                 const std::string* severity = nullptr;
1540                 const std::string* message = nullptr;
1541                 const std::string* filePath = nullptr;
1542                 const std::string* resolution = nullptr;
1543                 bool resolved = false;
1544                 for (const auto& interfaceMap : objectPath.second)
1545                 {
1546                     if (interfaceMap.first ==
1547                         "xyz.openbmc_project.Logging.Entry")
1548                     {
1549                         for (const auto& propertyMap : interfaceMap.second)
1550                         {
1551                             if (propertyMap.first == "Id")
1552                             {
1553                                 id = std::get_if<uint32_t>(&propertyMap.second);
1554                             }
1555                             else if (propertyMap.first == "Timestamp")
1556                             {
1557                                 timestamp =
1558                                     std::get_if<uint64_t>(&propertyMap.second);
1559                             }
1560                             else if (propertyMap.first == "UpdateTimestamp")
1561                             {
1562                                 updateTimestamp =
1563                                     std::get_if<uint64_t>(&propertyMap.second);
1564                             }
1565                             else if (propertyMap.first == "Severity")
1566                             {
1567                                 severity = std::get_if<std::string>(
1568                                     &propertyMap.second);
1569                             }
1570                             else if (propertyMap.first == "Resolution")
1571                             {
1572                                 resolution = std::get_if<std::string>(
1573                                     &propertyMap.second);
1574                             }
1575                             else if (propertyMap.first == "Message")
1576                             {
1577                                 message = std::get_if<std::string>(
1578                                     &propertyMap.second);
1579                             }
1580                             else if (propertyMap.first == "Resolved")
1581                             {
1582                                 const bool* resolveptr =
1583                                     std::get_if<bool>(&propertyMap.second);
1584                                 if (resolveptr == nullptr)
1585                                 {
1586                                     messages::internalError(asyncResp->res);
1587                                     return;
1588                                 }
1589                                 resolved = *resolveptr;
1590                             }
1591                         }
1592                         if (id == nullptr || message == nullptr ||
1593                             severity == nullptr)
1594                         {
1595                             messages::internalError(asyncResp->res);
1596                             return;
1597                         }
1598                     }
1599                     else if (interfaceMap.first ==
1600                              "xyz.openbmc_project.Common.FilePath")
1601                     {
1602                         for (const auto& propertyMap : interfaceMap.second)
1603                         {
1604                             if (propertyMap.first == "Path")
1605                             {
1606                                 filePath = std::get_if<std::string>(
1607                                     &propertyMap.second);
1608                             }
1609                         }
1610                     }
1611                 }
1612                 // Object path without the
1613                 // xyz.openbmc_project.Logging.Entry interface, ignore
1614                 // and continue.
1615                 if (id == nullptr || message == nullptr ||
1616                     severity == nullptr || timestamp == nullptr ||
1617                     updateTimestamp == nullptr)
1618                 {
1619                     continue;
1620                 }
1621                 entriesArray.push_back({});
1622                 nlohmann::json& thisEntry = entriesArray.back();
1623                 thisEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1624                 thisEntry["@odata.id"] =
1625                     "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1626                     std::to_string(*id);
1627                 thisEntry["Name"] = "System Event Log Entry";
1628                 thisEntry["Id"] = std::to_string(*id);
1629                 thisEntry["Message"] = *message;
1630                 thisEntry["Resolved"] = resolved;
1631                 if ((resolution != nullptr) && (!(*resolution).empty()))
1632                 {
1633                     thisEntry["Resolution"] = *resolution;
1634                 }
1635                 thisEntry["EntryType"] = "Event";
1636                 thisEntry["Severity"] =
1637                     translateSeverityDbusToRedfish(*severity);
1638                 thisEntry["Created"] =
1639                     redfish::time_utils::getDateTimeUintMs(*timestamp);
1640                 thisEntry["Modified"] =
1641                     redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
1642                 if (filePath != nullptr)
1643                 {
1644                     thisEntry["AdditionalDataURI"] =
1645                         "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1646                         std::to_string(*id) + "/attachment";
1647                 }
1648             }
1649             std::sort(
1650                 entriesArray.begin(), entriesArray.end(),
1651                 [](const nlohmann::json& left, const nlohmann::json& right) {
1652                 return (left["Id"] <= right["Id"]);
1653                 });
1654             asyncResp->res.jsonValue["Members@odata.count"] =
1655                 entriesArray.size();
1656             },
1657             "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1658             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1659         });
1660 }
1661 
1662 inline void requestRoutesDBusEventLogEntry(App& app)
1663 {
1664     BMCWEB_ROUTE(
1665         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1666         .privileges(redfish::privileges::getLogEntry)
1667         .methods(boost::beast::http::verb::get)(
1668             [&app](const crow::Request& req,
1669                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1670                    const std::string& systemName, const std::string& param) {
1671         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1672         {
1673             return;
1674         }
1675         if (systemName != "system")
1676         {
1677             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1678                                        systemName);
1679             return;
1680         }
1681 
1682         std::string entryID = param;
1683         dbus::utility::escapePathForDbus(entryID);
1684 
1685         // DBus implementation of EventLog/Entries
1686         // Make call to Logging Service to find all log entry objects
1687         sdbusplus::asio::getAllProperties(
1688             *crow::connections::systemBus, "xyz.openbmc_project.Logging",
1689             "/xyz/openbmc_project/logging/entry/" + entryID, "",
1690             [asyncResp, entryID](const boost::system::error_code ec,
1691                                  const dbus::utility::DBusPropertiesMap& resp) {
1692             if (ec.value() == EBADR)
1693             {
1694                 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1695                                            entryID);
1696                 return;
1697             }
1698             if (ec)
1699             {
1700                 BMCWEB_LOG_ERROR
1701                     << "EventLogEntry (DBus) resp_handler got error " << ec;
1702                 messages::internalError(asyncResp->res);
1703                 return;
1704             }
1705             const uint32_t* id = nullptr;
1706             const uint64_t* timestamp = nullptr;
1707             const uint64_t* updateTimestamp = nullptr;
1708             const std::string* severity = nullptr;
1709             const std::string* message = nullptr;
1710             const std::string* filePath = nullptr;
1711             const std::string* resolution = nullptr;
1712             bool resolved = false;
1713 
1714             const bool success = sdbusplus::unpackPropertiesNoThrow(
1715                 dbus_utils::UnpackErrorPrinter(), resp, "Id", id, "Timestamp",
1716                 timestamp, "UpdateTimestamp", updateTimestamp, "Severity",
1717                 severity, "Message", message, "Resolved", resolved,
1718                 "Resolution", resolution, "Path", filePath);
1719 
1720             if (!success)
1721             {
1722                 messages::internalError(asyncResp->res);
1723                 return;
1724             }
1725 
1726             if (id == nullptr || message == nullptr || severity == nullptr ||
1727                 timestamp == nullptr || updateTimestamp == nullptr)
1728             {
1729                 messages::internalError(asyncResp->res);
1730                 return;
1731             }
1732             asyncResp->res.jsonValue["@odata.type"] =
1733                 "#LogEntry.v1_9_0.LogEntry";
1734             asyncResp->res.jsonValue["@odata.id"] =
1735                 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1736                 std::to_string(*id);
1737             asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1738             asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1739             asyncResp->res.jsonValue["Message"] = *message;
1740             asyncResp->res.jsonValue["Resolved"] = resolved;
1741             if ((resolution != nullptr) && (!(*resolution).empty()))
1742             {
1743                 asyncResp->res.jsonValue["Resolution"] = *resolution;
1744             }
1745             asyncResp->res.jsonValue["EntryType"] = "Event";
1746             asyncResp->res.jsonValue["Severity"] =
1747                 translateSeverityDbusToRedfish(*severity);
1748             asyncResp->res.jsonValue["Created"] =
1749                 redfish::time_utils::getDateTimeUintMs(*timestamp);
1750             asyncResp->res.jsonValue["Modified"] =
1751                 redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
1752             if (filePath != nullptr)
1753             {
1754                 asyncResp->res.jsonValue["AdditionalDataURI"] =
1755                     "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1756                     std::to_string(*id) + "/attachment";
1757             }
1758             });
1759         });
1760 
1761     BMCWEB_ROUTE(
1762         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1763         .privileges(redfish::privileges::patchLogEntry)
1764         .methods(boost::beast::http::verb::patch)(
1765             [&app](const crow::Request& req,
1766                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1767                    const std::string& systemName, const std::string& entryId) {
1768         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1769         {
1770             return;
1771         }
1772         if (systemName != "system")
1773         {
1774             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1775                                        systemName);
1776             return;
1777         }
1778         std::optional<bool> resolved;
1779 
1780         if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1781                                       resolved))
1782         {
1783             return;
1784         }
1785         BMCWEB_LOG_DEBUG << "Set Resolved";
1786 
1787         crow::connections::systemBus->async_method_call(
1788             [asyncResp, entryId](const boost::system::error_code ec) {
1789             if (ec)
1790             {
1791                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1792                 messages::internalError(asyncResp->res);
1793                 return;
1794             }
1795             },
1796             "xyz.openbmc_project.Logging",
1797             "/xyz/openbmc_project/logging/entry/" + entryId,
1798             "org.freedesktop.DBus.Properties", "Set",
1799             "xyz.openbmc_project.Logging.Entry", "Resolved",
1800             dbus::utility::DbusVariantType(*resolved));
1801         });
1802 
1803     BMCWEB_ROUTE(
1804         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1805         .privileges(redfish::privileges::deleteLogEntry)
1806 
1807         .methods(boost::beast::http::verb::delete_)(
1808             [&app](const crow::Request& req,
1809                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1810                    const std::string& systemName, const std::string& param) {
1811         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1812         {
1813             return;
1814         }
1815         if (systemName != "system")
1816         {
1817             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1818                                        systemName);
1819             return;
1820         }
1821         BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1822 
1823         std::string entryID = param;
1824 
1825         dbus::utility::escapePathForDbus(entryID);
1826 
1827         // Process response from Logging service.
1828         auto respHandler =
1829             [asyncResp, entryID](const boost::system::error_code ec) {
1830             BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1831             if (ec)
1832             {
1833                 if (ec.value() == EBADR)
1834                 {
1835                     messages::resourceNotFound(asyncResp->res, "LogEntry",
1836                                                entryID);
1837                     return;
1838                 }
1839                 // TODO Handle for specific error code
1840                 BMCWEB_LOG_ERROR
1841                     << "EventLogEntry (DBus) doDelete respHandler got error "
1842                     << ec;
1843                 asyncResp->res.result(
1844                     boost::beast::http::status::internal_server_error);
1845                 return;
1846             }
1847 
1848             asyncResp->res.result(boost::beast::http::status::ok);
1849         };
1850 
1851         // Make call to Logging service to request Delete Log
1852         crow::connections::systemBus->async_method_call(
1853             respHandler, "xyz.openbmc_project.Logging",
1854             "/xyz/openbmc_project/logging/entry/" + entryID,
1855             "xyz.openbmc_project.Object.Delete", "Delete");
1856         });
1857 }
1858 
1859 inline void requestRoutesDBusEventLogEntryDownload(App& app)
1860 {
1861     BMCWEB_ROUTE(
1862         app,
1863         "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment")
1864         .privileges(redfish::privileges::getLogEntry)
1865         .methods(boost::beast::http::verb::get)(
1866             [&app](const crow::Request& req,
1867                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1868                    const std::string& systemName, const std::string& param) {
1869         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1870         {
1871             return;
1872         }
1873         if (http_helpers::isContentTypeAllowed(
1874                 req.getHeaderValue("Accept"),
1875                 http_helpers::ContentType::OctetStream, true))
1876         {
1877             asyncResp->res.result(boost::beast::http::status::bad_request);
1878             return;
1879         }
1880         if (systemName != "system")
1881         {
1882             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1883                                        systemName);
1884             return;
1885         }
1886 
1887         std::string entryID = param;
1888         dbus::utility::escapePathForDbus(entryID);
1889 
1890         crow::connections::systemBus->async_method_call(
1891             [asyncResp, entryID](const boost::system::error_code ec,
1892                                  const sdbusplus::message::unix_fd& unixfd) {
1893             if (ec.value() == EBADR)
1894             {
1895                 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1896                                            entryID);
1897                 return;
1898             }
1899             if (ec)
1900             {
1901                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1902                 messages::internalError(asyncResp->res);
1903                 return;
1904             }
1905 
1906             int fd = -1;
1907             fd = dup(unixfd);
1908             if (fd == -1)
1909             {
1910                 messages::internalError(asyncResp->res);
1911                 return;
1912             }
1913 
1914             long long int size = lseek(fd, 0, SEEK_END);
1915             if (size == -1)
1916             {
1917                 messages::internalError(asyncResp->res);
1918                 return;
1919             }
1920 
1921             // Arbitrary max size of 64kb
1922             constexpr int maxFileSize = 65536;
1923             if (size > maxFileSize)
1924             {
1925                 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1926                                  << maxFileSize;
1927                 messages::internalError(asyncResp->res);
1928                 return;
1929             }
1930             std::vector<char> data(static_cast<size_t>(size));
1931             long long int rc = lseek(fd, 0, SEEK_SET);
1932             if (rc == -1)
1933             {
1934                 messages::internalError(asyncResp->res);
1935                 return;
1936             }
1937             rc = read(fd, data.data(), data.size());
1938             if ((rc == -1) || (rc != size))
1939             {
1940                 messages::internalError(asyncResp->res);
1941                 return;
1942             }
1943             close(fd);
1944 
1945             std::string_view strData(data.data(), data.size());
1946             std::string output = crow::utility::base64encode(strData);
1947 
1948             asyncResp->res.addHeader(boost::beast::http::field::content_type,
1949                                      "application/octet-stream");
1950             asyncResp->res.addHeader(
1951                 boost::beast::http::field::content_transfer_encoding, "Base64");
1952             asyncResp->res.body() = std::move(output);
1953             },
1954             "xyz.openbmc_project.Logging",
1955             "/xyz/openbmc_project/logging/entry/" + entryID,
1956             "xyz.openbmc_project.Logging.Entry", "GetEntry");
1957         });
1958 }
1959 
1960 constexpr const char* hostLoggerFolderPath = "/var/log/console";
1961 
1962 inline bool
1963     getHostLoggerFiles(const std::string& hostLoggerFilePath,
1964                        std::vector<std::filesystem::path>& hostLoggerFiles)
1965 {
1966     std::error_code ec;
1967     std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1968     if (ec)
1969     {
1970         BMCWEB_LOG_ERROR << ec.message();
1971         return false;
1972     }
1973     for (const std::filesystem::directory_entry& it : logPath)
1974     {
1975         std::string filename = it.path().filename();
1976         // Prefix of each log files is "log". Find the file and save the
1977         // path
1978         if (filename.starts_with("log"))
1979         {
1980             hostLoggerFiles.emplace_back(it.path());
1981         }
1982     }
1983     // As the log files rotate, they are appended with a ".#" that is higher for
1984     // the older logs. Since we start from oldest logs, sort the name in
1985     // descending order.
1986     std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1987               AlphanumLess<std::string>());
1988 
1989     return true;
1990 }
1991 
1992 inline bool getHostLoggerEntries(
1993     const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1994     uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
1995 {
1996     GzFileReader logFile;
1997 
1998     // Go though all log files and expose host logs.
1999     for (const std::filesystem::path& it : hostLoggerFiles)
2000     {
2001         if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
2002         {
2003             BMCWEB_LOG_ERROR << "fail to expose host logs";
2004             return false;
2005         }
2006     }
2007     // Get lastMessage from constructor by getter
2008     std::string lastMessage = logFile.getLastMessage();
2009     if (!lastMessage.empty())
2010     {
2011         logCount++;
2012         if (logCount > skip && logCount <= (skip + top))
2013         {
2014             logEntries.push_back(lastMessage);
2015         }
2016     }
2017     return true;
2018 }
2019 
2020 inline void fillHostLoggerEntryJson(const std::string& logEntryID,
2021                                     const std::string& msg,
2022                                     nlohmann::json::object_t& logEntryJson)
2023 {
2024     // Fill in the log entry with the gathered data.
2025     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
2026     logEntryJson["@odata.id"] =
2027         "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
2028         logEntryID;
2029     logEntryJson["Name"] = "Host Logger Entry";
2030     logEntryJson["Id"] = logEntryID;
2031     logEntryJson["Message"] = msg;
2032     logEntryJson["EntryType"] = "Oem";
2033     logEntryJson["Severity"] = "OK";
2034     logEntryJson["OemRecordFormat"] = "Host Logger Entry";
2035 }
2036 
2037 inline void requestRoutesSystemHostLogger(App& app)
2038 {
2039     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/")
2040         .privileges(redfish::privileges::getLogService)
2041         .methods(boost::beast::http::verb::get)(
2042             [&app](const crow::Request& req,
2043                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2044                    const std::string& systemName) {
2045         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2046         {
2047             return;
2048         }
2049         if (systemName != "system")
2050         {
2051             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2052                                        systemName);
2053             return;
2054         }
2055         asyncResp->res.jsonValue["@odata.id"] =
2056             "/redfish/v1/Systems/system/LogServices/HostLogger";
2057         asyncResp->res.jsonValue["@odata.type"] =
2058             "#LogService.v1_1_0.LogService";
2059         asyncResp->res.jsonValue["Name"] = "Host Logger Service";
2060         asyncResp->res.jsonValue["Description"] = "Host Logger Service";
2061         asyncResp->res.jsonValue["Id"] = "HostLogger";
2062         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2063             "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
2064         });
2065 }
2066 
2067 inline void requestRoutesSystemHostLoggerCollection(App& app)
2068 {
2069     BMCWEB_ROUTE(app,
2070                  "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/")
2071         .privileges(redfish::privileges::getLogEntry)
2072         .methods(boost::beast::http::verb::get)(
2073             [&app](const crow::Request& req,
2074                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2075                    const std::string& systemName) {
2076         query_param::QueryCapabilities capabilities = {
2077             .canDelegateTop = true,
2078             .canDelegateSkip = true,
2079         };
2080         query_param::Query delegatedQuery;
2081         if (!redfish::setUpRedfishRouteWithDelegation(
2082                 app, req, asyncResp, delegatedQuery, capabilities))
2083         {
2084             return;
2085         }
2086         if (systemName != "system")
2087         {
2088             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2089                                        systemName);
2090             return;
2091         }
2092         asyncResp->res.jsonValue["@odata.id"] =
2093             "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
2094         asyncResp->res.jsonValue["@odata.type"] =
2095             "#LogEntryCollection.LogEntryCollection";
2096         asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
2097         asyncResp->res.jsonValue["Description"] =
2098             "Collection of HostLogger Entries";
2099         nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2100         logEntryArray = nlohmann::json::array();
2101         asyncResp->res.jsonValue["Members@odata.count"] = 0;
2102 
2103         std::vector<std::filesystem::path> hostLoggerFiles;
2104         if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2105         {
2106             BMCWEB_LOG_ERROR << "fail to get host log file path";
2107             return;
2108         }
2109         // If we weren't provided top and skip limits, use the defaults.
2110         size_t skip = delegatedQuery.skip.value_or(0);
2111         size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
2112         size_t logCount = 0;
2113         // This vector only store the entries we want to expose that
2114         // control by skip and top.
2115         std::vector<std::string> logEntries;
2116         if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
2117                                   logCount))
2118         {
2119             messages::internalError(asyncResp->res);
2120             return;
2121         }
2122         // If vector is empty, that means skip value larger than total
2123         // log count
2124         if (logEntries.empty())
2125         {
2126             asyncResp->res.jsonValue["Members@odata.count"] = logCount;
2127             return;
2128         }
2129         if (!logEntries.empty())
2130         {
2131             for (size_t i = 0; i < logEntries.size(); i++)
2132             {
2133                 nlohmann::json::object_t hostLogEntry;
2134                 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
2135                                         hostLogEntry);
2136                 logEntryArray.push_back(std::move(hostLogEntry));
2137             }
2138 
2139             asyncResp->res.jsonValue["Members@odata.count"] = logCount;
2140             if (skip + top < logCount)
2141             {
2142                 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2143                     "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
2144                     std::to_string(skip + top);
2145             }
2146         }
2147         });
2148 }
2149 
2150 inline void requestRoutesSystemHostLoggerLogEntry(App& app)
2151 {
2152     BMCWEB_ROUTE(
2153         app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/<str>/")
2154         .privileges(redfish::privileges::getLogEntry)
2155         .methods(boost::beast::http::verb::get)(
2156             [&app](const crow::Request& req,
2157                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2158                    const std::string& systemName, const std::string& param) {
2159         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2160         {
2161             return;
2162         }
2163         if (systemName != "system")
2164         {
2165             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2166                                        systemName);
2167             return;
2168         }
2169         const std::string& targetID = param;
2170 
2171         uint64_t idInt = 0;
2172 
2173         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
2174         const char* end = targetID.data() + targetID.size();
2175 
2176         auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
2177         if (ec == std::errc::invalid_argument ||
2178             ec == std::errc::result_out_of_range)
2179         {
2180             messages::resourceNotFound(asyncResp->res, "LogEntry", param);
2181             return;
2182         }
2183 
2184         std::vector<std::filesystem::path> hostLoggerFiles;
2185         if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2186         {
2187             BMCWEB_LOG_ERROR << "fail to get host log file path";
2188             return;
2189         }
2190 
2191         size_t logCount = 0;
2192         size_t top = 1;
2193         std::vector<std::string> logEntries;
2194         // We can get specific entry by skip and top. For example, if we
2195         // want to get nth entry, we can set skip = n-1 and top = 1 to
2196         // get that entry
2197         if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
2198                                   logCount))
2199         {
2200             messages::internalError(asyncResp->res);
2201             return;
2202         }
2203 
2204         if (!logEntries.empty())
2205         {
2206             nlohmann::json::object_t hostLogEntry;
2207             fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
2208             asyncResp->res.jsonValue.update(hostLogEntry);
2209             return;
2210         }
2211 
2212         // Requested ID was not found
2213         messages::resourceNotFound(asyncResp->res, "LogEntry", param);
2214         });
2215 }
2216 
2217 constexpr char const* dumpManagerIface =
2218     "xyz.openbmc_project.Collection.DeleteAll";
2219 inline void handleBMCLogServicesCollectionGet(
2220     crow::App& app, const crow::Request& req,
2221     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2222 {
2223     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2224     {
2225         return;
2226     }
2227     // Collections don't include the static data added by SubRoute
2228     // because it has a duplicate entry for members
2229     asyncResp->res.jsonValue["@odata.type"] =
2230         "#LogServiceCollection.LogServiceCollection";
2231     asyncResp->res.jsonValue["@odata.id"] =
2232         "/redfish/v1/Managers/bmc/LogServices";
2233     asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2234     asyncResp->res.jsonValue["Description"] =
2235         "Collection of LogServices for this Manager";
2236     nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2237     logServiceArray = nlohmann::json::array();
2238 
2239 #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
2240     nlohmann::json::object_t journal;
2241     journal["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/Journal";
2242     logServiceArray.push_back(std::move(journal));
2243 #endif
2244 
2245     asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2246 
2247 #ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2248     auto respHandler =
2249         [asyncResp](
2250             const boost::system::error_code ec,
2251             const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2252         if (ec)
2253         {
2254             BMCWEB_LOG_ERROR
2255                 << "handleBMCLogServicesCollectionGet respHandler got error "
2256                 << ec;
2257             // Assume that getting an error simply means there are no dump
2258             // LogServices. Return without adding any error response.
2259             return;
2260         }
2261 
2262         nlohmann::json& logServiceArrayLocal =
2263             asyncResp->res.jsonValue["Members"];
2264 
2265         for (const std::string& path : subTreePaths)
2266         {
2267             if (path == "/xyz/openbmc_project/dump/bmc")
2268             {
2269                 nlohmann::json::object_t member;
2270                 member["@odata.id"] =
2271                     "/redfish/v1/Managers/bmc/LogServices/Dump";
2272                 logServiceArrayLocal.push_back(std::move(member));
2273             }
2274             else if (path == "/xyz/openbmc_project/dump/faultlog")
2275             {
2276                 nlohmann::json::object_t member;
2277                 member["@odata.id"] =
2278                     "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2279                 logServiceArrayLocal.push_back(std::move(member));
2280             }
2281         }
2282 
2283         asyncResp->res.jsonValue["Members@odata.count"] =
2284             logServiceArrayLocal.size();
2285     };
2286 
2287     crow::connections::systemBus->async_method_call(
2288         respHandler, "xyz.openbmc_project.ObjectMapper",
2289         "/xyz/openbmc_project/object_mapper",
2290         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2291         "/xyz/openbmc_project/dump", 0,
2292         std::array<const char*, 1>{dumpManagerIface});
2293 #endif
2294 }
2295 
2296 inline void requestRoutesBMCLogServiceCollection(App& app)
2297 {
2298     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
2299         .privileges(redfish::privileges::getLogServiceCollection)
2300         .methods(boost::beast::http::verb::get)(
2301             std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app)));
2302 }
2303 
2304 inline void requestRoutesBMCJournalLogService(App& app)
2305 {
2306     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
2307         .privileges(redfish::privileges::getLogService)
2308         .methods(boost::beast::http::verb::get)(
2309             [&app](const crow::Request& req,
2310                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2311         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2312         {
2313             return;
2314         }
2315         asyncResp->res.jsonValue["@odata.type"] =
2316             "#LogService.v1_1_0.LogService";
2317         asyncResp->res.jsonValue["@odata.id"] =
2318             "/redfish/v1/Managers/bmc/LogServices/Journal";
2319         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2320         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2321         asyncResp->res.jsonValue["Id"] = "BMC Journal";
2322         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2323 
2324         std::pair<std::string, std::string> redfishDateTimeOffset =
2325             redfish::time_utils::getDateTimeOffsetNow();
2326         asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2327         asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2328             redfishDateTimeOffset.second;
2329 
2330         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2331             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2332         });
2333 }
2334 
2335 static int
2336     fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2337                                sd_journal* journal,
2338                                nlohmann::json::object_t& bmcJournalLogEntryJson)
2339 {
2340     // Get the Log Entry contents
2341     int ret = 0;
2342 
2343     std::string message;
2344     std::string_view syslogID;
2345     ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2346     if (ret < 0)
2347     {
2348         BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2349                          << strerror(-ret);
2350     }
2351     if (!syslogID.empty())
2352     {
2353         message += std::string(syslogID) + ": ";
2354     }
2355 
2356     std::string_view msg;
2357     ret = getJournalMetadata(journal, "MESSAGE", msg);
2358     if (ret < 0)
2359     {
2360         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2361         return 1;
2362     }
2363     message += std::string(msg);
2364 
2365     // Get the severity from the PRIORITY field
2366     long int severity = 8; // Default to an invalid priority
2367     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
2368     if (ret < 0)
2369     {
2370         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
2371     }
2372 
2373     // Get the Created time from the timestamp
2374     std::string entryTimeStr;
2375     if (!getEntryTimestamp(journal, entryTimeStr))
2376     {
2377         return 1;
2378     }
2379 
2380     // Fill in the log entry with the gathered data
2381     bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
2382     bmcJournalLogEntryJson["@odata.id"] =
2383         "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2384         bmcJournalLogEntryID;
2385     bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2386     bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2387     bmcJournalLogEntryJson["Message"] = std::move(message);
2388     bmcJournalLogEntryJson["EntryType"] = "Oem";
2389     bmcJournalLogEntryJson["Severity"] = severity <= 2   ? "Critical"
2390                                          : severity <= 4 ? "Warning"
2391                                                          : "OK";
2392     bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2393     bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
2394     return 0;
2395 }
2396 
2397 inline void requestRoutesBMCJournalLogEntryCollection(App& app)
2398 {
2399     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
2400         .privileges(redfish::privileges::getLogEntryCollection)
2401         .methods(boost::beast::http::verb::get)(
2402             [&app](const crow::Request& req,
2403                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2404         query_param::QueryCapabilities capabilities = {
2405             .canDelegateTop = true,
2406             .canDelegateSkip = true,
2407         };
2408         query_param::Query delegatedQuery;
2409         if (!redfish::setUpRedfishRouteWithDelegation(
2410                 app, req, asyncResp, delegatedQuery, capabilities))
2411         {
2412             return;
2413         }
2414 
2415         size_t skip = delegatedQuery.skip.value_or(0);
2416         size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
2417 
2418         // Collections don't include the static data added by SubRoute
2419         // because it has a duplicate entry for members
2420         asyncResp->res.jsonValue["@odata.type"] =
2421             "#LogEntryCollection.LogEntryCollection";
2422         asyncResp->res.jsonValue["@odata.id"] =
2423             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2424         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2425         asyncResp->res.jsonValue["Description"] =
2426             "Collection of BMC Journal Entries";
2427         nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2428         logEntryArray = nlohmann::json::array();
2429 
2430         // Go through the journal and use the timestamp to create a
2431         // unique ID for each entry
2432         sd_journal* journalTmp = nullptr;
2433         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2434         if (ret < 0)
2435         {
2436             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2437             messages::internalError(asyncResp->res);
2438             return;
2439         }
2440         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2441             journalTmp, sd_journal_close);
2442         journalTmp = nullptr;
2443         uint64_t entryCount = 0;
2444         // Reset the unique ID on the first entry
2445         bool firstEntry = true;
2446         SD_JOURNAL_FOREACH(journal.get())
2447         {
2448             entryCount++;
2449             // Handle paging using skip (number of entries to skip from
2450             // the start) and top (number of entries to display)
2451             if (entryCount <= skip || entryCount > skip + top)
2452             {
2453                 continue;
2454             }
2455 
2456             std::string idStr;
2457             if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2458             {
2459                 continue;
2460             }
2461             firstEntry = false;
2462 
2463             nlohmann::json::object_t bmcJournalLogEntry;
2464             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2465                                            bmcJournalLogEntry) != 0)
2466             {
2467                 messages::internalError(asyncResp->res);
2468                 return;
2469             }
2470             logEntryArray.push_back(std::move(bmcJournalLogEntry));
2471         }
2472         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2473         if (skip + top < entryCount)
2474         {
2475             asyncResp->res.jsonValue["Members@odata.nextLink"] =
2476                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2477                 std::to_string(skip + top);
2478         }
2479         });
2480 }
2481 
2482 inline void requestRoutesBMCJournalLogEntry(App& app)
2483 {
2484     BMCWEB_ROUTE(app,
2485                  "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
2486         .privileges(redfish::privileges::getLogEntry)
2487         .methods(boost::beast::http::verb::get)(
2488             [&app](const crow::Request& req,
2489                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2490                    const std::string& entryID) {
2491         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2492         {
2493             return;
2494         }
2495         // Convert the unique ID back to a timestamp to find the entry
2496         uint64_t ts = 0;
2497         uint64_t index = 0;
2498         if (!getTimestampFromID(asyncResp, entryID, ts, index))
2499         {
2500             return;
2501         }
2502 
2503         sd_journal* journalTmp = nullptr;
2504         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2505         if (ret < 0)
2506         {
2507             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2508             messages::internalError(asyncResp->res);
2509             return;
2510         }
2511         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2512             journalTmp, sd_journal_close);
2513         journalTmp = nullptr;
2514         // Go to the timestamp in the log and move to the entry at the
2515         // index tracking the unique ID
2516         std::string idStr;
2517         bool firstEntry = true;
2518         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2519         if (ret < 0)
2520         {
2521             BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2522                              << strerror(-ret);
2523             messages::internalError(asyncResp->res);
2524             return;
2525         }
2526         for (uint64_t i = 0; i <= index; i++)
2527         {
2528             sd_journal_next(journal.get());
2529             if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2530             {
2531                 messages::internalError(asyncResp->res);
2532                 return;
2533             }
2534             firstEntry = false;
2535         }
2536         // Confirm that the entry ID matches what was requested
2537         if (idStr != entryID)
2538         {
2539             messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
2540             return;
2541         }
2542 
2543         nlohmann::json::object_t bmcJournalLogEntry;
2544         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2545                                        bmcJournalLogEntry) != 0)
2546         {
2547             messages::internalError(asyncResp->res);
2548             return;
2549         }
2550         asyncResp->res.jsonValue.update(bmcJournalLogEntry);
2551         });
2552 }
2553 
2554 inline void
2555     getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2556                        const std::string& dumpType)
2557 {
2558     std::string dumpPath;
2559     std::string overWritePolicy;
2560     bool collectDiagnosticDataSupported = false;
2561 
2562     if (dumpType == "BMC")
2563     {
2564         dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2565         overWritePolicy = "WrapsWhenFull";
2566         collectDiagnosticDataSupported = true;
2567     }
2568     else if (dumpType == "FaultLog")
2569     {
2570         dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2571         overWritePolicy = "Unknown";
2572         collectDiagnosticDataSupported = false;
2573     }
2574     else if (dumpType == "System")
2575     {
2576         dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2577         overWritePolicy = "WrapsWhenFull";
2578         collectDiagnosticDataSupported = true;
2579     }
2580     else
2581     {
2582         BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2583                          << dumpType;
2584         messages::internalError(asyncResp->res);
2585         return;
2586     }
2587 
2588     asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2589     asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2590     asyncResp->res.jsonValue["Name"] = "Dump LogService";
2591     asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2592     asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2593     asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2594 
2595     std::pair<std::string, std::string> redfishDateTimeOffset =
2596         redfish::time_utils::getDateTimeOffsetNow();
2597     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2598     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2599         redfishDateTimeOffset.second;
2600 
2601     asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2602     asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2603         dumpPath + "/Actions/LogService.ClearLog";
2604 
2605     if (collectDiagnosticDataSupported)
2606     {
2607         asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2608                                 ["target"] =
2609             dumpPath + "/Actions/LogService.CollectDiagnosticData";
2610     }
2611 }
2612 
2613 inline void handleLogServicesDumpServiceGet(
2614     crow::App& app, const std::string& dumpType, const crow::Request& req,
2615     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2616 {
2617     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2618     {
2619         return;
2620     }
2621     getDumpServiceInfo(asyncResp, dumpType);
2622 }
2623 
2624 inline void handleLogServicesDumpServiceComputerSystemGet(
2625     crow::App& app, const crow::Request& req,
2626     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2627     const std::string& chassisId)
2628 {
2629     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2630     {
2631         return;
2632     }
2633     if (chassisId != "system")
2634     {
2635         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2636         return;
2637     }
2638     getDumpServiceInfo(asyncResp, "System");
2639 }
2640 
2641 inline void handleLogServicesDumpEntriesCollectionGet(
2642     crow::App& app, const std::string& dumpType, const crow::Request& req,
2643     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2644 {
2645     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2646     {
2647         return;
2648     }
2649     getDumpEntryCollection(asyncResp, dumpType);
2650 }
2651 
2652 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet(
2653     crow::App& app, const crow::Request& req,
2654     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2655     const std::string& chassisId)
2656 {
2657     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2658     {
2659         return;
2660     }
2661     if (chassisId != "system")
2662     {
2663         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2664         return;
2665     }
2666     getDumpEntryCollection(asyncResp, "System");
2667 }
2668 
2669 inline void handleLogServicesDumpEntryGet(
2670     crow::App& app, const std::string& dumpType, const crow::Request& req,
2671     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2672     const std::string& dumpId)
2673 {
2674     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2675     {
2676         return;
2677     }
2678     getDumpEntryById(asyncResp, dumpId, dumpType);
2679 }
2680 inline void handleLogServicesDumpEntryComputerSystemGet(
2681     crow::App& app, const crow::Request& req,
2682     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2683     const std::string& chassisId, const std::string& dumpId)
2684 {
2685     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2686     {
2687         return;
2688     }
2689     if (chassisId != "system")
2690     {
2691         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2692         return;
2693     }
2694     getDumpEntryById(asyncResp, dumpId, "System");
2695 }
2696 
2697 inline void handleLogServicesDumpEntryDelete(
2698     crow::App& app, const std::string& dumpType, const crow::Request& req,
2699     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2700     const std::string& dumpId)
2701 {
2702     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2703     {
2704         return;
2705     }
2706     deleteDumpEntry(asyncResp, dumpId, dumpType);
2707 }
2708 
2709 inline void handleLogServicesDumpEntryComputerSystemDelete(
2710     crow::App& app, const crow::Request& req,
2711     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2712     const std::string& chassisId, const std::string& dumpId)
2713 {
2714     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2715     {
2716         return;
2717     }
2718     if (chassisId != "system")
2719     {
2720         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2721         return;
2722     }
2723     deleteDumpEntry(asyncResp, dumpId, "System");
2724 }
2725 
2726 inline void handleLogServicesDumpCollectDiagnosticDataPost(
2727     crow::App& app, const std::string& dumpType, const crow::Request& req,
2728     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2729 {
2730     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2731     {
2732         return;
2733     }
2734     createDump(asyncResp, req, dumpType);
2735 }
2736 
2737 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(
2738     crow::App& app, const crow::Request& req,
2739     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2740     const std::string& chassisId)
2741 {
2742     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2743     {
2744         return;
2745     }
2746     if (chassisId != "system")
2747     {
2748         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2749         return;
2750     }
2751     createDump(asyncResp, req, "System");
2752 }
2753 
2754 inline void handleLogServicesDumpClearLogPost(
2755     crow::App& app, const std::string& dumpType, const crow::Request& req,
2756     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2757 {
2758     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2759     {
2760         return;
2761     }
2762     clearDump(asyncResp, dumpType);
2763 }
2764 
2765 inline void handleLogServicesDumpClearLogComputerSystemPost(
2766     crow::App& app, const crow::Request& req,
2767     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2768     const std::string& chassisId)
2769 {
2770     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2771     {
2772         return;
2773     }
2774     if (chassisId != "system")
2775     {
2776         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2777         return;
2778     }
2779     clearDump(asyncResp, "System");
2780 }
2781 
2782 inline void requestRoutesBMCDumpService(App& app)
2783 {
2784     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
2785         .privileges(redfish::privileges::getLogService)
2786         .methods(boost::beast::http::verb::get)(std::bind_front(
2787             handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
2788 }
2789 
2790 inline void requestRoutesBMCDumpEntryCollection(App& app)
2791 {
2792     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
2793         .privileges(redfish::privileges::getLogEntryCollection)
2794         .methods(boost::beast::http::verb::get)(std::bind_front(
2795             handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
2796 }
2797 
2798 inline void requestRoutesBMCDumpEntry(App& app)
2799 {
2800     BMCWEB_ROUTE(app,
2801                  "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
2802         .privileges(redfish::privileges::getLogEntry)
2803         .methods(boost::beast::http::verb::get)(std::bind_front(
2804             handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2805 
2806     BMCWEB_ROUTE(app,
2807                  "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
2808         .privileges(redfish::privileges::deleteLogEntry)
2809         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2810             handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
2811 }
2812 
2813 inline void requestRoutesBMCDumpCreate(App& app)
2814 {
2815     BMCWEB_ROUTE(
2816         app,
2817         "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2818         .privileges(redfish::privileges::postLogService)
2819         .methods(boost::beast::http::verb::post)(
2820             std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2821                             std::ref(app), "BMC"));
2822 }
2823 
2824 inline void requestRoutesBMCDumpClear(App& app)
2825 {
2826     BMCWEB_ROUTE(
2827         app,
2828         "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
2829         .privileges(redfish::privileges::postLogService)
2830         .methods(boost::beast::http::verb::post)(std::bind_front(
2831             handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2832 }
2833 
2834 inline void requestRoutesFaultLogDumpService(App& app)
2835 {
2836     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2837         .privileges(redfish::privileges::getLogService)
2838         .methods(boost::beast::http::verb::get)(std::bind_front(
2839             handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2840 }
2841 
2842 inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2843 {
2844     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2845         .privileges(redfish::privileges::getLogEntryCollection)
2846         .methods(boost::beast::http::verb::get)(
2847             std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2848                             std::ref(app), "FaultLog"));
2849 }
2850 
2851 inline void requestRoutesFaultLogDumpEntry(App& app)
2852 {
2853     BMCWEB_ROUTE(app,
2854                  "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2855         .privileges(redfish::privileges::getLogEntry)
2856         .methods(boost::beast::http::verb::get)(std::bind_front(
2857             handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2858 
2859     BMCWEB_ROUTE(app,
2860                  "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2861         .privileges(redfish::privileges::deleteLogEntry)
2862         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2863             handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2864 }
2865 
2866 inline void requestRoutesFaultLogDumpClear(App& app)
2867 {
2868     BMCWEB_ROUTE(
2869         app,
2870         "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2871         .privileges(redfish::privileges::postLogService)
2872         .methods(boost::beast::http::verb::post)(std::bind_front(
2873             handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
2874 }
2875 
2876 inline void requestRoutesSystemDumpService(App& app)
2877 {
2878     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/")
2879         .privileges(redfish::privileges::getLogService)
2880         .methods(boost::beast::http::verb::get)(std::bind_front(
2881             handleLogServicesDumpServiceComputerSystemGet, std::ref(app)));
2882 }
2883 
2884 inline void requestRoutesSystemDumpEntryCollection(App& app)
2885 {
2886     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/")
2887         .privileges(redfish::privileges::getLogEntryCollection)
2888         .methods(boost::beast::http::verb::get)(std::bind_front(
2889             handleLogServicesDumpEntriesCollectionComputerSystemGet,
2890             std::ref(app)));
2891 }
2892 
2893 inline void requestRoutesSystemDumpEntry(App& app)
2894 {
2895     BMCWEB_ROUTE(app,
2896                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2897         .privileges(redfish::privileges::getLogEntry)
2898         .methods(boost::beast::http::verb::get)(std::bind_front(
2899             handleLogServicesDumpEntryComputerSystemGet, std::ref(app)));
2900 
2901     BMCWEB_ROUTE(app,
2902                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2903         .privileges(redfish::privileges::deleteLogEntry)
2904         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2905             handleLogServicesDumpEntryComputerSystemDelete, std::ref(app)));
2906 }
2907 
2908 inline void requestRoutesSystemDumpCreate(App& app)
2909 {
2910     BMCWEB_ROUTE(
2911         app,
2912         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2913         .privileges(redfish::privileges::postLogService)
2914         .methods(boost::beast::http::verb::post)(std::bind_front(
2915             handleLogServicesDumpCollectDiagnosticDataComputerSystemPost,
2916             std::ref(app)));
2917 }
2918 
2919 inline void requestRoutesSystemDumpClear(App& app)
2920 {
2921     BMCWEB_ROUTE(
2922         app,
2923         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2924         .privileges(redfish::privileges::postLogService)
2925         .methods(boost::beast::http::verb::post)(std::bind_front(
2926             handleLogServicesDumpClearLogComputerSystemPost, std::ref(app)));
2927 }
2928 
2929 inline void requestRoutesCrashdumpService(App& app)
2930 {
2931     // Note: Deviated from redfish privilege registry for GET & HEAD
2932     // method for security reasons.
2933     /**
2934      * Functions triggers appropriate requests on DBus
2935      */
2936     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/")
2937         // This is incorrect, should be:
2938         //.privileges(redfish::privileges::getLogService)
2939         .privileges({{"ConfigureManager"}})
2940         .methods(boost::beast::http::verb::get)(
2941             [&app](const crow::Request& req,
2942                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2943                    const std::string& systemName) {
2944         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2945         {
2946             return;
2947         }
2948         if (systemName != "system")
2949         {
2950             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2951                                        systemName);
2952             return;
2953         }
2954 
2955         // Copy over the static data to include the entries added by
2956         // SubRoute
2957         asyncResp->res.jsonValue["@odata.id"] =
2958             "/redfish/v1/Systems/system/LogServices/Crashdump";
2959         asyncResp->res.jsonValue["@odata.type"] =
2960             "#LogService.v1_2_0.LogService";
2961         asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2962         asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2963         asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2964         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2965         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
2966 
2967         std::pair<std::string, std::string> redfishDateTimeOffset =
2968             redfish::time_utils::getDateTimeOffsetNow();
2969         asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2970         asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2971             redfishDateTimeOffset.second;
2972 
2973         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2974             "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2975         asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2976             "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2977         asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2978                                 ["target"] =
2979             "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
2980         });
2981 }
2982 
2983 void inline requestRoutesCrashdumpClear(App& app)
2984 {
2985     BMCWEB_ROUTE(
2986         app,
2987         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/")
2988         // This is incorrect, should be:
2989         //.privileges(redfish::privileges::postLogService)
2990         .privileges({{"ConfigureComponents"}})
2991         .methods(boost::beast::http::verb::post)(
2992             [&app](const crow::Request& req,
2993                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2994                    const std::string& systemName) {
2995         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2996         {
2997             return;
2998         }
2999         if (systemName != "system")
3000         {
3001             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3002                                        systemName);
3003             return;
3004         }
3005         crow::connections::systemBus->async_method_call(
3006             [asyncResp](const boost::system::error_code ec,
3007                         const std::string&) {
3008             if (ec)
3009             {
3010                 messages::internalError(asyncResp->res);
3011                 return;
3012             }
3013             messages::success(asyncResp->res);
3014             },
3015             crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
3016         });
3017 }
3018 
3019 static void
3020     logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3021                       const std::string& logID, nlohmann::json& logEntryJson)
3022 {
3023     auto getStoredLogCallback =
3024         [asyncResp, logID,
3025          &logEntryJson](const boost::system::error_code ec,
3026                         const dbus::utility::DBusPropertiesMap& params) {
3027         if (ec)
3028         {
3029             BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
3030             if (ec.value() ==
3031                 boost::system::linux_error::bad_request_descriptor)
3032             {
3033                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3034             }
3035             else
3036             {
3037                 messages::internalError(asyncResp->res);
3038             }
3039             return;
3040         }
3041 
3042         std::string timestamp{};
3043         std::string filename{};
3044         std::string logfile{};
3045         parseCrashdumpParameters(params, filename, timestamp, logfile);
3046 
3047         if (filename.empty() || timestamp.empty())
3048         {
3049             messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3050             return;
3051         }
3052 
3053         std::string crashdumpURI =
3054             "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
3055             logID + "/" + filename;
3056         nlohmann::json::object_t logEntry;
3057         logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
3058         logEntry["@odata.id"] =
3059             "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
3060         logEntry["Name"] = "CPU Crashdump";
3061         logEntry["Id"] = logID;
3062         logEntry["EntryType"] = "Oem";
3063         logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
3064         logEntry["DiagnosticDataType"] = "OEM";
3065         logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
3066         logEntry["Created"] = std::move(timestamp);
3067 
3068         // If logEntryJson references an array of LogEntry resources
3069         // ('Members' list), then push this as a new entry, otherwise set it
3070         // directly
3071         if (logEntryJson.is_array())
3072         {
3073             logEntryJson.push_back(logEntry);
3074             asyncResp->res.jsonValue["Members@odata.count"] =
3075                 logEntryJson.size();
3076         }
3077         else
3078         {
3079             logEntryJson.update(logEntry);
3080         }
3081     };
3082     sdbusplus::asio::getAllProperties(
3083         *crow::connections::systemBus, crashdumpObject,
3084         crashdumpPath + std::string("/") + logID, crashdumpInterface,
3085         std::move(getStoredLogCallback));
3086 }
3087 
3088 inline void requestRoutesCrashdumpEntryCollection(App& app)
3089 {
3090     // Note: Deviated from redfish privilege registry for GET & HEAD
3091     // method for security reasons.
3092     /**
3093      * Functions triggers appropriate requests on DBus
3094      */
3095     BMCWEB_ROUTE(app,
3096                  "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/")
3097         // This is incorrect, should be.
3098         //.privileges(redfish::privileges::postLogEntryCollection)
3099         .privileges({{"ConfigureComponents"}})
3100         .methods(boost::beast::http::verb::get)(
3101             [&app](const crow::Request& req,
3102                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3103                    const std::string& systemName) {
3104         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3105         {
3106             return;
3107         }
3108         if (systemName != "system")
3109         {
3110             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3111                                        systemName);
3112             return;
3113         }
3114 
3115         crow::connections::systemBus->async_method_call(
3116             [asyncResp](const boost::system::error_code ec,
3117                         const std::vector<std::string>& resp) {
3118             if (ec)
3119             {
3120                 if (ec.value() !=
3121                     boost::system::errc::no_such_file_or_directory)
3122                 {
3123                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
3124                                      << ec.message();
3125                     messages::internalError(asyncResp->res);
3126                     return;
3127                 }
3128             }
3129             asyncResp->res.jsonValue["@odata.type"] =
3130                 "#LogEntryCollection.LogEntryCollection";
3131             asyncResp->res.jsonValue["@odata.id"] =
3132                 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
3133             asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
3134             asyncResp->res.jsonValue["Description"] =
3135                 "Collection of Crashdump Entries";
3136             asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3137             asyncResp->res.jsonValue["Members@odata.count"] = 0;
3138 
3139             for (const std::string& path : resp)
3140             {
3141                 const sdbusplus::message::object_path objPath(path);
3142                 // Get the log ID
3143                 std::string logID = objPath.filename();
3144                 if (logID.empty())
3145                 {
3146                     continue;
3147                 }
3148                 // Add the log entry to the array
3149                 logCrashdumpEntry(asyncResp, logID,
3150                                   asyncResp->res.jsonValue["Members"]);
3151             }
3152             },
3153             "xyz.openbmc_project.ObjectMapper",
3154             "/xyz/openbmc_project/object_mapper",
3155             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
3156             std::array<const char*, 1>{crashdumpInterface});
3157         });
3158 }
3159 
3160 inline void requestRoutesCrashdumpEntry(App& app)
3161 {
3162     // Note: Deviated from redfish privilege registry for GET & HEAD
3163     // method for security reasons.
3164 
3165     BMCWEB_ROUTE(
3166         app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/")
3167         // this is incorrect, should be
3168         // .privileges(redfish::privileges::getLogEntry)
3169         .privileges({{"ConfigureComponents"}})
3170         .methods(boost::beast::http::verb::get)(
3171             [&app](const crow::Request& req,
3172                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3173                    const std::string& systemName, const std::string& param) {
3174         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3175         {
3176             return;
3177         }
3178         if (systemName != "system")
3179         {
3180             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3181                                        systemName);
3182             ;
3183             return;
3184         }
3185         const std::string& logID = param;
3186         logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
3187         });
3188 }
3189 
3190 inline void requestRoutesCrashdumpFile(App& app)
3191 {
3192     // Note: Deviated from redfish privilege registry for GET & HEAD
3193     // method for security reasons.
3194     BMCWEB_ROUTE(
3195         app,
3196         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/")
3197         .privileges(redfish::privileges::getLogEntry)
3198         .methods(boost::beast::http::verb::get)(
3199             [](const crow::Request& req,
3200                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3201                const std::string& systemName, const std::string& logID,
3202                const std::string& fileName) {
3203         // Do not call getRedfishRoute here since the crashdump file is not a
3204         // Redfish resource.
3205 
3206         if (systemName != "system")
3207         {
3208             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3209                                        systemName);
3210             ;
3211             return;
3212         }
3213 
3214         auto getStoredLogCallback =
3215             [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
3216                 const boost::system::error_code ec,
3217                 const std::vector<
3218                     std::pair<std::string, dbus::utility::DbusVariantType>>&
3219                     resp) {
3220             if (ec)
3221             {
3222                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
3223                 messages::internalError(asyncResp->res);
3224                 return;
3225             }
3226 
3227             std::string dbusFilename{};
3228             std::string dbusTimestamp{};
3229             std::string dbusFilepath{};
3230 
3231             parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
3232                                      dbusFilepath);
3233 
3234             if (dbusFilename.empty() || dbusTimestamp.empty() ||
3235                 dbusFilepath.empty())
3236             {
3237                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3238                 return;
3239             }
3240 
3241             // Verify the file name parameter is correct
3242             if (fileName != dbusFilename)
3243             {
3244                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3245                 return;
3246             }
3247 
3248             if (!std::filesystem::exists(dbusFilepath))
3249             {
3250                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3251                 return;
3252             }
3253             std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
3254             asyncResp->res.body() =
3255                 std::string(std::istreambuf_iterator<char>{ifs}, {});
3256 
3257             // Configure this to be a file download when accessed
3258             // from a browser
3259             asyncResp->res.addHeader(
3260                 boost::beast::http::field::content_disposition, "attachment");
3261         };
3262         sdbusplus::asio::getAllProperties(
3263             *crow::connections::systemBus, crashdumpObject,
3264             crashdumpPath + std::string("/") + logID, crashdumpInterface,
3265             std::move(getStoredLogCallback));
3266         });
3267 }
3268 
3269 enum class OEMDiagnosticType
3270 {
3271     onDemand,
3272     telemetry,
3273     invalid,
3274 };
3275 
3276 inline OEMDiagnosticType
3277     getOEMDiagnosticType(const std::string_view& oemDiagStr)
3278 {
3279     if (oemDiagStr == "OnDemand")
3280     {
3281         return OEMDiagnosticType::onDemand;
3282     }
3283     if (oemDiagStr == "Telemetry")
3284     {
3285         return OEMDiagnosticType::telemetry;
3286     }
3287 
3288     return OEMDiagnosticType::invalid;
3289 }
3290 
3291 inline void requestRoutesCrashdumpCollect(App& app)
3292 {
3293     // Note: Deviated from redfish privilege registry for GET & HEAD
3294     // method for security reasons.
3295     BMCWEB_ROUTE(
3296         app,
3297         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
3298         // The below is incorrect;  Should be ConfigureManager
3299         //.privileges(redfish::privileges::postLogService)
3300         .privileges({{"ConfigureComponents"}})
3301         .methods(boost::beast::http::verb::post)(
3302             [&app](const crow::Request& req,
3303                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3304                    const std::string& systemName) {
3305         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3306         {
3307             return;
3308         }
3309 
3310         if (systemName != "system")
3311         {
3312             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3313                                        systemName);
3314             ;
3315             return;
3316         }
3317 
3318         std::string diagnosticDataType;
3319         std::string oemDiagnosticDataType;
3320         if (!redfish::json_util::readJsonAction(
3321                 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3322                 "OEMDiagnosticDataType", oemDiagnosticDataType))
3323         {
3324             return;
3325         }
3326 
3327         if (diagnosticDataType != "OEM")
3328         {
3329             BMCWEB_LOG_ERROR
3330                 << "Only OEM DiagnosticDataType supported for Crashdump";
3331             messages::actionParameterValueFormatError(
3332                 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3333                 "CollectDiagnosticData");
3334             return;
3335         }
3336 
3337         OEMDiagnosticType oemDiagType =
3338             getOEMDiagnosticType(oemDiagnosticDataType);
3339 
3340         std::string iface;
3341         std::string method;
3342         std::string taskMatchStr;
3343         if (oemDiagType == OEMDiagnosticType::onDemand)
3344         {
3345             iface = crashdumpOnDemandInterface;
3346             method = "GenerateOnDemandLog";
3347             taskMatchStr = "type='signal',"
3348                            "interface='org.freedesktop.DBus.Properties',"
3349                            "member='PropertiesChanged',"
3350                            "arg0namespace='com.intel.crashdump'";
3351         }
3352         else if (oemDiagType == OEMDiagnosticType::telemetry)
3353         {
3354             iface = crashdumpTelemetryInterface;
3355             method = "GenerateTelemetryLog";
3356             taskMatchStr = "type='signal',"
3357                            "interface='org.freedesktop.DBus.Properties',"
3358                            "member='PropertiesChanged',"
3359                            "arg0namespace='com.intel.crashdump'";
3360         }
3361         else
3362         {
3363             BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3364                              << oemDiagnosticDataType;
3365             messages::actionParameterValueFormatError(
3366                 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3367                 "CollectDiagnosticData");
3368             return;
3369         }
3370 
3371         auto collectCrashdumpCallback =
3372             [asyncResp, payload(task::Payload(req)),
3373              taskMatchStr](const boost::system::error_code ec,
3374                            const std::string&) mutable {
3375             if (ec)
3376             {
3377                 if (ec.value() == boost::system::errc::operation_not_supported)
3378                 {
3379                     messages::resourceInStandby(asyncResp->res);
3380                 }
3381                 else if (ec.value() ==
3382                          boost::system::errc::device_or_resource_busy)
3383                 {
3384                     messages::serviceTemporarilyUnavailable(asyncResp->res,
3385                                                             "60");
3386                 }
3387                 else
3388                 {
3389                     messages::internalError(asyncResp->res);
3390                 }
3391                 return;
3392             }
3393             std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
3394                 [](boost::system::error_code err, sdbusplus::message_t&,
3395                    const std::shared_ptr<task::TaskData>& taskData) {
3396                 if (!err)
3397                 {
3398                     taskData->messages.emplace_back(messages::taskCompletedOK(
3399                         std::to_string(taskData->index)));
3400                     taskData->state = "Completed";
3401                 }
3402                 return task::completed;
3403                 },
3404                 taskMatchStr);
3405 
3406             task->startTimer(std::chrono::minutes(5));
3407             task->populateResp(asyncResp->res);
3408             task->payload.emplace(std::move(payload));
3409         };
3410 
3411         crow::connections::systemBus->async_method_call(
3412             std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3413             iface, method);
3414         });
3415 }
3416 
3417 /**
3418  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3419  */
3420 inline void requestRoutesDBusLogServiceActionsClear(App& app)
3421 {
3422     /**
3423      * Function handles POST method request.
3424      * The Clear Log actions does not require any parameter.The action deletes
3425      * all entries found in the Entries collection for this Log Service.
3426      */
3427 
3428     BMCWEB_ROUTE(
3429         app,
3430         "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
3431         .privileges(redfish::privileges::postLogService)
3432         .methods(boost::beast::http::verb::post)(
3433             [&app](const crow::Request& req,
3434                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3435                    const std::string& systemName) {
3436         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3437         {
3438             return;
3439         }
3440         if (systemName != "system")
3441         {
3442             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3443                                        systemName);
3444             ;
3445             return;
3446         }
3447         BMCWEB_LOG_DEBUG << "Do delete all entries.";
3448 
3449         // Process response from Logging service.
3450         auto respHandler = [asyncResp](const boost::system::error_code ec) {
3451             BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3452             if (ec)
3453             {
3454                 // TODO Handle for specific error code
3455                 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3456                 asyncResp->res.result(
3457                     boost::beast::http::status::internal_server_error);
3458                 return;
3459             }
3460 
3461             asyncResp->res.result(boost::beast::http::status::no_content);
3462         };
3463 
3464         // Make call to Logging service to request Clear Log
3465         crow::connections::systemBus->async_method_call(
3466             respHandler, "xyz.openbmc_project.Logging",
3467             "/xyz/openbmc_project/logging",
3468             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3469         });
3470 }
3471 
3472 /****************************************************
3473  * Redfish PostCode interfaces
3474  * using DBUS interface: getPostCodesTS
3475  ******************************************************/
3476 inline void requestRoutesPostCodesLogService(App& app)
3477 {
3478     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/")
3479         .privileges(redfish::privileges::getLogService)
3480         .methods(boost::beast::http::verb::get)(
3481             [&app](const crow::Request& req,
3482                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3483                    const std::string& systemName) {
3484         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3485         {
3486             return;
3487         }
3488         if (systemName != "system")
3489         {
3490             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3491                                        systemName);
3492             ;
3493             return;
3494         }
3495         asyncResp->res.jsonValue["@odata.id"] =
3496             "/redfish/v1/Systems/system/LogServices/PostCodes";
3497         asyncResp->res.jsonValue["@odata.type"] =
3498             "#LogService.v1_1_0.LogService";
3499         asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3500         asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3501         asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3502         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3503         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3504             "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3505 
3506         std::pair<std::string, std::string> redfishDateTimeOffset =
3507             redfish::time_utils::getDateTimeOffsetNow();
3508         asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3509         asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3510             redfishDateTimeOffset.second;
3511 
3512         asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3513             {"target",
3514              "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3515         });
3516 }
3517 
3518 inline void requestRoutesPostCodesClear(App& app)
3519 {
3520     BMCWEB_ROUTE(
3521         app,
3522         "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/")
3523         // The following privilege is incorrect;  It should be ConfigureManager
3524         //.privileges(redfish::privileges::postLogService)
3525         .privileges({{"ConfigureComponents"}})
3526         .methods(boost::beast::http::verb::post)(
3527             [&app](const crow::Request& req,
3528                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3529                    const std::string& systemName) {
3530         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3531         {
3532             return;
3533         }
3534         if (systemName != "system")
3535         {
3536             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3537                                        systemName);
3538             ;
3539             return;
3540         }
3541         BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3542 
3543         // Make call to post-code service to request clear all
3544         crow::connections::systemBus->async_method_call(
3545             [asyncResp](const boost::system::error_code ec) {
3546             if (ec)
3547             {
3548                 // TODO Handle for specific error code
3549                 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3550                                  << ec;
3551                 asyncResp->res.result(
3552                     boost::beast::http::status::internal_server_error);
3553                 messages::internalError(asyncResp->res);
3554                 return;
3555             }
3556             },
3557             "xyz.openbmc_project.State.Boot.PostCode0",
3558             "/xyz/openbmc_project/State/Boot/PostCode0",
3559             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3560         });
3561 }
3562 
3563 static void fillPostCodeEntry(
3564     const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3565     const boost::container::flat_map<
3566         uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
3567     const uint16_t bootIndex, const uint64_t codeIndex = 0,
3568     const uint64_t skip = 0, const uint64_t top = 0)
3569 {
3570     // Get the Message from the MessageRegistry
3571     const registries::Message* message =
3572         registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
3573 
3574     uint64_t currentCodeIndex = 0;
3575     nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
3576 
3577     uint64_t firstCodeTimeUs = 0;
3578     for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3579              code : postcode)
3580     {
3581         currentCodeIndex++;
3582         std::string postcodeEntryID =
3583             "B" + std::to_string(bootIndex) + "-" +
3584             std::to_string(currentCodeIndex); // 1 based index in EntryID string
3585 
3586         uint64_t usecSinceEpoch = code.first;
3587         uint64_t usTimeOffset = 0;
3588 
3589         if (1 == currentCodeIndex)
3590         { // already incremented
3591             firstCodeTimeUs = code.first;
3592         }
3593         else
3594         {
3595             usTimeOffset = code.first - firstCodeTimeUs;
3596         }
3597 
3598         // skip if no specific codeIndex is specified and currentCodeIndex does
3599         // not fall between top and skip
3600         if ((codeIndex == 0) &&
3601             (currentCodeIndex <= skip || currentCodeIndex > top))
3602         {
3603             continue;
3604         }
3605 
3606         // skip if a specific codeIndex is specified and does not match the
3607         // currentIndex
3608         if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3609         {
3610             // This is done for simplicity. 1st entry is needed to calculate
3611             // time offset. To improve efficiency, one can get to the entry
3612             // directly (possibly with flatmap's nth method)
3613             continue;
3614         }
3615 
3616         // currentCodeIndex is within top and skip or equal to specified code
3617         // index
3618 
3619         // Get the Created time from the timestamp
3620         std::string entryTimeStr;
3621         entryTimeStr =
3622             redfish::time_utils::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
3623 
3624         // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3625         std::ostringstream hexCode;
3626         hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3627                 << std::get<0>(code.second);
3628         std::ostringstream timeOffsetStr;
3629         // Set Fixed -Point Notation
3630         timeOffsetStr << std::fixed;
3631         // Set precision to 4 digits
3632         timeOffsetStr << std::setprecision(4);
3633         // Add double to stream
3634         timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3635         std::vector<std::string> messageArgs = {
3636             std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3637 
3638         // Get MessageArgs template from message registry
3639         std::string msg;
3640         if (message != nullptr)
3641         {
3642             msg = message->message;
3643 
3644             // fill in this post code value
3645             int i = 0;
3646             for (const std::string& messageArg : messageArgs)
3647             {
3648                 std::string argStr = "%" + std::to_string(++i);
3649                 size_t argPos = msg.find(argStr);
3650                 if (argPos != std::string::npos)
3651                 {
3652                     msg.replace(argPos, argStr.length(), messageArg);
3653                 }
3654             }
3655         }
3656 
3657         // Get Severity template from message registry
3658         std::string severity;
3659         if (message != nullptr)
3660         {
3661             severity = message->messageSeverity;
3662         }
3663 
3664         // add to AsyncResp
3665         logEntryArray.push_back({});
3666         nlohmann::json& bmcLogEntry = logEntryArray.back();
3667         bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
3668         bmcLogEntry["@odata.id"] =
3669             "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3670             postcodeEntryID;
3671         bmcLogEntry["Name"] = "POST Code Log Entry";
3672         bmcLogEntry["Id"] = postcodeEntryID;
3673         bmcLogEntry["Message"] = std::move(msg);
3674         bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3675         bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3676         bmcLogEntry["EntryType"] = "Event";
3677         bmcLogEntry["Severity"] = std::move(severity);
3678         bmcLogEntry["Created"] = entryTimeStr;
3679         if (!std::get<std::vector<uint8_t>>(code.second).empty())
3680         {
3681             bmcLogEntry["AdditionalDataURI"] =
3682                 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3683                 postcodeEntryID + "/attachment";
3684         }
3685     }
3686 }
3687 
3688 static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3689                                 const uint16_t bootIndex,
3690                                 const uint64_t codeIndex)
3691 {
3692     crow::connections::systemBus->async_method_call(
3693         [aResp, bootIndex,
3694          codeIndex](const boost::system::error_code ec,
3695                     const boost::container::flat_map<
3696                         uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3697                         postcode) {
3698         if (ec)
3699         {
3700             BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3701             messages::internalError(aResp->res);
3702             return;
3703         }
3704 
3705         // skip the empty postcode boots
3706         if (postcode.empty())
3707         {
3708             return;
3709         }
3710 
3711         fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3712 
3713         aResp->res.jsonValue["Members@odata.count"] =
3714             aResp->res.jsonValue["Members"].size();
3715         },
3716         "xyz.openbmc_project.State.Boot.PostCode0",
3717         "/xyz/openbmc_project/State/Boot/PostCode0",
3718         "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3719         bootIndex);
3720 }
3721 
3722 static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3723                                const uint16_t bootIndex,
3724                                const uint16_t bootCount,
3725                                const uint64_t entryCount, size_t skip,
3726                                size_t top)
3727 {
3728     crow::connections::systemBus->async_method_call(
3729         [aResp, bootIndex, bootCount, entryCount, skip,
3730          top](const boost::system::error_code ec,
3731               const boost::container::flat_map<
3732                   uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3733                   postcode) {
3734         if (ec)
3735         {
3736             BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3737             messages::internalError(aResp->res);
3738             return;
3739         }
3740 
3741         uint64_t endCount = entryCount;
3742         if (!postcode.empty())
3743         {
3744             endCount = entryCount + postcode.size();
3745             if (skip < endCount && (top + skip) > entryCount)
3746             {
3747                 uint64_t thisBootSkip =
3748                     std::max(static_cast<uint64_t>(skip), entryCount) -
3749                     entryCount;
3750                 uint64_t thisBootTop =
3751                     std::min(static_cast<uint64_t>(top + skip), endCount) -
3752                     entryCount;
3753 
3754                 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3755                                   thisBootTop);
3756             }
3757             aResp->res.jsonValue["Members@odata.count"] = endCount;
3758         }
3759 
3760         // continue to previous bootIndex
3761         if (bootIndex < bootCount)
3762         {
3763             getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3764                                bootCount, endCount, skip, top);
3765         }
3766         else if (skip + top < endCount)
3767         {
3768             aResp->res.jsonValue["Members@odata.nextLink"] =
3769                 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3770                 std::to_string(skip + top);
3771         }
3772         },
3773         "xyz.openbmc_project.State.Boot.PostCode0",
3774         "/xyz/openbmc_project/State/Boot/PostCode0",
3775         "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3776         bootIndex);
3777 }
3778 
3779 static void
3780     getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3781                          size_t skip, size_t top)
3782 {
3783     uint64_t entryCount = 0;
3784     sdbusplus::asio::getProperty<uint16_t>(
3785         *crow::connections::systemBus,
3786         "xyz.openbmc_project.State.Boot.PostCode0",
3787         "/xyz/openbmc_project/State/Boot/PostCode0",
3788         "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3789         [aResp, entryCount, skip, top](const boost::system::error_code ec,
3790                                        const uint16_t bootCount) {
3791         if (ec)
3792         {
3793             BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3794             messages::internalError(aResp->res);
3795             return;
3796         }
3797         getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3798         });
3799 }
3800 
3801 inline void requestRoutesPostCodesEntryCollection(App& app)
3802 {
3803     BMCWEB_ROUTE(app,
3804                  "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/")
3805         .privileges(redfish::privileges::getLogEntryCollection)
3806         .methods(boost::beast::http::verb::get)(
3807             [&app](const crow::Request& req,
3808                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3809                    const std::string& systemName) {
3810         query_param::QueryCapabilities capabilities = {
3811             .canDelegateTop = true,
3812             .canDelegateSkip = true,
3813         };
3814         query_param::Query delegatedQuery;
3815         if (!redfish::setUpRedfishRouteWithDelegation(
3816                 app, req, asyncResp, delegatedQuery, capabilities))
3817         {
3818             return;
3819         }
3820 
3821         if (systemName != "system")
3822         {
3823             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3824                                        systemName);
3825             ;
3826             return;
3827         }
3828         asyncResp->res.jsonValue["@odata.type"] =
3829             "#LogEntryCollection.LogEntryCollection";
3830         asyncResp->res.jsonValue["@odata.id"] =
3831             "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3832         asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3833         asyncResp->res.jsonValue["Description"] =
3834             "Collection of POST Code Log Entries";
3835         asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3836         asyncResp->res.jsonValue["Members@odata.count"] = 0;
3837         size_t skip = delegatedQuery.skip.value_or(0);
3838         size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
3839         getCurrentBootNumber(asyncResp, skip, top);
3840         });
3841 }
3842 
3843 /**
3844  * @brief Parse post code ID and get the current value and index value
3845  *        eg: postCodeID=B1-2, currentValue=1, index=2
3846  *
3847  * @param[in]  postCodeID     Post Code ID
3848  * @param[out] currentValue   Current value
3849  * @param[out] index          Index value
3850  *
3851  * @return bool true if the parsing is successful, false the parsing fails
3852  */
3853 inline static bool parsePostCode(const std::string& postCodeID,
3854                                  uint64_t& currentValue, uint16_t& index)
3855 {
3856     std::vector<std::string> split;
3857     boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3858     if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3859     {
3860         return false;
3861     }
3862 
3863     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
3864     const char* start = split[0].data() + 1;
3865     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
3866     const char* end = split[0].data() + split[0].size();
3867     auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3868 
3869     if (ptrIndex != end || ecIndex != std::errc())
3870     {
3871         return false;
3872     }
3873 
3874     start = split[1].data();
3875 
3876     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
3877     end = split[1].data() + split[1].size();
3878     auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3879 
3880     return ptrValue == end && ecValue == std::errc();
3881 }
3882 
3883 inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3884 {
3885     BMCWEB_ROUTE(
3886         app,
3887         "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/")
3888         .privileges(redfish::privileges::getLogEntry)
3889         .methods(boost::beast::http::verb::get)(
3890             [&app](const crow::Request& req,
3891                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3892                    const std::string& systemName,
3893                    const std::string& postCodeID) {
3894         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3895         {
3896             return;
3897         }
3898         if (http_helpers::isContentTypeAllowed(
3899                 req.getHeaderValue("Accept"),
3900                 http_helpers::ContentType::OctetStream, true))
3901         {
3902             asyncResp->res.result(boost::beast::http::status::bad_request);
3903             return;
3904         }
3905         if (systemName != "system")
3906         {
3907             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3908                                        systemName);
3909             ;
3910             return;
3911         }
3912 
3913         uint64_t currentValue = 0;
3914         uint16_t index = 0;
3915         if (!parsePostCode(postCodeID, currentValue, index))
3916         {
3917             messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3918             return;
3919         }
3920 
3921         crow::connections::systemBus->async_method_call(
3922             [asyncResp, postCodeID, currentValue](
3923                 const boost::system::error_code ec,
3924                 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3925                     postcodes) {
3926             if (ec.value() == EBADR)
3927             {
3928                 messages::resourceNotFound(asyncResp->res, "LogEntry",
3929                                            postCodeID);
3930                 return;
3931             }
3932             if (ec)
3933             {
3934                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3935                 messages::internalError(asyncResp->res);
3936                 return;
3937             }
3938 
3939             size_t value = static_cast<size_t>(currentValue) - 1;
3940             if (value == std::string::npos || postcodes.size() < currentValue)
3941             {
3942                 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3943                 messages::resourceNotFound(asyncResp->res, "LogEntry",
3944                                            postCodeID);
3945                 return;
3946             }
3947 
3948             const auto& [tID, c] = postcodes[value];
3949             if (c.empty())
3950             {
3951                 BMCWEB_LOG_INFO << "No found post code data";
3952                 messages::resourceNotFound(asyncResp->res, "LogEntry",
3953                                            postCodeID);
3954                 return;
3955             }
3956             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3957             const char* d = reinterpret_cast<const char*>(c.data());
3958             std::string_view strData(d, c.size());
3959 
3960             asyncResp->res.addHeader(boost::beast::http::field::content_type,
3961                                      "application/octet-stream");
3962             asyncResp->res.addHeader(
3963                 boost::beast::http::field::content_transfer_encoding, "Base64");
3964             asyncResp->res.body() = crow::utility::base64encode(strData);
3965             },
3966             "xyz.openbmc_project.State.Boot.PostCode0",
3967             "/xyz/openbmc_project/State/Boot/PostCode0",
3968             "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3969         });
3970 }
3971 
3972 inline void requestRoutesPostCodesEntry(App& app)
3973 {
3974     BMCWEB_ROUTE(
3975         app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/")
3976         .privileges(redfish::privileges::getLogEntry)
3977         .methods(boost::beast::http::verb::get)(
3978             [&app](const crow::Request& req,
3979                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3980                    const std::string& systemName, const std::string& targetID) {
3981         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3982         {
3983             return;
3984         }
3985         if (systemName != "system")
3986         {
3987             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3988                                        systemName);
3989             return;
3990         }
3991 
3992         uint16_t bootIndex = 0;
3993         uint64_t codeIndex = 0;
3994         if (!parsePostCode(targetID, codeIndex, bootIndex))
3995         {
3996             // Requested ID was not found
3997             messages::resourceNotFound(asyncResp->res, "LogEntry", targetID);
3998             return;
3999         }
4000         if (bootIndex == 0 || codeIndex == 0)
4001         {
4002             BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
4003                              << targetID;
4004         }
4005 
4006         asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
4007         asyncResp->res.jsonValue["@odata.id"] =
4008             "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
4009         asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
4010         asyncResp->res.jsonValue["Description"] =
4011             "Collection of POST Code Log Entries";
4012         asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
4013         asyncResp->res.jsonValue["Members@odata.count"] = 0;
4014 
4015         getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
4016         });
4017 }
4018 
4019 } // namespace redfish
4020