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