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