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