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