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