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