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