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