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