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