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