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