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