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