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