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