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