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