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