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