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