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