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