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