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