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