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