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