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