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