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