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