xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision ed3982131dcef2b499da36e674d2d21b2289ef29)
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::getLogEntry)
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, resolved,
1629                      entryId](const boost::system::error_code ec) {
1630                         if (ec)
1631                         {
1632                             BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1633                             messages::internalError(asyncResp->res);
1634                             return;
1635                         }
1636                     },
1637                     "xyz.openbmc_project.Logging",
1638                     "/xyz/openbmc_project/logging/entry/" + entryId,
1639                     "org.freedesktop.DBus.Properties", "Set",
1640                     "xyz.openbmc_project.Logging.Entry", "Resolved",
1641                     std::variant<bool>(*resolved));
1642             });
1643 
1644     BMCWEB_ROUTE(
1645         app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
1646         .privileges(redfish::privileges::deleteLogEntry)
1647 
1648         .methods(boost::beast::http::verb::delete_)(
1649             [](const crow::Request&,
1650                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1651                const std::string& param)
1652 
1653             {
1654                 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1655 
1656                 std::string entryID = param;
1657 
1658                 dbus::utility::escapePathForDbus(entryID);
1659 
1660                 // Process response from Logging service.
1661                 auto respHandler = [asyncResp, entryID](
1662                                        const boost::system::error_code ec) {
1663                     BMCWEB_LOG_DEBUG
1664                         << "EventLogEntry (DBus) doDelete callback: Done";
1665                     if (ec)
1666                     {
1667                         if (ec.value() == EBADR)
1668                         {
1669                             messages::resourceNotFound(asyncResp->res,
1670                                                        "LogEntry", entryID);
1671                             return;
1672                         }
1673                         // TODO Handle for specific error code
1674                         BMCWEB_LOG_ERROR << "EventLogEntry (DBus) doDelete "
1675                                             "respHandler got error "
1676                                          << ec;
1677                         asyncResp->res.result(
1678                             boost::beast::http::status::internal_server_error);
1679                         return;
1680                     }
1681 
1682                     asyncResp->res.result(boost::beast::http::status::ok);
1683                 };
1684 
1685                 // Make call to Logging service to request Delete Log
1686                 crow::connections::systemBus->async_method_call(
1687                     respHandler, "xyz.openbmc_project.Logging",
1688                     "/xyz/openbmc_project/logging/entry/" + entryID,
1689                     "xyz.openbmc_project.Object.Delete", "Delete");
1690             });
1691 }
1692 
1693 inline void requestRoutesDBusEventLogEntryDownload(App& app)
1694 {
1695     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/"
1696                       "<str>/attachment")
1697         .privileges(redfish::privileges::getLogEntry)
1698         .methods(boost::beast::http::verb::get)(
1699             [](const crow::Request& req,
1700                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1701                const std::string& param)
1702 
1703             {
1704                 std::string_view acceptHeader = req.getHeaderValue("Accept");
1705                 // The iterators in boost/http/rfc7230.hpp end the string if '/'
1706                 // is found, so replace it with arbitrary character '|' which is
1707                 // not part of the Accept header syntax.
1708                 std::string acceptStr = boost::replace_all_copy(
1709                     std::string(acceptHeader), "/", "|");
1710                 boost::beast::http::ext_list acceptTypes{acceptStr};
1711                 bool supported = false;
1712                 for (const auto& type : acceptTypes)
1713                 {
1714                     if ((type.first == "*|*") ||
1715                         (type.first == "application|octet-stream"))
1716                     {
1717                         supported = true;
1718                         break;
1719                     }
1720                 }
1721                 if (!supported)
1722                 {
1723                     asyncResp->res.result(
1724                         boost::beast::http::status::bad_request);
1725                     return;
1726                 }
1727 
1728                 std::string entryID = param;
1729                 dbus::utility::escapePathForDbus(entryID);
1730 
1731                 crow::connections::systemBus->async_method_call(
1732                     [asyncResp,
1733                      entryID](const boost::system::error_code ec,
1734                               const sdbusplus::message::unix_fd& unixfd) {
1735                         if (ec.value() == EBADR)
1736                         {
1737                             messages::resourceNotFound(
1738                                 asyncResp->res, "EventLogAttachment", entryID);
1739                             return;
1740                         }
1741                         if (ec)
1742                         {
1743                             BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1744                             messages::internalError(asyncResp->res);
1745                             return;
1746                         }
1747 
1748                         int fd = -1;
1749                         fd = dup(unixfd);
1750                         if (fd == -1)
1751                         {
1752                             messages::internalError(asyncResp->res);
1753                             return;
1754                         }
1755 
1756                         long long int size = lseek(fd, 0, SEEK_END);
1757                         if (size == -1)
1758                         {
1759                             messages::internalError(asyncResp->res);
1760                             return;
1761                         }
1762 
1763                         // Arbitrary max size of 64kb
1764                         constexpr int maxFileSize = 65536;
1765                         if (size > maxFileSize)
1766                         {
1767                             BMCWEB_LOG_ERROR
1768                                 << "File size exceeds maximum allowed size of "
1769                                 << maxFileSize;
1770                             messages::internalError(asyncResp->res);
1771                             return;
1772                         }
1773                         std::vector<char> data(static_cast<size_t>(size));
1774                         long long int rc = lseek(fd, 0, SEEK_SET);
1775                         if (rc == -1)
1776                         {
1777                             messages::internalError(asyncResp->res);
1778                             return;
1779                         }
1780                         rc = read(fd, data.data(), data.size());
1781                         if ((rc == -1) || (rc != size))
1782                         {
1783                             messages::internalError(asyncResp->res);
1784                             return;
1785                         }
1786                         close(fd);
1787 
1788                         std::string_view strData(data.data(), data.size());
1789                         std::string output =
1790                             crow::utility::base64encode(strData);
1791 
1792                         asyncResp->res.addHeader("Content-Type",
1793                                                  "application/octet-stream");
1794                         asyncResp->res.addHeader("Content-Transfer-Encoding",
1795                                                  "Base64");
1796                         asyncResp->res.body() = std::move(output);
1797                     },
1798                     "xyz.openbmc_project.Logging",
1799                     "/xyz/openbmc_project/logging/entry/" + entryID,
1800                     "xyz.openbmc_project.Logging.Entry", "GetEntry");
1801             });
1802 }
1803 
1804 inline void requestRoutesBMCLogServiceCollection(App& app)
1805 {
1806     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
1807         .privileges(redfish::privileges::getLogService)
1808         .methods(boost::beast::http::verb::get)(
1809             [](const crow::Request&,
1810                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1811                 // Collections don't include the static data added by SubRoute
1812                 // because it has a duplicate entry for members
1813                 asyncResp->res.jsonValue["@odata.type"] =
1814                     "#LogServiceCollection.LogServiceCollection";
1815                 asyncResp->res.jsonValue["@odata.id"] =
1816                     "/redfish/v1/Managers/bmc/LogServices";
1817                 asyncResp->res.jsonValue["Name"] =
1818                     "Open BMC Log Services Collection";
1819                 asyncResp->res.jsonValue["Description"] =
1820                     "Collection of LogServices for this Manager";
1821                 nlohmann::json& logServiceArray =
1822                     asyncResp->res.jsonValue["Members"];
1823                 logServiceArray = nlohmann::json::array();
1824 #ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1825                 logServiceArray.push_back(
1826                     {{"@odata.id",
1827                       "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1828 #endif
1829 #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1830                 logServiceArray.push_back(
1831                     {{"@odata.id",
1832                       "/redfish/v1/Managers/bmc/LogServices/Journal"}});
1833 #endif
1834                 asyncResp->res.jsonValue["Members@odata.count"] =
1835                     logServiceArray.size();
1836             });
1837 }
1838 
1839 inline void requestRoutesBMCJournalLogService(App& app)
1840 {
1841     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
1842         .privileges(redfish::privileges::getLogService)
1843         .methods(boost::beast::http::verb::get)(
1844             [](const crow::Request&,
1845                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1846 
1847             {
1848                 asyncResp->res.jsonValue["@odata.type"] =
1849                     "#LogService.v1_1_0.LogService";
1850                 asyncResp->res.jsonValue["@odata.id"] =
1851                     "/redfish/v1/Managers/bmc/LogServices/Journal";
1852                 asyncResp->res.jsonValue["Name"] =
1853                     "Open BMC Journal Log Service";
1854                 asyncResp->res.jsonValue["Description"] =
1855                     "BMC Journal Log Service";
1856                 asyncResp->res.jsonValue["Id"] = "BMC Journal";
1857                 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1858 
1859                 std::pair<std::string, std::string> redfishDateTimeOffset =
1860                     crow::utility::getDateTimeOffsetNow();
1861                 asyncResp->res.jsonValue["DateTime"] =
1862                     redfishDateTimeOffset.first;
1863                 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1864                     redfishDateTimeOffset.second;
1865 
1866                 asyncResp->res.jsonValue["Entries"] = {
1867                     {"@odata.id",
1868                      "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
1869             });
1870 }
1871 
1872 static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1873                                       sd_journal* journal,
1874                                       nlohmann::json& bmcJournalLogEntryJson)
1875 {
1876     // Get the Log Entry contents
1877     int ret = 0;
1878 
1879     std::string message;
1880     std::string_view syslogID;
1881     ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
1882     if (ret < 0)
1883     {
1884         BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
1885                          << strerror(-ret);
1886     }
1887     if (!syslogID.empty())
1888     {
1889         message += std::string(syslogID) + ": ";
1890     }
1891 
1892     std::string_view msg;
1893     ret = getJournalMetadata(journal, "MESSAGE", msg);
1894     if (ret < 0)
1895     {
1896         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1897         return 1;
1898     }
1899     message += std::string(msg);
1900 
1901     // Get the severity from the PRIORITY field
1902     long int severity = 8; // Default to an invalid priority
1903     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
1904     if (ret < 0)
1905     {
1906         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
1907     }
1908 
1909     // Get the Created time from the timestamp
1910     std::string entryTimeStr;
1911     if (!getEntryTimestamp(journal, entryTimeStr))
1912     {
1913         return 1;
1914     }
1915 
1916     // Fill in the log entry with the gathered data
1917     bmcJournalLogEntryJson = {
1918         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1919         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1920                           bmcJournalLogEntryID},
1921         {"Name", "BMC Journal Entry"},
1922         {"Id", bmcJournalLogEntryID},
1923         {"Message", std::move(message)},
1924         {"EntryType", "Oem"},
1925         {"Severity", severity <= 2   ? "Critical"
1926                      : severity <= 4 ? "Warning"
1927                                      : "OK"},
1928         {"OemRecordFormat", "BMC Journal Entry"},
1929         {"Created", std::move(entryTimeStr)}};
1930     return 0;
1931 }
1932 
1933 inline void requestRoutesBMCJournalLogEntryCollection(App& app)
1934 {
1935     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
1936         .privileges(redfish::privileges::getLogEntryCollection)
1937         .methods(boost::beast::http::verb::get)(
1938             [](const crow::Request& req,
1939                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1940                 static constexpr const long maxEntriesPerPage = 1000;
1941                 uint64_t skip = 0;
1942                 uint64_t top = maxEntriesPerPage; // Show max entries by default
1943                 if (!getSkipParam(asyncResp, req, skip))
1944                 {
1945                     return;
1946                 }
1947                 if (!getTopParam(asyncResp, req, top))
1948                 {
1949                     return;
1950                 }
1951                 // Collections don't include the static data added by SubRoute
1952                 // because it has a duplicate entry for members
1953                 asyncResp->res.jsonValue["@odata.type"] =
1954                     "#LogEntryCollection.LogEntryCollection";
1955                 asyncResp->res.jsonValue["@odata.id"] =
1956                     "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1957                 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1958                 asyncResp->res.jsonValue["Description"] =
1959                     "Collection of BMC Journal Entries";
1960                 nlohmann::json& logEntryArray =
1961                     asyncResp->res.jsonValue["Members"];
1962                 logEntryArray = nlohmann::json::array();
1963 
1964                 // Go through the journal and use the timestamp to create a
1965                 // unique ID for each entry
1966                 sd_journal* journalTmp = nullptr;
1967                 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1968                 if (ret < 0)
1969                 {
1970                     BMCWEB_LOG_ERROR << "failed to open journal: "
1971                                      << strerror(-ret);
1972                     messages::internalError(asyncResp->res);
1973                     return;
1974                 }
1975                 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
1976                     journal(journalTmp, sd_journal_close);
1977                 journalTmp = nullptr;
1978                 uint64_t entryCount = 0;
1979                 // Reset the unique ID on the first entry
1980                 bool firstEntry = true;
1981                 SD_JOURNAL_FOREACH(journal.get())
1982                 {
1983                     entryCount++;
1984                     // Handle paging using skip (number of entries to skip from
1985                     // the start) and top (number of entries to display)
1986                     if (entryCount <= skip || entryCount > skip + top)
1987                     {
1988                         continue;
1989                     }
1990 
1991                     std::string idStr;
1992                     if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1993                     {
1994                         continue;
1995                     }
1996 
1997                     if (firstEntry)
1998                     {
1999                         firstEntry = false;
2000                     }
2001 
2002                     logEntryArray.push_back({});
2003                     nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2004                     if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2005                                                    bmcJournalLogEntry) != 0)
2006                     {
2007                         messages::internalError(asyncResp->res);
2008                         return;
2009                     }
2010                 }
2011                 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2012                 if (skip + top < entryCount)
2013                 {
2014                     asyncResp->res.jsonValue["Members@odata.nextLink"] =
2015                         "/redfish/v1/Managers/bmc/LogServices/Journal/"
2016                         "Entries?$skip=" +
2017                         std::to_string(skip + top);
2018                 }
2019             });
2020 }
2021 
2022 inline void requestRoutesBMCJournalLogEntry(App& app)
2023 {
2024     BMCWEB_ROUTE(app,
2025                  "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
2026         .privileges(redfish::privileges::getLogEntry)
2027         .methods(boost::beast::http::verb::get)(
2028             [](const crow::Request&,
2029                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2030                const std::string& entryID) {
2031                 // Convert the unique ID back to a timestamp to find the entry
2032                 uint64_t ts = 0;
2033                 uint64_t index = 0;
2034                 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2035                 {
2036                     return;
2037                 }
2038 
2039                 sd_journal* journalTmp = nullptr;
2040                 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2041                 if (ret < 0)
2042                 {
2043                     BMCWEB_LOG_ERROR << "failed to open journal: "
2044                                      << strerror(-ret);
2045                     messages::internalError(asyncResp->res);
2046                     return;
2047                 }
2048                 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2049                     journal(journalTmp, sd_journal_close);
2050                 journalTmp = nullptr;
2051                 // Go to the timestamp in the log and move to the entry at the
2052                 // index tracking the unique ID
2053                 std::string idStr;
2054                 bool firstEntry = true;
2055                 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2056                 if (ret < 0)
2057                 {
2058                     BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2059                                      << strerror(-ret);
2060                     messages::internalError(asyncResp->res);
2061                     return;
2062                 }
2063                 for (uint64_t i = 0; i <= index; i++)
2064                 {
2065                     sd_journal_next(journal.get());
2066                     if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2067                     {
2068                         messages::internalError(asyncResp->res);
2069                         return;
2070                     }
2071                     if (firstEntry)
2072                     {
2073                         firstEntry = false;
2074                     }
2075                 }
2076                 // Confirm that the entry ID matches what was requested
2077                 if (idStr != entryID)
2078                 {
2079                     messages::resourceMissingAtURI(asyncResp->res, entryID);
2080                     return;
2081                 }
2082 
2083                 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2084                                                asyncResp->res.jsonValue) != 0)
2085                 {
2086                     messages::internalError(asyncResp->res);
2087                     return;
2088                 }
2089             });
2090 }
2091 
2092 inline void requestRoutesBMCDumpService(App& app)
2093 {
2094     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
2095         .privileges(redfish::privileges::getLogService)
2096         .methods(boost::beast::http::verb::get)(
2097             [](const crow::Request&,
2098                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2099                 asyncResp->res.jsonValue["@odata.id"] =
2100                     "/redfish/v1/Managers/bmc/LogServices/Dump";
2101                 asyncResp->res.jsonValue["@odata.type"] =
2102                     "#LogService.v1_2_0.LogService";
2103                 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2104                 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2105                 asyncResp->res.jsonValue["Id"] = "Dump";
2106                 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2107 
2108                 std::pair<std::string, std::string> redfishDateTimeOffset =
2109                     crow::utility::getDateTimeOffsetNow();
2110                 asyncResp->res.jsonValue["DateTime"] =
2111                     redfishDateTimeOffset.first;
2112                 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2113                     redfishDateTimeOffset.second;
2114 
2115                 asyncResp->res.jsonValue["Entries"] = {
2116                     {"@odata.id",
2117                      "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2118                 asyncResp->res.jsonValue["Actions"] = {
2119                     {"#LogService.ClearLog",
2120                      {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2121                                  "Actions/LogService.ClearLog"}}},
2122                     {"#LogService.CollectDiagnosticData",
2123                      {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2124                                  "Actions/LogService.CollectDiagnosticData"}}}};
2125             });
2126 }
2127 
2128 inline void requestRoutesBMCDumpEntryCollection(App& app)
2129 {
2130 
2131     /**
2132      * Functions triggers appropriate requests on DBus
2133      */
2134     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
2135         .privileges(redfish::privileges::getLogEntryCollection)
2136         .methods(boost::beast::http::verb::get)(
2137             [](const crow::Request&,
2138                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2139                 asyncResp->res.jsonValue["@odata.type"] =
2140                     "#LogEntryCollection.LogEntryCollection";
2141                 asyncResp->res.jsonValue["@odata.id"] =
2142                     "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2143                 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2144                 asyncResp->res.jsonValue["Description"] =
2145                     "Collection of BMC Dump Entries";
2146 
2147                 getDumpEntryCollection(asyncResp, "BMC");
2148             });
2149 }
2150 
2151 inline void requestRoutesBMCDumpEntry(App& app)
2152 {
2153     BMCWEB_ROUTE(app,
2154                  "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
2155         .privileges(redfish::privileges::getLogEntry)
2156         .methods(boost::beast::http::verb::get)(
2157             [](const crow::Request&,
2158                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2159                const std::string& param) {
2160                 getDumpEntryById(asyncResp, param, "BMC");
2161             });
2162     BMCWEB_ROUTE(app,
2163                  "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
2164         .privileges(redfish::privileges::deleteLogEntry)
2165         .methods(boost::beast::http::verb::delete_)(
2166             [](const crow::Request&,
2167                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2168                const std::string& param) {
2169                 deleteDumpEntry(asyncResp, param, "bmc");
2170             });
2171 }
2172 
2173 inline void requestRoutesBMCDumpCreate(App& app)
2174 {
2175 
2176     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2177                       "Actions/"
2178                       "LogService.CollectDiagnosticData/")
2179         .privileges(redfish::privileges::postLogService)
2180         .methods(boost::beast::http::verb::post)(
2181             [](const crow::Request& req,
2182                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2183                 createDump(asyncResp, req, "BMC");
2184             });
2185 }
2186 
2187 inline void requestRoutesBMCDumpClear(App& app)
2188 {
2189     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2190                       "Actions/"
2191                       "LogService.ClearLog/")
2192         .privileges(redfish::privileges::postLogService)
2193         .methods(boost::beast::http::verb::post)(
2194             [](const crow::Request&,
2195                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2196                 clearDump(asyncResp, "BMC");
2197             });
2198 }
2199 
2200 inline void requestRoutesSystemDumpService(App& app)
2201 {
2202     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
2203         .privileges(redfish::privileges::getLogService)
2204         .methods(boost::beast::http::verb::get)(
2205             [](const crow::Request&,
2206                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2207 
2208             {
2209                 asyncResp->res.jsonValue["@odata.id"] =
2210                     "/redfish/v1/Systems/system/LogServices/Dump";
2211                 asyncResp->res.jsonValue["@odata.type"] =
2212                     "#LogService.v1_2_0.LogService";
2213                 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2214                 asyncResp->res.jsonValue["Description"] =
2215                     "System Dump LogService";
2216                 asyncResp->res.jsonValue["Id"] = "Dump";
2217                 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2218 
2219                 std::pair<std::string, std::string> redfishDateTimeOffset =
2220                     crow::utility::getDateTimeOffsetNow();
2221                 asyncResp->res.jsonValue["DateTime"] =
2222                     redfishDateTimeOffset.first;
2223                 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2224                     redfishDateTimeOffset.second;
2225 
2226                 asyncResp->res.jsonValue["Entries"] = {
2227                     {"@odata.id",
2228                      "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2229                 asyncResp->res.jsonValue["Actions"] = {
2230                     {"#LogService.ClearLog",
2231                      {{"target",
2232                        "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2233                        "LogService.ClearLog"}}},
2234                     {"#LogService.CollectDiagnosticData",
2235                      {{"target",
2236                        "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2237                        "LogService.CollectDiagnosticData"}}}};
2238             });
2239 }
2240 
2241 inline void requestRoutesSystemDumpEntryCollection(App& app)
2242 {
2243 
2244     /**
2245      * Functions triggers appropriate requests on DBus
2246      */
2247     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
2248         .privileges(redfish::privileges::getLogEntryCollection)
2249         .methods(boost::beast::http::verb::get)(
2250             [](const crow::Request&,
2251                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2252                 asyncResp->res.jsonValue["@odata.type"] =
2253                     "#LogEntryCollection.LogEntryCollection";
2254                 asyncResp->res.jsonValue["@odata.id"] =
2255                     "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2256                 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2257                 asyncResp->res.jsonValue["Description"] =
2258                     "Collection of System Dump Entries";
2259 
2260                 getDumpEntryCollection(asyncResp, "System");
2261             });
2262 }
2263 
2264 inline void requestRoutesSystemDumpEntry(App& app)
2265 {
2266     BMCWEB_ROUTE(app,
2267                  "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
2268         .privileges(redfish::privileges::getLogEntry)
2269 
2270         .methods(boost::beast::http::verb::get)(
2271             [](const crow::Request&,
2272                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2273                const std::string& param) {
2274                 getDumpEntryById(asyncResp, param, "System");
2275             });
2276 
2277     BMCWEB_ROUTE(app,
2278                  "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
2279         .privileges(redfish::privileges::deleteLogEntry)
2280         .methods(boost::beast::http::verb::delete_)(
2281             [](const crow::Request&,
2282                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2283                const std::string& param) {
2284                 deleteDumpEntry(asyncResp, param, "system");
2285             });
2286 }
2287 
2288 inline void requestRoutesSystemDumpCreate(App& app)
2289 {
2290     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2291                       "Actions/"
2292                       "LogService.CollectDiagnosticData/")
2293         .privileges(redfish::privileges::postLogService)
2294         .methods(boost::beast::http::verb::post)(
2295             [](const crow::Request& req,
2296                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2297 
2298             { createDump(asyncResp, req, "System"); });
2299 }
2300 
2301 inline void requestRoutesSystemDumpClear(App& app)
2302 {
2303     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2304                       "Actions/"
2305                       "LogService.ClearLog/")
2306         .privileges(redfish::privileges::postLogService)
2307         .methods(boost::beast::http::verb::post)(
2308             [](const crow::Request&,
2309                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2310 
2311             { clearDump(asyncResp, "System"); });
2312 }
2313 
2314 inline void requestRoutesCrashdumpService(App& app)
2315 {
2316     // Note: Deviated from redfish privilege registry for GET & HEAD
2317     // method for security reasons.
2318     /**
2319      * Functions triggers appropriate requests on DBus
2320      */
2321     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
2322         // This is incorrect, should be:
2323         //.privileges(redfish::privileges::getLogService)
2324         .privileges({{"ConfigureManager"}})
2325         .methods(
2326             boost::beast::http::verb::
2327                 get)([](const crow::Request&,
2328                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2329             // Copy over the static data to include the entries added by
2330             // SubRoute
2331             asyncResp->res.jsonValue["@odata.id"] =
2332                 "/redfish/v1/Systems/system/LogServices/Crashdump";
2333             asyncResp->res.jsonValue["@odata.type"] =
2334                 "#LogService.v1_2_0.LogService";
2335             asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2336             asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2337             asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2338             asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2339             asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
2340 
2341             std::pair<std::string, std::string> redfishDateTimeOffset =
2342                 crow::utility::getDateTimeOffsetNow();
2343             asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2344             asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2345                 redfishDateTimeOffset.second;
2346 
2347             asyncResp->res.jsonValue["Entries"] = {
2348                 {"@odata.id",
2349                  "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2350             asyncResp->res.jsonValue["Actions"] = {
2351                 {"#LogService.ClearLog",
2352                  {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2353                              "Actions/LogService.ClearLog"}}},
2354                 {"#LogService.CollectDiagnosticData",
2355                  {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2356                              "Actions/LogService.CollectDiagnosticData"}}}};
2357         });
2358 }
2359 
2360 void inline requestRoutesCrashdumpClear(App& app)
2361 {
2362     BMCWEB_ROUTE(app,
2363                  "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2364                  "LogService.ClearLog/")
2365         // This is incorrect, should be:
2366         //.privileges(redfish::privileges::postLogService)
2367         .privileges({{"ConfigureComponents"}})
2368         .methods(boost::beast::http::verb::post)(
2369             [](const crow::Request&,
2370                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2371                 crow::connections::systemBus->async_method_call(
2372                     [asyncResp](const boost::system::error_code ec,
2373                                 const std::string&) {
2374                         if (ec)
2375                         {
2376                             messages::internalError(asyncResp->res);
2377                             return;
2378                         }
2379                         messages::success(asyncResp->res);
2380                     },
2381                     crashdumpObject, crashdumpPath, deleteAllInterface,
2382                     "DeleteAll");
2383             });
2384 }
2385 
2386 static void
2387     logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2388                       const std::string& logID, nlohmann::json& logEntryJson)
2389 {
2390     auto getStoredLogCallback =
2391         [asyncResp, logID, &logEntryJson](
2392             const boost::system::error_code ec,
2393             const std::vector<std::pair<std::string, VariantType>>& params) {
2394             if (ec)
2395             {
2396                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2397                 if (ec.value() ==
2398                     boost::system::linux_error::bad_request_descriptor)
2399                 {
2400                     messages::resourceNotFound(asyncResp->res, "LogEntry",
2401                                                logID);
2402                 }
2403                 else
2404                 {
2405                     messages::internalError(asyncResp->res);
2406                 }
2407                 return;
2408             }
2409 
2410             std::string timestamp{};
2411             std::string filename{};
2412             std::string logfile{};
2413             parseCrashdumpParameters(params, filename, timestamp, logfile);
2414 
2415             if (filename.empty() || timestamp.empty())
2416             {
2417                 messages::resourceMissingAtURI(asyncResp->res, logID);
2418                 return;
2419             }
2420 
2421             std::string crashdumpURI =
2422                 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2423                 logID + "/" + filename;
2424             logEntryJson = {{"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
2425                             {"@odata.id", "/redfish/v1/Systems/system/"
2426                                           "LogServices/Crashdump/Entries/" +
2427                                               logID},
2428                             {"Name", "CPU Crashdump"},
2429                             {"Id", logID},
2430                             {"EntryType", "Oem"},
2431                             {"AdditionalDataURI", std::move(crashdumpURI)},
2432                             {"DiagnosticDataType", "OEM"},
2433                             {"OEMDiagnosticDataType", "PECICrashdump"},
2434                             {"Created", std::move(timestamp)}};
2435         };
2436     crow::connections::systemBus->async_method_call(
2437         std::move(getStoredLogCallback), crashdumpObject,
2438         crashdumpPath + std::string("/") + logID,
2439         "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2440 }
2441 
2442 inline void requestRoutesCrashdumpEntryCollection(App& app)
2443 {
2444     // Note: Deviated from redfish privilege registry for GET & HEAD
2445     // method for security reasons.
2446     /**
2447      * Functions triggers appropriate requests on DBus
2448      */
2449     BMCWEB_ROUTE(app,
2450                  "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
2451         // This is incorrect, should be.
2452         //.privileges(redfish::privileges::postLogEntryCollection)
2453         .privileges({{"ConfigureComponents"}})
2454         .methods(
2455             boost::beast::http::verb::
2456                 get)([](const crow::Request&,
2457                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2458             // Collections don't include the static data added by SubRoute
2459             // because it has a duplicate entry for members
2460             auto getLogEntriesCallback = [asyncResp](
2461                                              const boost::system::error_code ec,
2462                                              const std::vector<std::string>&
2463                                                  resp) {
2464                 if (ec)
2465                 {
2466                     if (ec.value() !=
2467                         boost::system::errc::no_such_file_or_directory)
2468                     {
2469                         BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2470                                          << ec.message();
2471                         messages::internalError(asyncResp->res);
2472                         return;
2473                     }
2474                 }
2475                 asyncResp->res.jsonValue["@odata.type"] =
2476                     "#LogEntryCollection.LogEntryCollection";
2477                 asyncResp->res.jsonValue["@odata.id"] =
2478                     "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2479                 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2480                 asyncResp->res.jsonValue["Description"] =
2481                     "Collection of Crashdump Entries";
2482                 nlohmann::json& logEntryArray =
2483                     asyncResp->res.jsonValue["Members"];
2484                 logEntryArray = nlohmann::json::array();
2485                 std::vector<std::string> logIDs;
2486                 // Get the list of log entries and build up an empty array big
2487                 // enough to hold them
2488                 for (const std::string& objpath : resp)
2489                 {
2490                     // Get the log ID
2491                     std::size_t lastPos = objpath.rfind('/');
2492                     if (lastPos == std::string::npos)
2493                     {
2494                         continue;
2495                     }
2496                     logIDs.emplace_back(objpath.substr(lastPos + 1));
2497 
2498                     // Add a space for the log entry to the array
2499                     logEntryArray.push_back({});
2500                 }
2501                 // Now go through and set up async calls to fill in the entries
2502                 size_t index = 0;
2503                 for (const std::string& logID : logIDs)
2504                 {
2505                     // Add the log entry to the array
2506                     logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
2507                 }
2508                 asyncResp->res.jsonValue["Members@odata.count"] =
2509                     logEntryArray.size();
2510             };
2511             crow::connections::systemBus->async_method_call(
2512                 std::move(getLogEntriesCallback),
2513                 "xyz.openbmc_project.ObjectMapper",
2514                 "/xyz/openbmc_project/object_mapper",
2515                 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2516                 std::array<const char*, 1>{crashdumpInterface});
2517         });
2518 }
2519 
2520 inline void requestRoutesCrashdumpEntry(App& app)
2521 {
2522     // Note: Deviated from redfish privilege registry for GET & HEAD
2523     // method for security reasons.
2524 
2525     BMCWEB_ROUTE(
2526         app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
2527         // this is incorrect, should be
2528         // .privileges(redfish::privileges::getLogEntry)
2529         .privileges({{"ConfigureComponents"}})
2530         .methods(boost::beast::http::verb::get)(
2531             [](const crow::Request&,
2532                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2533                const std::string& param) {
2534                 const std::string& logID = param;
2535                 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2536             });
2537 }
2538 
2539 inline void requestRoutesCrashdumpFile(App& app)
2540 {
2541     // Note: Deviated from redfish privilege registry for GET & HEAD
2542     // method for security reasons.
2543     BMCWEB_ROUTE(
2544         app,
2545         "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
2546         .privileges(redfish::privileges::getLogEntry)
2547         .methods(boost::beast::http::verb::get)(
2548             [](const crow::Request&,
2549                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2550                const std::string& logID, const std::string& fileName) {
2551                 auto getStoredLogCallback =
2552                     [asyncResp, logID, fileName](
2553                         const boost::system::error_code ec,
2554                         const std::vector<std::pair<std::string, VariantType>>&
2555                             resp) {
2556                         if (ec)
2557                         {
2558                             BMCWEB_LOG_DEBUG << "failed to get log ec: "
2559                                              << ec.message();
2560                             messages::internalError(asyncResp->res);
2561                             return;
2562                         }
2563 
2564                         std::string dbusFilename{};
2565                         std::string dbusTimestamp{};
2566                         std::string dbusFilepath{};
2567 
2568                         parseCrashdumpParameters(resp, dbusFilename,
2569                                                  dbusTimestamp, dbusFilepath);
2570 
2571                         if (dbusFilename.empty() || dbusTimestamp.empty() ||
2572                             dbusFilepath.empty())
2573                         {
2574                             messages::resourceMissingAtURI(asyncResp->res,
2575                                                            fileName);
2576                             return;
2577                         }
2578 
2579                         // Verify the file name parameter is correct
2580                         if (fileName != dbusFilename)
2581                         {
2582                             messages::resourceMissingAtURI(asyncResp->res,
2583                                                            fileName);
2584                             return;
2585                         }
2586 
2587                         if (!std::filesystem::exists(dbusFilepath))
2588                         {
2589                             messages::resourceMissingAtURI(asyncResp->res,
2590                                                            fileName);
2591                             return;
2592                         }
2593                         std::ifstream ifs(dbusFilepath, std::ios::in |
2594                                                             std::ios::binary |
2595                                                             std::ios::ate);
2596                         std::ifstream::pos_type fileSize = ifs.tellg();
2597                         if (fileSize < 0)
2598                         {
2599                             messages::generalError(asyncResp->res);
2600                             return;
2601                         }
2602                         ifs.seekg(0, std::ios::beg);
2603 
2604                         auto crashData = std::make_unique<char[]>(
2605                             static_cast<unsigned int>(fileSize));
2606 
2607                         ifs.read(crashData.get(), static_cast<int>(fileSize));
2608 
2609                         // The cast to std::string is intentional in order to
2610                         // use the assign() that applies move mechanics
2611                         asyncResp->res.body().assign(
2612                             static_cast<std::string>(crashData.get()));
2613 
2614                         // Configure this to be a file download when accessed
2615                         // from a browser
2616                         asyncResp->res.addHeader("Content-Disposition",
2617                                                  "attachment");
2618                     };
2619                 crow::connections::systemBus->async_method_call(
2620                     std::move(getStoredLogCallback), crashdumpObject,
2621                     crashdumpPath + std::string("/") + logID,
2622                     "org.freedesktop.DBus.Properties", "GetAll",
2623                     crashdumpInterface);
2624             });
2625 }
2626 
2627 inline void requestRoutesCrashdumpCollect(App& app)
2628 {
2629     // Note: Deviated from redfish privilege registry for GET & HEAD
2630     // method for security reasons.
2631     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/"
2632                       "Actions/LogService.CollectDiagnosticData/")
2633         // The below is incorrect;  Should be ConfigureManager
2634         //.privileges(redfish::privileges::postLogService)
2635         .privileges({{"ConfigureComponents"}})
2636         .methods(
2637             boost::beast::http::verb::
2638                 post)([](const crow::Request& req,
2639                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2640             std::string diagnosticDataType;
2641             std::string oemDiagnosticDataType;
2642             if (!redfish::json_util::readJson(
2643                     req, asyncResp->res, "DiagnosticDataType",
2644                     diagnosticDataType, "OEMDiagnosticDataType",
2645                     oemDiagnosticDataType))
2646             {
2647                 return;
2648             }
2649 
2650             if (diagnosticDataType != "OEM")
2651             {
2652                 BMCWEB_LOG_ERROR
2653                     << "Only OEM DiagnosticDataType supported for Crashdump";
2654                 messages::actionParameterValueFormatError(
2655                     asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2656                     "CollectDiagnosticData");
2657                 return;
2658             }
2659 
2660             auto collectCrashdumpCallback = [asyncResp, req](
2661                                                 const boost::system::error_code
2662                                                     ec,
2663                                                 const std::string&) {
2664                 if (ec)
2665                 {
2666                     if (ec.value() ==
2667                         boost::system::errc::operation_not_supported)
2668                     {
2669                         messages::resourceInStandby(asyncResp->res);
2670                     }
2671                     else if (ec.value() ==
2672                              boost::system::errc::device_or_resource_busy)
2673                     {
2674                         messages::serviceTemporarilyUnavailable(asyncResp->res,
2675                                                                 "60");
2676                     }
2677                     else
2678                     {
2679                         messages::internalError(asyncResp->res);
2680                     }
2681                     return;
2682                 }
2683                 std::shared_ptr<task::TaskData> task =
2684                     task::TaskData::createTask(
2685                         [](boost::system::error_code err,
2686                            sdbusplus::message::message&,
2687                            const std::shared_ptr<task::TaskData>& taskData) {
2688                             if (!err)
2689                             {
2690                                 taskData->messages.emplace_back(
2691                                     messages::taskCompletedOK(
2692                                         std::to_string(taskData->index)));
2693                                 taskData->state = "Completed";
2694                             }
2695                             return task::completed;
2696                         },
2697                         "type='signal',interface='org.freedesktop.DBus."
2698                         "Properties',"
2699                         "member='PropertiesChanged',arg0namespace='com.intel."
2700                         "crashdump'");
2701                 task->startTimer(std::chrono::minutes(5));
2702                 task->populateResp(asyncResp->res);
2703                 task->payload.emplace(req);
2704             };
2705 
2706             if (oemDiagnosticDataType == "OnDemand")
2707             {
2708                 crow::connections::systemBus->async_method_call(
2709                     std::move(collectCrashdumpCallback), crashdumpObject,
2710                     crashdumpPath, crashdumpOnDemandInterface,
2711                     "GenerateOnDemandLog");
2712             }
2713             else if (oemDiagnosticDataType == "Telemetry")
2714             {
2715                 crow::connections::systemBus->async_method_call(
2716                     std::move(collectCrashdumpCallback), crashdumpObject,
2717                     crashdumpPath, crashdumpTelemetryInterface,
2718                     "GenerateTelemetryLog");
2719             }
2720             else
2721             {
2722                 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2723                                  << oemDiagnosticDataType;
2724                 messages::actionParameterValueFormatError(
2725                     asyncResp->res, oemDiagnosticDataType,
2726                     "OEMDiagnosticDataType", "CollectDiagnosticData");
2727                 return;
2728             }
2729         });
2730 }
2731 
2732 /**
2733  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2734  */
2735 inline void requestRoutesDBusLogServiceActionsClear(App& app)
2736 {
2737     /**
2738      * Function handles POST method request.
2739      * The Clear Log actions does not require any parameter.The action deletes
2740      * all entries found in the Entries collection for this Log Service.
2741      */
2742 
2743     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
2744                       "LogService.ClearLog/")
2745         .privileges(redfish::privileges::postLogService)
2746         .methods(boost::beast::http::verb::post)(
2747             [](const crow::Request&,
2748                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2749                 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2750 
2751                 // Process response from Logging service.
2752                 auto respHandler = [asyncResp](
2753                                        const boost::system::error_code ec) {
2754                     BMCWEB_LOG_DEBUG
2755                         << "doClearLog resp_handler callback: Done";
2756                     if (ec)
2757                     {
2758                         // TODO Handle for specific error code
2759                         BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
2760                                          << ec;
2761                         asyncResp->res.result(
2762                             boost::beast::http::status::internal_server_error);
2763                         return;
2764                     }
2765 
2766                     asyncResp->res.result(
2767                         boost::beast::http::status::no_content);
2768                 };
2769 
2770                 // Make call to Logging service to request Clear Log
2771                 crow::connections::systemBus->async_method_call(
2772                     respHandler, "xyz.openbmc_project.Logging",
2773                     "/xyz/openbmc_project/logging",
2774                     "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2775             });
2776 }
2777 
2778 /****************************************************
2779  * Redfish PostCode interfaces
2780  * using DBUS interface: getPostCodesTS
2781  ******************************************************/
2782 inline void requestRoutesPostCodesLogService(App& app)
2783 {
2784     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2785         .privileges(redfish::privileges::getLogService)
2786         .methods(boost::beast::http::verb::get)(
2787             [](const crow::Request&,
2788                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2789                 asyncResp->res.jsonValue = {
2790                     {"@odata.id",
2791                      "/redfish/v1/Systems/system/LogServices/PostCodes"},
2792                     {"@odata.type", "#LogService.v1_1_0.LogService"},
2793                     {"Name", "POST Code Log Service"},
2794                     {"Description", "POST Code Log Service"},
2795                     {"Id", "BIOS POST Code Log"},
2796                     {"OverWritePolicy", "WrapsWhenFull"},
2797                     {"Entries",
2798                      {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2799                                     "PostCodes/Entries"}}}};
2800 
2801                 std::pair<std::string, std::string> redfishDateTimeOffset =
2802                     crow::utility::getDateTimeOffsetNow();
2803                 asyncResp->res.jsonValue["DateTime"] =
2804                     redfishDateTimeOffset.first;
2805                 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2806                     redfishDateTimeOffset.second;
2807 
2808                 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2809                     {"target",
2810                      "/redfish/v1/Systems/system/LogServices/PostCodes/"
2811                      "Actions/LogService.ClearLog"}};
2812             });
2813 }
2814 
2815 inline void requestRoutesPostCodesClear(App& app)
2816 {
2817     BMCWEB_ROUTE(app,
2818                  "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2819                  "LogService.ClearLog/")
2820         // The following privilege is incorrect;  It should be ConfigureManager
2821         //.privileges(redfish::privileges::postLogService)
2822         .privileges({{"ConfigureComponents"}})
2823         .methods(boost::beast::http::verb::post)(
2824             [](const crow::Request&,
2825                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2826                 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2827 
2828                 // Make call to post-code service to request clear all
2829                 crow::connections::systemBus->async_method_call(
2830                     [asyncResp](const boost::system::error_code ec) {
2831                         if (ec)
2832                         {
2833                             // TODO Handle for specific error code
2834                             BMCWEB_LOG_ERROR
2835                                 << "doClearPostCodes resp_handler got error "
2836                                 << ec;
2837                             asyncResp->res.result(boost::beast::http::status::
2838                                                       internal_server_error);
2839                             messages::internalError(asyncResp->res);
2840                             return;
2841                         }
2842                     },
2843                     "xyz.openbmc_project.State.Boot.PostCode0",
2844                     "/xyz/openbmc_project/State/Boot/PostCode0",
2845                     "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2846             });
2847 }
2848 
2849 static void fillPostCodeEntry(
2850     const std::shared_ptr<bmcweb::AsyncResp>& aResp,
2851     const boost::container::flat_map<
2852         uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
2853     const uint16_t bootIndex, const uint64_t codeIndex = 0,
2854     const uint64_t skip = 0, const uint64_t top = 0)
2855 {
2856     // Get the Message from the MessageRegistry
2857     const message_registries::Message* message =
2858         message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
2859 
2860     uint64_t currentCodeIndex = 0;
2861     nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
2862 
2863     uint64_t firstCodeTimeUs = 0;
2864     for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2865              code : postcode)
2866     {
2867         currentCodeIndex++;
2868         std::string postcodeEntryID =
2869             "B" + std::to_string(bootIndex) + "-" +
2870             std::to_string(currentCodeIndex); // 1 based index in EntryID string
2871 
2872         uint64_t usecSinceEpoch = code.first;
2873         uint64_t usTimeOffset = 0;
2874 
2875         if (1 == currentCodeIndex)
2876         { // already incremented
2877             firstCodeTimeUs = code.first;
2878         }
2879         else
2880         {
2881             usTimeOffset = code.first - firstCodeTimeUs;
2882         }
2883 
2884         // skip if no specific codeIndex is specified and currentCodeIndex does
2885         // not fall between top and skip
2886         if ((codeIndex == 0) &&
2887             (currentCodeIndex <= skip || currentCodeIndex > top))
2888         {
2889             continue;
2890         }
2891 
2892         // skip if a specific codeIndex is specified and does not match the
2893         // currentIndex
2894         if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2895         {
2896             // This is done for simplicity. 1st entry is needed to calculate
2897             // time offset. To improve efficiency, one can get to the entry
2898             // directly (possibly with flatmap's nth method)
2899             continue;
2900         }
2901 
2902         // currentCodeIndex is within top and skip or equal to specified code
2903         // index
2904 
2905         // Get the Created time from the timestamp
2906         std::string entryTimeStr;
2907         entryTimeStr = crow::utility::getDateTime(
2908             static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
2909 
2910         // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2911         std::ostringstream hexCode;
2912         hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2913                 << std::get<0>(code.second);
2914         std::ostringstream timeOffsetStr;
2915         // Set Fixed -Point Notation
2916         timeOffsetStr << std::fixed;
2917         // Set precision to 4 digits
2918         timeOffsetStr << std::setprecision(4);
2919         // Add double to stream
2920         timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2921         std::vector<std::string> messageArgs = {
2922             std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2923 
2924         // Get MessageArgs template from message registry
2925         std::string msg;
2926         if (message != nullptr)
2927         {
2928             msg = message->message;
2929 
2930             // fill in this post code value
2931             int i = 0;
2932             for (const std::string& messageArg : messageArgs)
2933             {
2934                 std::string argStr = "%" + std::to_string(++i);
2935                 size_t argPos = msg.find(argStr);
2936                 if (argPos != std::string::npos)
2937                 {
2938                     msg.replace(argPos, argStr.length(), messageArg);
2939                 }
2940             }
2941         }
2942 
2943         // Get Severity template from message registry
2944         std::string severity;
2945         if (message != nullptr)
2946         {
2947             severity = message->severity;
2948         }
2949 
2950         // add to AsyncResp
2951         logEntryArray.push_back({});
2952         nlohmann::json& bmcLogEntry = logEntryArray.back();
2953         bmcLogEntry = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2954                        {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2955                                      "PostCodes/Entries/" +
2956                                          postcodeEntryID},
2957                        {"Name", "POST Code Log Entry"},
2958                        {"Id", postcodeEntryID},
2959                        {"Message", std::move(msg)},
2960                        {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
2961                        {"MessageArgs", std::move(messageArgs)},
2962                        {"EntryType", "Event"},
2963                        {"Severity", std::move(severity)},
2964                        {"Created", entryTimeStr}};
2965     }
2966 }
2967 
2968 static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
2969                                 const uint16_t bootIndex,
2970                                 const uint64_t codeIndex)
2971 {
2972     crow::connections::systemBus->async_method_call(
2973         [aResp, bootIndex,
2974          codeIndex](const boost::system::error_code ec,
2975                     const boost::container::flat_map<
2976                         uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2977                         postcode) {
2978             if (ec)
2979             {
2980                 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2981                 messages::internalError(aResp->res);
2982                 return;
2983             }
2984 
2985             // skip the empty postcode boots
2986             if (postcode.empty())
2987             {
2988                 return;
2989             }
2990 
2991             fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2992 
2993             aResp->res.jsonValue["Members@odata.count"] =
2994                 aResp->res.jsonValue["Members"].size();
2995         },
2996         "xyz.openbmc_project.State.Boot.PostCode0",
2997         "/xyz/openbmc_project/State/Boot/PostCode0",
2998         "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2999         bootIndex);
3000 }
3001 
3002 static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3003                                const uint16_t bootIndex,
3004                                const uint16_t bootCount,
3005                                const uint64_t entryCount, const uint64_t skip,
3006                                const uint64_t top)
3007 {
3008     crow::connections::systemBus->async_method_call(
3009         [aResp, bootIndex, bootCount, entryCount, skip,
3010          top](const boost::system::error_code ec,
3011               const boost::container::flat_map<
3012                   uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3013                   postcode) {
3014             if (ec)
3015             {
3016                 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3017                 messages::internalError(aResp->res);
3018                 return;
3019             }
3020 
3021             uint64_t endCount = entryCount;
3022             if (!postcode.empty())
3023             {
3024                 endCount = entryCount + postcode.size();
3025 
3026                 if ((skip < endCount) && ((top + skip) > entryCount))
3027                 {
3028                     uint64_t thisBootSkip =
3029                         std::max(skip, entryCount) - entryCount;
3030                     uint64_t thisBootTop =
3031                         std::min(top + skip, endCount) - entryCount;
3032 
3033                     fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3034                                       thisBootSkip, thisBootTop);
3035                 }
3036                 aResp->res.jsonValue["Members@odata.count"] = endCount;
3037             }
3038 
3039             // continue to previous bootIndex
3040             if (bootIndex < bootCount)
3041             {
3042                 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3043                                    bootCount, endCount, skip, top);
3044             }
3045             else
3046             {
3047                 aResp->res.jsonValue["Members@odata.nextLink"] =
3048                     "/redfish/v1/Systems/system/LogServices/PostCodes/"
3049                     "Entries?$skip=" +
3050                     std::to_string(skip + top);
3051             }
3052         },
3053         "xyz.openbmc_project.State.Boot.PostCode0",
3054         "/xyz/openbmc_project/State/Boot/PostCode0",
3055         "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3056         bootIndex);
3057 }
3058 
3059 static void
3060     getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3061                          const uint64_t skip, const uint64_t top)
3062 {
3063     uint64_t entryCount = 0;
3064     crow::connections::systemBus->async_method_call(
3065         [aResp, entryCount, skip,
3066          top](const boost::system::error_code ec,
3067               const std::variant<uint16_t>& bootCount) {
3068             if (ec)
3069             {
3070                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3071                 messages::internalError(aResp->res);
3072                 return;
3073             }
3074             auto pVal = std::get_if<uint16_t>(&bootCount);
3075             if (pVal)
3076             {
3077                 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3078             }
3079             else
3080             {
3081                 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3082             }
3083         },
3084         "xyz.openbmc_project.State.Boot.PostCode0",
3085         "/xyz/openbmc_project/State/Boot/PostCode0",
3086         "org.freedesktop.DBus.Properties", "Get",
3087         "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3088 }
3089 
3090 inline void requestRoutesPostCodesEntryCollection(App& app)
3091 {
3092     BMCWEB_ROUTE(app,
3093                  "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3094         .privileges(redfish::privileges::getLogEntryCollection)
3095         .methods(boost::beast::http::verb::get)(
3096             [](const crow::Request& req,
3097                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3098                 asyncResp->res.jsonValue["@odata.type"] =
3099                     "#LogEntryCollection.LogEntryCollection";
3100                 asyncResp->res.jsonValue["@odata.id"] =
3101                     "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3102                 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3103                 asyncResp->res.jsonValue["Description"] =
3104                     "Collection of POST Code Log Entries";
3105                 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3106                 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3107 
3108                 uint64_t skip = 0;
3109                 uint64_t top = maxEntriesPerPage; // Show max entries by default
3110                 if (!getSkipParam(asyncResp, req, skip))
3111                 {
3112                     return;
3113                 }
3114                 if (!getTopParam(asyncResp, req, top))
3115                 {
3116                     return;
3117                 }
3118                 getCurrentBootNumber(asyncResp, skip, top);
3119             });
3120 }
3121 
3122 inline void requestRoutesPostCodesEntry(App& app)
3123 {
3124     BMCWEB_ROUTE(
3125         app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
3126         .privileges(redfish::privileges::getLogEntry)
3127         .methods(boost::beast::http::verb::get)(
3128             [](const crow::Request&,
3129                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3130                const std::string& targetID) {
3131                 size_t bootPos = targetID.find('B');
3132                 if (bootPos == std::string::npos)
3133                 {
3134                     // Requested ID was not found
3135                     messages::resourceMissingAtURI(asyncResp->res, targetID);
3136                     return;
3137                 }
3138                 std::string_view bootIndexStr(targetID);
3139                 bootIndexStr.remove_prefix(bootPos + 1);
3140                 uint16_t bootIndex = 0;
3141                 uint64_t codeIndex = 0;
3142                 size_t dashPos = bootIndexStr.find('-');
3143 
3144                 if (dashPos == std::string::npos)
3145                 {
3146                     return;
3147                 }
3148                 std::string_view codeIndexStr(bootIndexStr);
3149                 bootIndexStr.remove_suffix(dashPos);
3150                 codeIndexStr.remove_prefix(dashPos + 1);
3151 
3152                 bootIndex = static_cast<uint16_t>(
3153                     strtoul(std::string(bootIndexStr).c_str(), nullptr, 0));
3154                 codeIndex =
3155                     strtoul(std::string(codeIndexStr).c_str(), nullptr, 0);
3156                 if (bootIndex == 0 || codeIndex == 0)
3157                 {
3158                     BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3159                                      << targetID;
3160                 }
3161 
3162                 asyncResp->res.jsonValue["@odata.type"] =
3163                     "#LogEntry.v1_4_0.LogEntry";
3164                 asyncResp->res.jsonValue["@odata.id"] =
3165                     "/redfish/v1/Systems/system/LogServices/PostCodes/"
3166                     "Entries";
3167                 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3168                 asyncResp->res.jsonValue["Description"] =
3169                     "Collection of POST Code Log Entries";
3170                 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3171                 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3172 
3173                 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3174             });
3175 }
3176 
3177 } // namespace redfish
3178