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