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