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