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