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     std::string msg = redfish::registries::fillMessageArgs(messageArgs,
1319                                                            message->message);
1320     if (msg.empty())
1321     {
1322         return LogParseError::parseFailed;
1323     }
1324 
1325     // Get the Created time from the timestamp. The log timestamp is in RFC3339
1326     // format which matches the Redfish format except for the fractional seconds
1327     // between the '.' and the '+', so just remove them.
1328     std::size_t dot = timestamp.find_first_of('.');
1329     std::size_t plus = timestamp.find_first_of('+');
1330     if (dot != std::string::npos && plus != std::string::npos)
1331     {
1332         timestamp.erase(dot, plus - dot);
1333     }
1334 
1335     // Fill in the log entry with the gathered data
1336     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1337     logEntryJson["@odata.id"] = boost::urls::format(
1338         "/redfish/v1/Systems/system/LogServices/EventLog/Entries/{}",
1339         logEntryID);
1340     logEntryJson["Name"] = "System Event Log Entry";
1341     logEntryJson["Id"] = logEntryID;
1342     logEntryJson["Message"] = std::move(msg);
1343     logEntryJson["MessageId"] = std::move(messageID);
1344     logEntryJson["MessageArgs"] = messageArgs;
1345     logEntryJson["EntryType"] = "Event";
1346     logEntryJson["Severity"] = message->messageSeverity;
1347     logEntryJson["Created"] = std::move(timestamp);
1348     return LogParseError::success;
1349 }
1350 
1351 inline void requestRoutesJournalEventLogEntryCollection(App& app)
1352 {
1353     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1354         .privileges(redfish::privileges::getLogEntryCollection)
1355         .methods(boost::beast::http::verb::get)(
1356             [&app](const crow::Request& req,
1357                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1358                    const std::string& systemName) {
1359         query_param::QueryCapabilities capabilities = {
1360             .canDelegateTop = true,
1361             .canDelegateSkip = true,
1362         };
1363         query_param::Query delegatedQuery;
1364         if (!redfish::setUpRedfishRouteWithDelegation(
1365                 app, req, asyncResp, delegatedQuery, capabilities))
1366         {
1367             return;
1368         }
1369         if constexpr (bmcwebEnableMultiHost)
1370         {
1371             // Option currently returns no systems.  TBD
1372             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1373                                        systemName);
1374             return;
1375         }
1376         if (systemName != "system")
1377         {
1378             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1379                                        systemName);
1380             return;
1381         }
1382 
1383         size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
1384         size_t skip = delegatedQuery.skip.value_or(0);
1385 
1386         // Collections don't include the static data added by SubRoute
1387         // because it has a duplicate entry for members
1388         asyncResp->res.jsonValue["@odata.type"] =
1389             "#LogEntryCollection.LogEntryCollection";
1390         asyncResp->res.jsonValue["@odata.id"] =
1391             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1392         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1393         asyncResp->res.jsonValue["Description"] =
1394             "Collection of System Event Log Entries";
1395 
1396         nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1397         logEntryArray = nlohmann::json::array();
1398         // Go through the log files and create a unique ID for each
1399         // entry
1400         std::vector<std::filesystem::path> redfishLogFiles;
1401         getRedfishLogFiles(redfishLogFiles);
1402         uint64_t entryCount = 0;
1403         std::string logEntry;
1404 
1405         // Oldest logs are in the last file, so start there and loop
1406         // backwards
1407         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1408              it++)
1409         {
1410             std::ifstream logStream(*it);
1411             if (!logStream.is_open())
1412             {
1413                 continue;
1414             }
1415 
1416             // Reset the unique ID on the first entry
1417             bool firstEntry = true;
1418             while (std::getline(logStream, logEntry))
1419             {
1420                 std::string idStr;
1421                 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1422                 {
1423                     continue;
1424                 }
1425                 firstEntry = false;
1426 
1427                 nlohmann::json::object_t bmcLogEntry;
1428                 LogParseError status = fillEventLogEntryJson(idStr, logEntry,
1429                                                              bmcLogEntry);
1430                 if (status == LogParseError::messageIdNotInRegistry)
1431                 {
1432                     continue;
1433                 }
1434                 if (status != LogParseError::success)
1435                 {
1436                     messages::internalError(asyncResp->res);
1437                     return;
1438                 }
1439 
1440                 entryCount++;
1441                 // Handle paging using skip (number of entries to skip from the
1442                 // start) and top (number of entries to display)
1443                 if (entryCount <= skip || entryCount > skip + top)
1444                 {
1445                     continue;
1446                 }
1447 
1448                 logEntryArray.emplace_back(std::move(bmcLogEntry));
1449             }
1450         }
1451         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1452         if (skip + top < entryCount)
1453         {
1454             asyncResp->res.jsonValue["Members@odata.nextLink"] =
1455                 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
1456                 std::to_string(skip + top);
1457         }
1458         });
1459 }
1460 
1461 inline void requestRoutesJournalEventLogEntry(App& app)
1462 {
1463     BMCWEB_ROUTE(
1464         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1465         .privileges(redfish::privileges::getLogEntry)
1466         .methods(boost::beast::http::verb::get)(
1467             [&app](const crow::Request& req,
1468                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1469                    const std::string& systemName, const std::string& param) {
1470         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1471         {
1472             return;
1473         }
1474         if constexpr (bmcwebEnableMultiHost)
1475         {
1476             // Option currently returns no systems.  TBD
1477             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1478                                        systemName);
1479             return;
1480         }
1481 
1482         if (systemName != "system")
1483         {
1484             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1485                                        systemName);
1486             return;
1487         }
1488 
1489         const std::string& targetID = param;
1490 
1491         // Go through the log files and check the unique ID for each
1492         // entry to find the target entry
1493         std::vector<std::filesystem::path> redfishLogFiles;
1494         getRedfishLogFiles(redfishLogFiles);
1495         std::string logEntry;
1496 
1497         // Oldest logs are in the last file, so start there and loop
1498         // backwards
1499         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1500              it++)
1501         {
1502             std::ifstream logStream(*it);
1503             if (!logStream.is_open())
1504             {
1505                 continue;
1506             }
1507 
1508             // Reset the unique ID on the first entry
1509             bool firstEntry = true;
1510             while (std::getline(logStream, logEntry))
1511             {
1512                 std::string idStr;
1513                 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1514                 {
1515                     continue;
1516                 }
1517                 firstEntry = false;
1518 
1519                 if (idStr == targetID)
1520                 {
1521                     nlohmann::json::object_t bmcLogEntry;
1522                     LogParseError status =
1523                         fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1524                     if (status != LogParseError::success)
1525                     {
1526                         messages::internalError(asyncResp->res);
1527                         return;
1528                     }
1529                     asyncResp->res.jsonValue.update(bmcLogEntry);
1530                     return;
1531                 }
1532             }
1533         }
1534         // Requested ID was not found
1535         messages::resourceNotFound(asyncResp->res, "LogEntry", targetID);
1536         });
1537 }
1538 
1539 inline void requestRoutesDBusEventLogEntryCollection(App& app)
1540 {
1541     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1542         .privileges(redfish::privileges::getLogEntryCollection)
1543         .methods(boost::beast::http::verb::get)(
1544             [&app](const crow::Request& req,
1545                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1546                    const std::string& systemName) {
1547         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1548         {
1549             return;
1550         }
1551         if constexpr (bmcwebEnableMultiHost)
1552         {
1553             // Option currently returns no systems.  TBD
1554             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1555                                        systemName);
1556             return;
1557         }
1558         if (systemName != "system")
1559         {
1560             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1561                                        systemName);
1562             return;
1563         }
1564 
1565         // Collections don't include the static data added by SubRoute
1566         // because it has a duplicate entry for members
1567         asyncResp->res.jsonValue["@odata.type"] =
1568             "#LogEntryCollection.LogEntryCollection";
1569         asyncResp->res.jsonValue["@odata.id"] =
1570             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1571         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1572         asyncResp->res.jsonValue["Description"] =
1573             "Collection of System Event Log Entries";
1574 
1575         // DBus implementation of EventLog/Entries
1576         // Make call to Logging Service to find all log entry objects
1577         sdbusplus::message::object_path path("/xyz/openbmc_project/logging");
1578         dbus::utility::getManagedObjects(
1579             "xyz.openbmc_project.Logging", path,
1580             [asyncResp](const boost::system::error_code& ec,
1581                         const dbus::utility::ManagedObjectType& resp) {
1582             if (ec)
1583             {
1584                 // TODO Handle for specific error code
1585                 BMCWEB_LOG_ERROR
1586                     << "getLogEntriesIfaceData resp_handler got error " << ec;
1587                 messages::internalError(asyncResp->res);
1588                 return;
1589             }
1590             nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1591             entriesArray = nlohmann::json::array();
1592             for (const auto& objectPath : resp)
1593             {
1594                 const uint32_t* id = nullptr;
1595                 const uint64_t* timestamp = nullptr;
1596                 const uint64_t* updateTimestamp = nullptr;
1597                 const std::string* severity = nullptr;
1598                 const std::string* message = nullptr;
1599                 const std::string* filePath = nullptr;
1600                 const std::string* resolution = nullptr;
1601                 bool resolved = false;
1602                 const std::string* notify = nullptr;
1603 
1604                 for (const auto& interfaceMap : objectPath.second)
1605                 {
1606                     if (interfaceMap.first ==
1607                         "xyz.openbmc_project.Logging.Entry")
1608                     {
1609                         for (const auto& propertyMap : interfaceMap.second)
1610                         {
1611                             if (propertyMap.first == "Id")
1612                             {
1613                                 id = std::get_if<uint32_t>(&propertyMap.second);
1614                             }
1615                             else if (propertyMap.first == "Timestamp")
1616                             {
1617                                 timestamp =
1618                                     std::get_if<uint64_t>(&propertyMap.second);
1619                             }
1620                             else if (propertyMap.first == "UpdateTimestamp")
1621                             {
1622                                 updateTimestamp =
1623                                     std::get_if<uint64_t>(&propertyMap.second);
1624                             }
1625                             else if (propertyMap.first == "Severity")
1626                             {
1627                                 severity = std::get_if<std::string>(
1628                                     &propertyMap.second);
1629                             }
1630                             else if (propertyMap.first == "Resolution")
1631                             {
1632                                 resolution = std::get_if<std::string>(
1633                                     &propertyMap.second);
1634                             }
1635                             else if (propertyMap.first == "Message")
1636                             {
1637                                 message = std::get_if<std::string>(
1638                                     &propertyMap.second);
1639                             }
1640                             else if (propertyMap.first == "Resolved")
1641                             {
1642                                 const bool* resolveptr =
1643                                     std::get_if<bool>(&propertyMap.second);
1644                                 if (resolveptr == nullptr)
1645                                 {
1646                                     messages::internalError(asyncResp->res);
1647                                     return;
1648                                 }
1649                                 resolved = *resolveptr;
1650                             }
1651                             else if (propertyMap.first ==
1652                                      "ServiceProviderNotify")
1653                             {
1654                                 notify = std::get_if<std::string>(
1655                                     &propertyMap.second);
1656                                 if (notify == nullptr)
1657                                 {
1658                                     messages::internalError(asyncResp->res);
1659                                     return;
1660                                 }
1661                             }
1662                         }
1663                         if (id == nullptr || message == nullptr ||
1664                             severity == nullptr)
1665                         {
1666                             messages::internalError(asyncResp->res);
1667                             return;
1668                         }
1669                     }
1670                     else if (interfaceMap.first ==
1671                              "xyz.openbmc_project.Common.FilePath")
1672                     {
1673                         for (const auto& propertyMap : interfaceMap.second)
1674                         {
1675                             if (propertyMap.first == "Path")
1676                             {
1677                                 filePath = std::get_if<std::string>(
1678                                     &propertyMap.second);
1679                             }
1680                         }
1681                     }
1682                 }
1683                 // Object path without the
1684                 // xyz.openbmc_project.Logging.Entry interface, ignore
1685                 // and continue.
1686                 if (id == nullptr || message == nullptr ||
1687                     severity == nullptr || timestamp == nullptr ||
1688                     updateTimestamp == nullptr)
1689                 {
1690                     continue;
1691                 }
1692                 entriesArray.push_back({});
1693                 nlohmann::json& thisEntry = entriesArray.back();
1694                 thisEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1695                 thisEntry["@odata.id"] = boost::urls::format(
1696                     "/redfish/v1/Systems/system/LogServices/EventLog/Entries/{}",
1697                     std::to_string(*id));
1698                 thisEntry["Name"] = "System Event Log Entry";
1699                 thisEntry["Id"] = std::to_string(*id);
1700                 thisEntry["Message"] = *message;
1701                 thisEntry["Resolved"] = resolved;
1702                 if ((resolution != nullptr) && (!(*resolution).empty()))
1703                 {
1704                     thisEntry["Resolution"] = *resolution;
1705                 }
1706                 std::optional<bool> notifyAction =
1707                     getProviderNotifyAction(*notify);
1708                 if (notifyAction)
1709                 {
1710                     thisEntry["ServiceProviderNotified"] = *notifyAction;
1711                 }
1712                 thisEntry["EntryType"] = "Event";
1713                 thisEntry["Severity"] =
1714                     translateSeverityDbusToRedfish(*severity);
1715                 thisEntry["Created"] =
1716                     redfish::time_utils::getDateTimeUintMs(*timestamp);
1717                 thisEntry["Modified"] =
1718                     redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
1719                 if (filePath != nullptr)
1720                 {
1721                     thisEntry["AdditionalDataURI"] =
1722                         "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1723                         std::to_string(*id) + "/attachment";
1724                 }
1725             }
1726             std::sort(
1727                 entriesArray.begin(), entriesArray.end(),
1728                 [](const nlohmann::json& left, const nlohmann::json& right) {
1729                 return (left["Id"] <= right["Id"]);
1730                 });
1731             asyncResp->res.jsonValue["Members@odata.count"] =
1732                 entriesArray.size();
1733             });
1734         });
1735 }
1736 
1737 inline void requestRoutesDBusEventLogEntry(App& app)
1738 {
1739     BMCWEB_ROUTE(
1740         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1741         .privileges(redfish::privileges::getLogEntry)
1742         .methods(boost::beast::http::verb::get)(
1743             [&app](const crow::Request& req,
1744                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1745                    const std::string& systemName, const std::string& param) {
1746         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1747         {
1748             return;
1749         }
1750         if constexpr (bmcwebEnableMultiHost)
1751         {
1752             // Option currently returns no systems.  TBD
1753             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1754                                        systemName);
1755             return;
1756         }
1757         if (systemName != "system")
1758         {
1759             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1760                                        systemName);
1761             return;
1762         }
1763 
1764         std::string entryID = param;
1765         dbus::utility::escapePathForDbus(entryID);
1766 
1767         // DBus implementation of EventLog/Entries
1768         // Make call to Logging Service to find all log entry objects
1769         sdbusplus::asio::getAllProperties(
1770             *crow::connections::systemBus, "xyz.openbmc_project.Logging",
1771             "/xyz/openbmc_project/logging/entry/" + entryID, "",
1772             [asyncResp, entryID](const boost::system::error_code& ec,
1773                                  const dbus::utility::DBusPropertiesMap& resp) {
1774             if (ec.value() == EBADR)
1775             {
1776                 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1777                                            entryID);
1778                 return;
1779             }
1780             if (ec)
1781             {
1782                 BMCWEB_LOG_ERROR
1783                     << "EventLogEntry (DBus) resp_handler got error " << ec;
1784                 messages::internalError(asyncResp->res);
1785                 return;
1786             }
1787             const uint32_t* id = nullptr;
1788             const uint64_t* timestamp = nullptr;
1789             const uint64_t* updateTimestamp = nullptr;
1790             const std::string* severity = nullptr;
1791             const std::string* message = nullptr;
1792             const std::string* filePath = nullptr;
1793             const std::string* resolution = nullptr;
1794             bool resolved = false;
1795             const std::string* notify = nullptr;
1796 
1797             const bool success = sdbusplus::unpackPropertiesNoThrow(
1798                 dbus_utils::UnpackErrorPrinter(), resp, "Id", id, "Timestamp",
1799                 timestamp, "UpdateTimestamp", updateTimestamp, "Severity",
1800                 severity, "Message", message, "Resolved", resolved,
1801                 "Resolution", resolution, "Path", filePath,
1802                 "ServiceProviderNotify", notify);
1803 
1804             if (!success)
1805             {
1806                 messages::internalError(asyncResp->res);
1807                 return;
1808             }
1809 
1810             if (id == nullptr || message == nullptr || severity == nullptr ||
1811                 timestamp == nullptr || updateTimestamp == nullptr ||
1812                 notify == nullptr)
1813             {
1814                 messages::internalError(asyncResp->res);
1815                 return;
1816             }
1817 
1818             asyncResp->res.jsonValue["@odata.type"] =
1819                 "#LogEntry.v1_9_0.LogEntry";
1820             asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1821                 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/{}",
1822                 std::to_string(*id));
1823             asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1824             asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1825             asyncResp->res.jsonValue["Message"] = *message;
1826             asyncResp->res.jsonValue["Resolved"] = resolved;
1827             std::optional<bool> notifyAction = getProviderNotifyAction(*notify);
1828             if (notifyAction)
1829             {
1830                 asyncResp->res.jsonValue["ServiceProviderNotified"] =
1831                     *notifyAction;
1832             }
1833             if ((resolution != nullptr) && (!(*resolution).empty()))
1834             {
1835                 asyncResp->res.jsonValue["Resolution"] = *resolution;
1836             }
1837             asyncResp->res.jsonValue["EntryType"] = "Event";
1838             asyncResp->res.jsonValue["Severity"] =
1839                 translateSeverityDbusToRedfish(*severity);
1840             asyncResp->res.jsonValue["Created"] =
1841                 redfish::time_utils::getDateTimeUintMs(*timestamp);
1842             asyncResp->res.jsonValue["Modified"] =
1843                 redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
1844             if (filePath != nullptr)
1845             {
1846                 asyncResp->res.jsonValue["AdditionalDataURI"] =
1847                     "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1848                     std::to_string(*id) + "/attachment";
1849             }
1850             });
1851         });
1852 
1853     BMCWEB_ROUTE(
1854         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1855         .privileges(redfish::privileges::patchLogEntry)
1856         .methods(boost::beast::http::verb::patch)(
1857             [&app](const crow::Request& req,
1858                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1859                    const std::string& systemName, const std::string& entryId) {
1860         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1861         {
1862             return;
1863         }
1864         if constexpr (bmcwebEnableMultiHost)
1865         {
1866             // Option currently returns no systems.  TBD
1867             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1868                                        systemName);
1869             return;
1870         }
1871         if (systemName != "system")
1872         {
1873             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1874                                        systemName);
1875             return;
1876         }
1877         std::optional<bool> resolved;
1878 
1879         if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1880                                       resolved))
1881         {
1882             return;
1883         }
1884         BMCWEB_LOG_DEBUG << "Set Resolved";
1885 
1886         sdbusplus::asio::setProperty(
1887             *crow::connections::systemBus, "xyz.openbmc_project.Logging",
1888             "/xyz/openbmc_project/logging/entry/" + entryId,
1889             "xyz.openbmc_project.Logging.Entry", "Resolved", *resolved,
1890             [asyncResp, entryId](const boost::system::error_code& ec) {
1891             if (ec)
1892             {
1893                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1894                 messages::internalError(asyncResp->res);
1895                 return;
1896             }
1897             });
1898         });
1899 
1900     BMCWEB_ROUTE(
1901         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1902         .privileges(redfish::privileges::deleteLogEntry)
1903 
1904         .methods(boost::beast::http::verb::delete_)(
1905             [&app](const crow::Request& req,
1906                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1907                    const std::string& systemName, const std::string& param) {
1908         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1909         {
1910             return;
1911         }
1912         if constexpr (bmcwebEnableMultiHost)
1913         {
1914             // Option currently returns no systems.  TBD
1915             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1916                                        systemName);
1917             return;
1918         }
1919         if (systemName != "system")
1920         {
1921             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1922                                        systemName);
1923             return;
1924         }
1925         BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1926 
1927         std::string entryID = param;
1928 
1929         dbus::utility::escapePathForDbus(entryID);
1930 
1931         // Process response from Logging service.
1932         auto respHandler =
1933             [asyncResp, entryID](const boost::system::error_code& ec) {
1934             BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1935             if (ec)
1936             {
1937                 if (ec.value() == EBADR)
1938                 {
1939                     messages::resourceNotFound(asyncResp->res, "LogEntry",
1940                                                entryID);
1941                     return;
1942                 }
1943                 // TODO Handle for specific error code
1944                 BMCWEB_LOG_ERROR
1945                     << "EventLogEntry (DBus) doDelete respHandler got error "
1946                     << ec;
1947                 asyncResp->res.result(
1948                     boost::beast::http::status::internal_server_error);
1949                 return;
1950             }
1951 
1952             asyncResp->res.result(boost::beast::http::status::ok);
1953         };
1954 
1955         // Make call to Logging service to request Delete Log
1956         crow::connections::systemBus->async_method_call(
1957             respHandler, "xyz.openbmc_project.Logging",
1958             "/xyz/openbmc_project/logging/entry/" + entryID,
1959             "xyz.openbmc_project.Object.Delete", "Delete");
1960         });
1961 }
1962 
1963 inline void requestRoutesDBusEventLogEntryDownload(App& app)
1964 {
1965     BMCWEB_ROUTE(
1966         app,
1967         "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment")
1968         .privileges(redfish::privileges::getLogEntry)
1969         .methods(boost::beast::http::verb::get)(
1970             [&app](const crow::Request& req,
1971                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1972                    const std::string& systemName, const std::string& param) {
1973         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1974         {
1975             return;
1976         }
1977         if (!http_helpers::isContentTypeAllowed(
1978                 req.getHeaderValue("Accept"),
1979                 http_helpers::ContentType::OctetStream, true))
1980         {
1981             asyncResp->res.result(boost::beast::http::status::bad_request);
1982             return;
1983         }
1984         if constexpr (bmcwebEnableMultiHost)
1985         {
1986             // Option currently returns no systems.  TBD
1987             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1988                                        systemName);
1989             return;
1990         }
1991         if (systemName != "system")
1992         {
1993             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1994                                        systemName);
1995             return;
1996         }
1997 
1998         std::string entryID = param;
1999         dbus::utility::escapePathForDbus(entryID);
2000 
2001         crow::connections::systemBus->async_method_call(
2002             [asyncResp, entryID](const boost::system::error_code& ec,
2003                                  const sdbusplus::message::unix_fd& unixfd) {
2004             if (ec.value() == EBADR)
2005             {
2006                 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
2007                                            entryID);
2008                 return;
2009             }
2010             if (ec)
2011             {
2012                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2013                 messages::internalError(asyncResp->res);
2014                 return;
2015             }
2016 
2017             int fd = -1;
2018             fd = dup(unixfd);
2019             if (fd == -1)
2020             {
2021                 messages::internalError(asyncResp->res);
2022                 return;
2023             }
2024 
2025             long long int size = lseek(fd, 0, SEEK_END);
2026             if (size == -1)
2027             {
2028                 messages::internalError(asyncResp->res);
2029                 return;
2030             }
2031 
2032             // Arbitrary max size of 64kb
2033             constexpr int maxFileSize = 65536;
2034             if (size > maxFileSize)
2035             {
2036                 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
2037                                  << maxFileSize;
2038                 messages::internalError(asyncResp->res);
2039                 return;
2040             }
2041             std::vector<char> data(static_cast<size_t>(size));
2042             long long int rc = lseek(fd, 0, SEEK_SET);
2043             if (rc == -1)
2044             {
2045                 messages::internalError(asyncResp->res);
2046                 return;
2047             }
2048             rc = read(fd, data.data(), data.size());
2049             if ((rc == -1) || (rc != size))
2050             {
2051                 messages::internalError(asyncResp->res);
2052                 return;
2053             }
2054             close(fd);
2055 
2056             std::string_view strData(data.data(), data.size());
2057             std::string output = crow::utility::base64encode(strData);
2058 
2059             asyncResp->res.addHeader(boost::beast::http::field::content_type,
2060                                      "application/octet-stream");
2061             asyncResp->res.addHeader(
2062                 boost::beast::http::field::content_transfer_encoding, "Base64");
2063             asyncResp->res.body() = std::move(output);
2064             },
2065             "xyz.openbmc_project.Logging",
2066             "/xyz/openbmc_project/logging/entry/" + entryID,
2067             "xyz.openbmc_project.Logging.Entry", "GetEntry");
2068         });
2069 }
2070 
2071 constexpr const char* hostLoggerFolderPath = "/var/log/console";
2072 
2073 inline bool
2074     getHostLoggerFiles(const std::string& hostLoggerFilePath,
2075                        std::vector<std::filesystem::path>& hostLoggerFiles)
2076 {
2077     std::error_code ec;
2078     std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
2079     if (ec)
2080     {
2081         BMCWEB_LOG_ERROR << ec.message();
2082         return false;
2083     }
2084     for (const std::filesystem::directory_entry& it : logPath)
2085     {
2086         std::string filename = it.path().filename();
2087         // Prefix of each log files is "log". Find the file and save the
2088         // path
2089         if (filename.starts_with("log"))
2090         {
2091             hostLoggerFiles.emplace_back(it.path());
2092         }
2093     }
2094     // As the log files rotate, they are appended with a ".#" that is higher for
2095     // the older logs. Since we start from oldest logs, sort the name in
2096     // descending order.
2097     std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
2098               AlphanumLess<std::string>());
2099 
2100     return true;
2101 }
2102 
2103 inline bool getHostLoggerEntries(
2104     const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
2105     uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
2106 {
2107     GzFileReader logFile;
2108 
2109     // Go though all log files and expose host logs.
2110     for (const std::filesystem::path& it : hostLoggerFiles)
2111     {
2112         if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
2113         {
2114             BMCWEB_LOG_ERROR << "fail to expose host logs";
2115             return false;
2116         }
2117     }
2118     // Get lastMessage from constructor by getter
2119     std::string lastMessage = logFile.getLastMessage();
2120     if (!lastMessage.empty())
2121     {
2122         logCount++;
2123         if (logCount > skip && logCount <= (skip + top))
2124         {
2125             logEntries.push_back(lastMessage);
2126         }
2127     }
2128     return true;
2129 }
2130 
2131 inline void fillHostLoggerEntryJson(const std::string& logEntryID,
2132                                     const std::string& msg,
2133                                     nlohmann::json::object_t& logEntryJson)
2134 {
2135     // Fill in the log entry with the gathered data.
2136     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
2137     logEntryJson["@odata.id"] = boost::urls::format(
2138         "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/{}",
2139         logEntryID);
2140     logEntryJson["Name"] = "Host Logger Entry";
2141     logEntryJson["Id"] = logEntryID;
2142     logEntryJson["Message"] = msg;
2143     logEntryJson["EntryType"] = "Oem";
2144     logEntryJson["Severity"] = "OK";
2145     logEntryJson["OemRecordFormat"] = "Host Logger Entry";
2146 }
2147 
2148 inline void requestRoutesSystemHostLogger(App& app)
2149 {
2150     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/")
2151         .privileges(redfish::privileges::getLogService)
2152         .methods(boost::beast::http::verb::get)(
2153             [&app](const crow::Request& req,
2154                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2155                    const std::string& systemName) {
2156         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2157         {
2158             return;
2159         }
2160         if constexpr (bmcwebEnableMultiHost)
2161         {
2162             // Option currently returns no systems.  TBD
2163             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2164                                        systemName);
2165             return;
2166         }
2167         if (systemName != "system")
2168         {
2169             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2170                                        systemName);
2171             return;
2172         }
2173         asyncResp->res.jsonValue["@odata.id"] =
2174             "/redfish/v1/Systems/system/LogServices/HostLogger";
2175         asyncResp->res.jsonValue["@odata.type"] =
2176             "#LogService.v1_1_0.LogService";
2177         asyncResp->res.jsonValue["Name"] = "Host Logger Service";
2178         asyncResp->res.jsonValue["Description"] = "Host Logger Service";
2179         asyncResp->res.jsonValue["Id"] = "HostLogger";
2180         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2181             "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
2182         });
2183 }
2184 
2185 inline void requestRoutesSystemHostLoggerCollection(App& app)
2186 {
2187     BMCWEB_ROUTE(app,
2188                  "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/")
2189         .privileges(redfish::privileges::getLogEntry)
2190         .methods(boost::beast::http::verb::get)(
2191             [&app](const crow::Request& req,
2192                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2193                    const std::string& systemName) {
2194         query_param::QueryCapabilities capabilities = {
2195             .canDelegateTop = true,
2196             .canDelegateSkip = true,
2197         };
2198         query_param::Query delegatedQuery;
2199         if (!redfish::setUpRedfishRouteWithDelegation(
2200                 app, req, asyncResp, delegatedQuery, capabilities))
2201         {
2202             return;
2203         }
2204         if constexpr (bmcwebEnableMultiHost)
2205         {
2206             // Option currently returns no systems.  TBD
2207             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2208                                        systemName);
2209             return;
2210         }
2211         if (systemName != "system")
2212         {
2213             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2214                                        systemName);
2215             return;
2216         }
2217         asyncResp->res.jsonValue["@odata.id"] =
2218             "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
2219         asyncResp->res.jsonValue["@odata.type"] =
2220             "#LogEntryCollection.LogEntryCollection";
2221         asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
2222         asyncResp->res.jsonValue["Description"] =
2223             "Collection of HostLogger Entries";
2224         nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2225         logEntryArray = nlohmann::json::array();
2226         asyncResp->res.jsonValue["Members@odata.count"] = 0;
2227 
2228         std::vector<std::filesystem::path> hostLoggerFiles;
2229         if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2230         {
2231             BMCWEB_LOG_ERROR << "fail to get host log file path";
2232             return;
2233         }
2234         // If we weren't provided top and skip limits, use the defaults.
2235         size_t skip = delegatedQuery.skip.value_or(0);
2236         size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
2237         size_t logCount = 0;
2238         // This vector only store the entries we want to expose that
2239         // control by skip and top.
2240         std::vector<std::string> logEntries;
2241         if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
2242                                   logCount))
2243         {
2244             messages::internalError(asyncResp->res);
2245             return;
2246         }
2247         // If vector is empty, that means skip value larger than total
2248         // log count
2249         if (logEntries.empty())
2250         {
2251             asyncResp->res.jsonValue["Members@odata.count"] = logCount;
2252             return;
2253         }
2254         if (!logEntries.empty())
2255         {
2256             for (size_t i = 0; i < logEntries.size(); i++)
2257             {
2258                 nlohmann::json::object_t hostLogEntry;
2259                 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
2260                                         hostLogEntry);
2261                 logEntryArray.emplace_back(std::move(hostLogEntry));
2262             }
2263 
2264             asyncResp->res.jsonValue["Members@odata.count"] = logCount;
2265             if (skip + top < logCount)
2266             {
2267                 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2268                     "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
2269                     std::to_string(skip + top);
2270             }
2271         }
2272         });
2273 }
2274 
2275 inline void requestRoutesSystemHostLoggerLogEntry(App& app)
2276 {
2277     BMCWEB_ROUTE(
2278         app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/<str>/")
2279         .privileges(redfish::privileges::getLogEntry)
2280         .methods(boost::beast::http::verb::get)(
2281             [&app](const crow::Request& req,
2282                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2283                    const std::string& systemName, const std::string& param) {
2284         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2285         {
2286             return;
2287         }
2288         if constexpr (bmcwebEnableMultiHost)
2289         {
2290             // Option currently returns no systems.  TBD
2291             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2292                                        systemName);
2293             return;
2294         }
2295         if (systemName != "system")
2296         {
2297             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2298                                        systemName);
2299             return;
2300         }
2301         const std::string& targetID = param;
2302 
2303         uint64_t idInt = 0;
2304 
2305         auto [ptr, ec] = std::from_chars(&*targetID.begin(), &*targetID.end(),
2306                                          idInt);
2307         if (ec == std::errc::invalid_argument ||
2308             ec == std::errc::result_out_of_range)
2309         {
2310             messages::resourceNotFound(asyncResp->res, "LogEntry", param);
2311             return;
2312         }
2313 
2314         std::vector<std::filesystem::path> hostLoggerFiles;
2315         if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2316         {
2317             BMCWEB_LOG_ERROR << "fail to get host log file path";
2318             return;
2319         }
2320 
2321         size_t logCount = 0;
2322         size_t top = 1;
2323         std::vector<std::string> logEntries;
2324         // We can get specific entry by skip and top. For example, if we
2325         // want to get nth entry, we can set skip = n-1 and top = 1 to
2326         // get that entry
2327         if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
2328                                   logCount))
2329         {
2330             messages::internalError(asyncResp->res);
2331             return;
2332         }
2333 
2334         if (!logEntries.empty())
2335         {
2336             nlohmann::json::object_t hostLogEntry;
2337             fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
2338             asyncResp->res.jsonValue.update(hostLogEntry);
2339             return;
2340         }
2341 
2342         // Requested ID was not found
2343         messages::resourceNotFound(asyncResp->res, "LogEntry", param);
2344         });
2345 }
2346 
2347 inline void handleBMCLogServicesCollectionGet(
2348     crow::App& app, const crow::Request& req,
2349     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2350 {
2351     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2352     {
2353         return;
2354     }
2355     // Collections don't include the static data added by SubRoute
2356     // because it has a duplicate entry for members
2357     asyncResp->res.jsonValue["@odata.type"] =
2358         "#LogServiceCollection.LogServiceCollection";
2359     asyncResp->res.jsonValue["@odata.id"] =
2360         "/redfish/v1/Managers/bmc/LogServices";
2361     asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2362     asyncResp->res.jsonValue["Description"] =
2363         "Collection of LogServices for this Manager";
2364     nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2365     logServiceArray = nlohmann::json::array();
2366 
2367 #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
2368     nlohmann::json::object_t journal;
2369     journal["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/Journal";
2370     logServiceArray.emplace_back(std::move(journal));
2371 #endif
2372 
2373     asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2374 
2375 #ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2376     constexpr std::array<std::string_view, 1> interfaces = {
2377         "xyz.openbmc_project.Collection.DeleteAll"};
2378     dbus::utility::getSubTreePaths(
2379         "/xyz/openbmc_project/dump", 0, interfaces,
2380         [asyncResp](
2381             const boost::system::error_code& ec,
2382             const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2383         if (ec)
2384         {
2385             BMCWEB_LOG_ERROR
2386                 << "handleBMCLogServicesCollectionGet respHandler got error "
2387                 << ec;
2388             // Assume that getting an error simply means there are no dump
2389             // LogServices. Return without adding any error response.
2390             return;
2391         }
2392 
2393         nlohmann::json& logServiceArrayLocal =
2394             asyncResp->res.jsonValue["Members"];
2395 
2396         for (const std::string& path : subTreePaths)
2397         {
2398             if (path == "/xyz/openbmc_project/dump/bmc")
2399             {
2400                 nlohmann::json::object_t member;
2401                 member["@odata.id"] =
2402                     "/redfish/v1/Managers/bmc/LogServices/Dump";
2403                 logServiceArrayLocal.emplace_back(std::move(member));
2404             }
2405             else if (path == "/xyz/openbmc_project/dump/faultlog")
2406             {
2407                 nlohmann::json::object_t member;
2408                 member["@odata.id"] =
2409                     "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2410                 logServiceArrayLocal.emplace_back(std::move(member));
2411             }
2412         }
2413 
2414         asyncResp->res.jsonValue["Members@odata.count"] =
2415             logServiceArrayLocal.size();
2416         });
2417 #endif
2418 }
2419 
2420 inline void requestRoutesBMCLogServiceCollection(App& app)
2421 {
2422     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
2423         .privileges(redfish::privileges::getLogServiceCollection)
2424         .methods(boost::beast::http::verb::get)(
2425             std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app)));
2426 }
2427 
2428 inline void requestRoutesBMCJournalLogService(App& app)
2429 {
2430     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
2431         .privileges(redfish::privileges::getLogService)
2432         .methods(boost::beast::http::verb::get)(
2433             [&app](const crow::Request& req,
2434                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2435         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2436         {
2437             return;
2438         }
2439         asyncResp->res.jsonValue["@odata.type"] =
2440             "#LogService.v1_1_0.LogService";
2441         asyncResp->res.jsonValue["@odata.id"] =
2442             "/redfish/v1/Managers/bmc/LogServices/Journal";
2443         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2444         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2445         asyncResp->res.jsonValue["Id"] = "Journal";
2446         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2447 
2448         std::pair<std::string, std::string> redfishDateTimeOffset =
2449             redfish::time_utils::getDateTimeOffsetNow();
2450         asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2451         asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2452             redfishDateTimeOffset.second;
2453 
2454         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2455             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2456         });
2457 }
2458 
2459 static int
2460     fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2461                                sd_journal* journal,
2462                                nlohmann::json::object_t& bmcJournalLogEntryJson)
2463 {
2464     // Get the Log Entry contents
2465     int ret = 0;
2466 
2467     std::string message;
2468     std::string_view syslogID;
2469     ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2470     if (ret < 0)
2471     {
2472         BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2473                          << strerror(-ret);
2474     }
2475     if (!syslogID.empty())
2476     {
2477         message += std::string(syslogID) + ": ";
2478     }
2479 
2480     std::string_view msg;
2481     ret = getJournalMetadata(journal, "MESSAGE", msg);
2482     if (ret < 0)
2483     {
2484         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2485         return 1;
2486     }
2487     message += std::string(msg);
2488 
2489     // Get the severity from the PRIORITY field
2490     long int severity = 8; // Default to an invalid priority
2491     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
2492     if (ret < 0)
2493     {
2494         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
2495     }
2496 
2497     // Get the Created time from the timestamp
2498     std::string entryTimeStr;
2499     if (!getEntryTimestamp(journal, entryTimeStr))
2500     {
2501         return 1;
2502     }
2503 
2504     // Fill in the log entry with the gathered data
2505     bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
2506     bmcJournalLogEntryJson["@odata.id"] = boost::urls::format(
2507         "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/{}",
2508         bmcJournalLogEntryID);
2509     bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2510     bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2511     bmcJournalLogEntryJson["Message"] = std::move(message);
2512     bmcJournalLogEntryJson["EntryType"] = "Oem";
2513     bmcJournalLogEntryJson["Severity"] = severity <= 2   ? "Critical"
2514                                          : severity <= 4 ? "Warning"
2515                                                          : "OK";
2516     bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2517     bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
2518     return 0;
2519 }
2520 
2521 inline void requestRoutesBMCJournalLogEntryCollection(App& app)
2522 {
2523     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
2524         .privileges(redfish::privileges::getLogEntryCollection)
2525         .methods(boost::beast::http::verb::get)(
2526             [&app](const crow::Request& req,
2527                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2528         query_param::QueryCapabilities capabilities = {
2529             .canDelegateTop = true,
2530             .canDelegateSkip = true,
2531         };
2532         query_param::Query delegatedQuery;
2533         if (!redfish::setUpRedfishRouteWithDelegation(
2534                 app, req, asyncResp, delegatedQuery, capabilities))
2535         {
2536             return;
2537         }
2538 
2539         size_t skip = delegatedQuery.skip.value_or(0);
2540         size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
2541 
2542         // Collections don't include the static data added by SubRoute
2543         // because it has a duplicate entry for members
2544         asyncResp->res.jsonValue["@odata.type"] =
2545             "#LogEntryCollection.LogEntryCollection";
2546         asyncResp->res.jsonValue["@odata.id"] =
2547             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2548         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2549         asyncResp->res.jsonValue["Description"] =
2550             "Collection of BMC Journal Entries";
2551         nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2552         logEntryArray = nlohmann::json::array();
2553 
2554         // Go through the journal and use the timestamp to create a
2555         // unique ID for each entry
2556         sd_journal* journalTmp = nullptr;
2557         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2558         if (ret < 0)
2559         {
2560             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2561             messages::internalError(asyncResp->res);
2562             return;
2563         }
2564         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2565             journalTmp, sd_journal_close);
2566         journalTmp = nullptr;
2567         uint64_t entryCount = 0;
2568         // Reset the unique ID on the first entry
2569         bool firstEntry = true;
2570         SD_JOURNAL_FOREACH(journal.get())
2571         {
2572             entryCount++;
2573             // Handle paging using skip (number of entries to skip from
2574             // the start) and top (number of entries to display)
2575             if (entryCount <= skip || entryCount > skip + top)
2576             {
2577                 continue;
2578             }
2579 
2580             std::string idStr;
2581             if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2582             {
2583                 continue;
2584             }
2585             firstEntry = false;
2586 
2587             nlohmann::json::object_t bmcJournalLogEntry;
2588             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2589                                            bmcJournalLogEntry) != 0)
2590             {
2591                 messages::internalError(asyncResp->res);
2592                 return;
2593             }
2594             logEntryArray.emplace_back(std::move(bmcJournalLogEntry));
2595         }
2596         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2597         if (skip + top < entryCount)
2598         {
2599             asyncResp->res.jsonValue["Members@odata.nextLink"] =
2600                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2601                 std::to_string(skip + top);
2602         }
2603         });
2604 }
2605 
2606 inline void requestRoutesBMCJournalLogEntry(App& app)
2607 {
2608     BMCWEB_ROUTE(app,
2609                  "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
2610         .privileges(redfish::privileges::getLogEntry)
2611         .methods(boost::beast::http::verb::get)(
2612             [&app](const crow::Request& req,
2613                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2614                    const std::string& entryID) {
2615         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2616         {
2617             return;
2618         }
2619         // Convert the unique ID back to a timestamp to find the entry
2620         uint64_t ts = 0;
2621         uint64_t index = 0;
2622         if (!getTimestampFromID(asyncResp, entryID, ts, index))
2623         {
2624             return;
2625         }
2626 
2627         sd_journal* journalTmp = nullptr;
2628         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2629         if (ret < 0)
2630         {
2631             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2632             messages::internalError(asyncResp->res);
2633             return;
2634         }
2635         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2636             journalTmp, sd_journal_close);
2637         journalTmp = nullptr;
2638         // Go to the timestamp in the log and move to the entry at the
2639         // index tracking the unique ID
2640         std::string idStr;
2641         bool firstEntry = true;
2642         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2643         if (ret < 0)
2644         {
2645             BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2646                              << strerror(-ret);
2647             messages::internalError(asyncResp->res);
2648             return;
2649         }
2650         for (uint64_t i = 0; i <= index; i++)
2651         {
2652             sd_journal_next(journal.get());
2653             if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2654             {
2655                 messages::internalError(asyncResp->res);
2656                 return;
2657             }
2658             firstEntry = false;
2659         }
2660         // Confirm that the entry ID matches what was requested
2661         if (idStr != entryID)
2662         {
2663             messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
2664             return;
2665         }
2666 
2667         nlohmann::json::object_t bmcJournalLogEntry;
2668         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2669                                        bmcJournalLogEntry) != 0)
2670         {
2671             messages::internalError(asyncResp->res);
2672             return;
2673         }
2674         asyncResp->res.jsonValue.update(bmcJournalLogEntry);
2675         });
2676 }
2677 
2678 inline void
2679     getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2680                        const std::string& dumpType)
2681 {
2682     std::string dumpPath;
2683     std::string overWritePolicy;
2684     bool collectDiagnosticDataSupported = false;
2685 
2686     if (dumpType == "BMC")
2687     {
2688         dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2689         overWritePolicy = "WrapsWhenFull";
2690         collectDiagnosticDataSupported = true;
2691     }
2692     else if (dumpType == "FaultLog")
2693     {
2694         dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2695         overWritePolicy = "Unknown";
2696         collectDiagnosticDataSupported = false;
2697     }
2698     else if (dumpType == "System")
2699     {
2700         dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2701         overWritePolicy = "WrapsWhenFull";
2702         collectDiagnosticDataSupported = true;
2703     }
2704     else
2705     {
2706         BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2707                          << dumpType;
2708         messages::internalError(asyncResp->res);
2709         return;
2710     }
2711 
2712     asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2713     asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2714     asyncResp->res.jsonValue["Name"] = "Dump LogService";
2715     asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2716     asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2717     asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2718 
2719     std::pair<std::string, std::string> redfishDateTimeOffset =
2720         redfish::time_utils::getDateTimeOffsetNow();
2721     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2722     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2723         redfishDateTimeOffset.second;
2724 
2725     asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2726 
2727     if (collectDiagnosticDataSupported)
2728     {
2729         asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2730                                 ["target"] =
2731             dumpPath + "/Actions/LogService.CollectDiagnosticData";
2732     }
2733 
2734     constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface};
2735     dbus::utility::getSubTreePaths(
2736         "/xyz/openbmc_project/dump", 0, interfaces,
2737         [asyncResp, dumpType, dumpPath](
2738             const boost::system::error_code& ec,
2739             const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2740         if (ec)
2741         {
2742             BMCWEB_LOG_ERROR << "getDumpServiceInfo respHandler got error "
2743                              << ec;
2744             // Assume that getting an error simply means there are no dump
2745             // LogServices. Return without adding any error response.
2746             return;
2747         }
2748 
2749         const std::string dbusDumpPath =
2750             "/xyz/openbmc_project/dump/" +
2751             boost::algorithm::to_lower_copy(dumpType);
2752 
2753         for (const std::string& path : subTreePaths)
2754         {
2755             if (path == dbusDumpPath)
2756             {
2757                 asyncResp->res
2758                     .jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2759                     dumpPath + "/Actions/LogService.ClearLog";
2760                 break;
2761             }
2762         }
2763         });
2764 }
2765 
2766 inline void handleLogServicesDumpServiceGet(
2767     crow::App& app, const std::string& dumpType, const crow::Request& req,
2768     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2769 {
2770     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2771     {
2772         return;
2773     }
2774     getDumpServiceInfo(asyncResp, dumpType);
2775 }
2776 
2777 inline void handleLogServicesDumpServiceComputerSystemGet(
2778     crow::App& app, const crow::Request& req,
2779     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2780     const std::string& chassisId)
2781 {
2782     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2783     {
2784         return;
2785     }
2786     if (chassisId != "system")
2787     {
2788         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2789         return;
2790     }
2791     getDumpServiceInfo(asyncResp, "System");
2792 }
2793 
2794 inline void handleLogServicesDumpEntriesCollectionGet(
2795     crow::App& app, const std::string& dumpType, const crow::Request& req,
2796     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2797 {
2798     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2799     {
2800         return;
2801     }
2802     getDumpEntryCollection(asyncResp, dumpType);
2803 }
2804 
2805 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet(
2806     crow::App& app, const crow::Request& req,
2807     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2808     const std::string& chassisId)
2809 {
2810     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2811     {
2812         return;
2813     }
2814     if (chassisId != "system")
2815     {
2816         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2817         return;
2818     }
2819     getDumpEntryCollection(asyncResp, "System");
2820 }
2821 
2822 inline void handleLogServicesDumpEntryGet(
2823     crow::App& app, const std::string& dumpType, const crow::Request& req,
2824     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2825     const std::string& dumpId)
2826 {
2827     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2828     {
2829         return;
2830     }
2831     getDumpEntryById(asyncResp, dumpId, dumpType);
2832 }
2833 inline void handleLogServicesDumpEntryComputerSystemGet(
2834     crow::App& app, const crow::Request& req,
2835     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2836     const std::string& chassisId, const std::string& dumpId)
2837 {
2838     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2839     {
2840         return;
2841     }
2842     if (chassisId != "system")
2843     {
2844         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2845         return;
2846     }
2847     getDumpEntryById(asyncResp, dumpId, "System");
2848 }
2849 
2850 inline void handleLogServicesDumpEntryDelete(
2851     crow::App& app, const std::string& dumpType, const crow::Request& req,
2852     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2853     const std::string& dumpId)
2854 {
2855     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2856     {
2857         return;
2858     }
2859     deleteDumpEntry(asyncResp, dumpId, dumpType);
2860 }
2861 
2862 inline void handleLogServicesDumpEntryComputerSystemDelete(
2863     crow::App& app, const crow::Request& req,
2864     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2865     const std::string& chassisId, const std::string& dumpId)
2866 {
2867     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2868     {
2869         return;
2870     }
2871     if (chassisId != "system")
2872     {
2873         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2874         return;
2875     }
2876     deleteDumpEntry(asyncResp, dumpId, "System");
2877 }
2878 
2879 inline void handleLogServicesDumpCollectDiagnosticDataPost(
2880     crow::App& app, const std::string& dumpType, const crow::Request& req,
2881     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2882 {
2883     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2884     {
2885         return;
2886     }
2887     createDump(asyncResp, req, dumpType);
2888 }
2889 
2890 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(
2891     crow::App& app, const crow::Request& req,
2892     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2893     const std::string& systemName)
2894 {
2895     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2896     {
2897         return;
2898     }
2899 
2900     if constexpr (bmcwebEnableMultiHost)
2901     {
2902         // Option currently returns no systems.  TBD
2903         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2904                                    systemName);
2905         return;
2906     }
2907     if (systemName != "system")
2908     {
2909         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2910                                    systemName);
2911         return;
2912     }
2913     createDump(asyncResp, req, "System");
2914 }
2915 
2916 inline void handleLogServicesDumpClearLogPost(
2917     crow::App& app, const std::string& dumpType, const crow::Request& req,
2918     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2919 {
2920     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2921     {
2922         return;
2923     }
2924     clearDump(asyncResp, dumpType);
2925 }
2926 
2927 inline void handleLogServicesDumpClearLogComputerSystemPost(
2928     crow::App& app, const crow::Request& req,
2929     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2930     const std::string& systemName)
2931 {
2932     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2933     {
2934         return;
2935     }
2936     if constexpr (bmcwebEnableMultiHost)
2937     {
2938         // Option currently returns no systems.  TBD
2939         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2940                                    systemName);
2941         return;
2942     }
2943     if (systemName != "system")
2944     {
2945         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2946                                    systemName);
2947         return;
2948     }
2949     clearDump(asyncResp, "System");
2950 }
2951 
2952 inline void requestRoutesBMCDumpService(App& app)
2953 {
2954     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
2955         .privileges(redfish::privileges::getLogService)
2956         .methods(boost::beast::http::verb::get)(std::bind_front(
2957             handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
2958 }
2959 
2960 inline void requestRoutesBMCDumpEntryCollection(App& app)
2961 {
2962     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
2963         .privileges(redfish::privileges::getLogEntryCollection)
2964         .methods(boost::beast::http::verb::get)(std::bind_front(
2965             handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
2966 }
2967 
2968 inline void requestRoutesBMCDumpEntry(App& app)
2969 {
2970     BMCWEB_ROUTE(app,
2971                  "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
2972         .privileges(redfish::privileges::getLogEntry)
2973         .methods(boost::beast::http::verb::get)(std::bind_front(
2974             handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2975 
2976     BMCWEB_ROUTE(app,
2977                  "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
2978         .privileges(redfish::privileges::deleteLogEntry)
2979         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2980             handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
2981 }
2982 
2983 inline void requestRoutesBMCDumpCreate(App& app)
2984 {
2985     BMCWEB_ROUTE(
2986         app,
2987         "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2988         .privileges(redfish::privileges::postLogService)
2989         .methods(boost::beast::http::verb::post)(
2990             std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2991                             std::ref(app), "BMC"));
2992 }
2993 
2994 inline void requestRoutesBMCDumpClear(App& app)
2995 {
2996     BMCWEB_ROUTE(
2997         app,
2998         "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
2999         .privileges(redfish::privileges::postLogService)
3000         .methods(boost::beast::http::verb::post)(std::bind_front(
3001             handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
3002 }
3003 
3004 inline void requestRoutesFaultLogDumpService(App& app)
3005 {
3006     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
3007         .privileges(redfish::privileges::getLogService)
3008         .methods(boost::beast::http::verb::get)(std::bind_front(
3009             handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
3010 }
3011 
3012 inline void requestRoutesFaultLogDumpEntryCollection(App& app)
3013 {
3014     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
3015         .privileges(redfish::privileges::getLogEntryCollection)
3016         .methods(boost::beast::http::verb::get)(
3017             std::bind_front(handleLogServicesDumpEntriesCollectionGet,
3018                             std::ref(app), "FaultLog"));
3019 }
3020 
3021 inline void requestRoutesFaultLogDumpEntry(App& app)
3022 {
3023     BMCWEB_ROUTE(app,
3024                  "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
3025         .privileges(redfish::privileges::getLogEntry)
3026         .methods(boost::beast::http::verb::get)(std::bind_front(
3027             handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
3028 
3029     BMCWEB_ROUTE(app,
3030                  "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
3031         .privileges(redfish::privileges::deleteLogEntry)
3032         .methods(boost::beast::http::verb::delete_)(std::bind_front(
3033             handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
3034 }
3035 
3036 inline void requestRoutesFaultLogDumpClear(App& app)
3037 {
3038     BMCWEB_ROUTE(
3039         app,
3040         "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
3041         .privileges(redfish::privileges::postLogService)
3042         .methods(boost::beast::http::verb::post)(std::bind_front(
3043             handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
3044 }
3045 
3046 inline void requestRoutesSystemDumpService(App& app)
3047 {
3048     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/")
3049         .privileges(redfish::privileges::getLogService)
3050         .methods(boost::beast::http::verb::get)(std::bind_front(
3051             handleLogServicesDumpServiceComputerSystemGet, std::ref(app)));
3052 }
3053 
3054 inline void requestRoutesSystemDumpEntryCollection(App& app)
3055 {
3056     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/")
3057         .privileges(redfish::privileges::getLogEntryCollection)
3058         .methods(boost::beast::http::verb::get)(std::bind_front(
3059             handleLogServicesDumpEntriesCollectionComputerSystemGet,
3060             std::ref(app)));
3061 }
3062 
3063 inline void requestRoutesSystemDumpEntry(App& app)
3064 {
3065     BMCWEB_ROUTE(app,
3066                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
3067         .privileges(redfish::privileges::getLogEntry)
3068         .methods(boost::beast::http::verb::get)(std::bind_front(
3069             handleLogServicesDumpEntryComputerSystemGet, std::ref(app)));
3070 
3071     BMCWEB_ROUTE(app,
3072                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
3073         .privileges(redfish::privileges::deleteLogEntry)
3074         .methods(boost::beast::http::verb::delete_)(std::bind_front(
3075             handleLogServicesDumpEntryComputerSystemDelete, std::ref(app)));
3076 }
3077 
3078 inline void requestRoutesSystemDumpCreate(App& app)
3079 {
3080     BMCWEB_ROUTE(
3081         app,
3082         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
3083         .privileges(redfish::privileges::postLogService)
3084         .methods(boost::beast::http::verb::post)(std::bind_front(
3085             handleLogServicesDumpCollectDiagnosticDataComputerSystemPost,
3086             std::ref(app)));
3087 }
3088 
3089 inline void requestRoutesSystemDumpClear(App& app)
3090 {
3091     BMCWEB_ROUTE(
3092         app,
3093         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
3094         .privileges(redfish::privileges::postLogService)
3095         .methods(boost::beast::http::verb::post)(std::bind_front(
3096             handleLogServicesDumpClearLogComputerSystemPost, std::ref(app)));
3097 }
3098 
3099 inline void requestRoutesCrashdumpService(App& app)
3100 {
3101     // Note: Deviated from redfish privilege registry for GET & HEAD
3102     // method for security reasons.
3103     /**
3104      * Functions triggers appropriate requests on DBus
3105      */
3106     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/")
3107         // This is incorrect, should be:
3108         //.privileges(redfish::privileges::getLogService)
3109         .privileges({{"ConfigureManager"}})
3110         .methods(boost::beast::http::verb::get)(
3111             [&app](const crow::Request& req,
3112                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3113                    const std::string& systemName) {
3114         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3115         {
3116             return;
3117         }
3118         if constexpr (bmcwebEnableMultiHost)
3119         {
3120             // Option currently returns no systems.  TBD
3121             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3122                                        systemName);
3123             return;
3124         }
3125         if (systemName != "system")
3126         {
3127             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3128                                        systemName);
3129             return;
3130         }
3131 
3132         // Copy over the static data to include the entries added by
3133         // SubRoute
3134         asyncResp->res.jsonValue["@odata.id"] =
3135             "/redfish/v1/Systems/system/LogServices/Crashdump";
3136         asyncResp->res.jsonValue["@odata.type"] =
3137             "#LogService.v1_2_0.LogService";
3138         asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
3139         asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
3140         asyncResp->res.jsonValue["Id"] = "Crashdump";
3141         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3142         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
3143 
3144         std::pair<std::string, std::string> redfishDateTimeOffset =
3145             redfish::time_utils::getDateTimeOffsetNow();
3146         asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3147         asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3148             redfishDateTimeOffset.second;
3149 
3150         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3151             "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
3152         asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
3153             "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
3154         asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
3155                                 ["target"] =
3156             "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
3157         });
3158 }
3159 
3160 void inline requestRoutesCrashdumpClear(App& app)
3161 {
3162     BMCWEB_ROUTE(
3163         app,
3164         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/")
3165         // This is incorrect, should be:
3166         //.privileges(redfish::privileges::postLogService)
3167         .privileges({{"ConfigureComponents"}})
3168         .methods(boost::beast::http::verb::post)(
3169             [&app](const crow::Request& req,
3170                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3171                    const std::string& systemName) {
3172         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3173         {
3174             return;
3175         }
3176         if constexpr (bmcwebEnableMultiHost)
3177         {
3178             // Option currently returns no systems.  TBD
3179             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3180                                        systemName);
3181             return;
3182         }
3183         if (systemName != "system")
3184         {
3185             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3186                                        systemName);
3187             return;
3188         }
3189         crow::connections::systemBus->async_method_call(
3190             [asyncResp](const boost::system::error_code& ec,
3191                         const std::string&) {
3192             if (ec)
3193             {
3194                 messages::internalError(asyncResp->res);
3195                 return;
3196             }
3197             messages::success(asyncResp->res);
3198             },
3199             crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
3200         });
3201 }
3202 
3203 static void
3204     logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3205                       const std::string& logID, nlohmann::json& logEntryJson)
3206 {
3207     auto getStoredLogCallback =
3208         [asyncResp, logID,
3209          &logEntryJson](const boost::system::error_code& ec,
3210                         const dbus::utility::DBusPropertiesMap& params) {
3211         if (ec)
3212         {
3213             BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
3214             if (ec.value() ==
3215                 boost::system::linux_error::bad_request_descriptor)
3216             {
3217                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3218             }
3219             else
3220             {
3221                 messages::internalError(asyncResp->res);
3222             }
3223             return;
3224         }
3225 
3226         std::string timestamp{};
3227         std::string filename{};
3228         std::string logfile{};
3229         parseCrashdumpParameters(params, filename, timestamp, logfile);
3230 
3231         if (filename.empty() || timestamp.empty())
3232         {
3233             messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3234             return;
3235         }
3236 
3237         std::string crashdumpURI =
3238             "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
3239             logID + "/" + filename;
3240         nlohmann::json::object_t logEntry;
3241         logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
3242         logEntry["@odata.id"] = boost::urls::format(
3243             "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/{}",
3244             logID);
3245         logEntry["Name"] = "CPU Crashdump";
3246         logEntry["Id"] = logID;
3247         logEntry["EntryType"] = "Oem";
3248         logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
3249         logEntry["DiagnosticDataType"] = "OEM";
3250         logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
3251         logEntry["Created"] = std::move(timestamp);
3252 
3253         // If logEntryJson references an array of LogEntry resources
3254         // ('Members' list), then push this as a new entry, otherwise set it
3255         // directly
3256         if (logEntryJson.is_array())
3257         {
3258             logEntryJson.push_back(logEntry);
3259             asyncResp->res.jsonValue["Members@odata.count"] =
3260                 logEntryJson.size();
3261         }
3262         else
3263         {
3264             logEntryJson.update(logEntry);
3265         }
3266     };
3267     sdbusplus::asio::getAllProperties(
3268         *crow::connections::systemBus, crashdumpObject,
3269         crashdumpPath + std::string("/") + logID, crashdumpInterface,
3270         std::move(getStoredLogCallback));
3271 }
3272 
3273 inline void requestRoutesCrashdumpEntryCollection(App& app)
3274 {
3275     // Note: Deviated from redfish privilege registry for GET & HEAD
3276     // method for security reasons.
3277     /**
3278      * Functions triggers appropriate requests on DBus
3279      */
3280     BMCWEB_ROUTE(app,
3281                  "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/")
3282         // This is incorrect, should be.
3283         //.privileges(redfish::privileges::postLogEntryCollection)
3284         .privileges({{"ConfigureComponents"}})
3285         .methods(boost::beast::http::verb::get)(
3286             [&app](const crow::Request& req,
3287                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3288                    const std::string& systemName) {
3289         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3290         {
3291             return;
3292         }
3293         if constexpr (bmcwebEnableMultiHost)
3294         {
3295             // Option currently returns no systems.  TBD
3296             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3297                                        systemName);
3298             return;
3299         }
3300         if (systemName != "system")
3301         {
3302             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3303                                        systemName);
3304             return;
3305         }
3306 
3307         constexpr std::array<std::string_view, 1> interfaces = {
3308             crashdumpInterface};
3309         dbus::utility::getSubTreePaths(
3310             "/", 0, interfaces,
3311             [asyncResp](const boost::system::error_code& ec,
3312                         const std::vector<std::string>& resp) {
3313             if (ec)
3314             {
3315                 if (ec.value() !=
3316                     boost::system::errc::no_such_file_or_directory)
3317                 {
3318                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
3319                                      << ec.message();
3320                     messages::internalError(asyncResp->res);
3321                     return;
3322                 }
3323             }
3324             asyncResp->res.jsonValue["@odata.type"] =
3325                 "#LogEntryCollection.LogEntryCollection";
3326             asyncResp->res.jsonValue["@odata.id"] =
3327                 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
3328             asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
3329             asyncResp->res.jsonValue["Description"] =
3330                 "Collection of Crashdump Entries";
3331             asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3332             asyncResp->res.jsonValue["Members@odata.count"] = 0;
3333 
3334             for (const std::string& path : resp)
3335             {
3336                 const sdbusplus::message::object_path objPath(path);
3337                 // Get the log ID
3338                 std::string logID = objPath.filename();
3339                 if (logID.empty())
3340                 {
3341                     continue;
3342                 }
3343                 // Add the log entry to the array
3344                 logCrashdumpEntry(asyncResp, logID,
3345                                   asyncResp->res.jsonValue["Members"]);
3346             }
3347             });
3348         });
3349 }
3350 
3351 inline void requestRoutesCrashdumpEntry(App& app)
3352 {
3353     // Note: Deviated from redfish privilege registry for GET & HEAD
3354     // method for security reasons.
3355 
3356     BMCWEB_ROUTE(
3357         app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/")
3358         // this is incorrect, should be
3359         // .privileges(redfish::privileges::getLogEntry)
3360         .privileges({{"ConfigureComponents"}})
3361         .methods(boost::beast::http::verb::get)(
3362             [&app](const crow::Request& req,
3363                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3364                    const std::string& systemName, const std::string& param) {
3365         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3366         {
3367             return;
3368         }
3369         if constexpr (bmcwebEnableMultiHost)
3370         {
3371             // Option currently returns no systems.  TBD
3372             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3373                                        systemName);
3374             return;
3375         }
3376         if (systemName != "system")
3377         {
3378             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3379                                        systemName);
3380             return;
3381         }
3382         const std::string& logID = param;
3383         logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
3384         });
3385 }
3386 
3387 inline void requestRoutesCrashdumpFile(App& app)
3388 {
3389     // Note: Deviated from redfish privilege registry for GET & HEAD
3390     // method for security reasons.
3391     BMCWEB_ROUTE(
3392         app,
3393         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/")
3394         .privileges(redfish::privileges::getLogEntry)
3395         .methods(boost::beast::http::verb::get)(
3396             [](const crow::Request& req,
3397                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3398                const std::string& systemName, const std::string& logID,
3399                const std::string& fileName) {
3400         // Do not call getRedfishRoute here since the crashdump file is not a
3401         // Redfish resource.
3402 
3403         if constexpr (bmcwebEnableMultiHost)
3404         {
3405             // Option currently returns no systems.  TBD
3406             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3407                                        systemName);
3408             return;
3409         }
3410         if (systemName != "system")
3411         {
3412             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3413                                        systemName);
3414             return;
3415         }
3416 
3417         auto getStoredLogCallback =
3418             [asyncResp, logID, fileName, url(boost::urls::url(req.url()))](
3419                 const boost::system::error_code& ec,
3420                 const std::vector<
3421                     std::pair<std::string, dbus::utility::DbusVariantType>>&
3422                     resp) {
3423             if (ec)
3424             {
3425                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
3426                 messages::internalError(asyncResp->res);
3427                 return;
3428             }
3429 
3430             std::string dbusFilename{};
3431             std::string dbusTimestamp{};
3432             std::string dbusFilepath{};
3433 
3434             parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
3435                                      dbusFilepath);
3436 
3437             if (dbusFilename.empty() || dbusTimestamp.empty() ||
3438                 dbusFilepath.empty())
3439             {
3440                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3441                 return;
3442             }
3443 
3444             // Verify the file name parameter is correct
3445             if (fileName != dbusFilename)
3446             {
3447                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3448                 return;
3449             }
3450 
3451             if (!std::filesystem::exists(dbusFilepath))
3452             {
3453                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
3454                 return;
3455             }
3456             std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
3457             asyncResp->res.body() =
3458                 std::string(std::istreambuf_iterator<char>{ifs}, {});
3459 
3460             // Configure this to be a file download when accessed
3461             // from a browser
3462             asyncResp->res.addHeader(
3463                 boost::beast::http::field::content_disposition, "attachment");
3464         };
3465         sdbusplus::asio::getAllProperties(
3466             *crow::connections::systemBus, crashdumpObject,
3467             crashdumpPath + std::string("/") + logID, crashdumpInterface,
3468             std::move(getStoredLogCallback));
3469         });
3470 }
3471 
3472 enum class OEMDiagnosticType
3473 {
3474     onDemand,
3475     telemetry,
3476     invalid,
3477 };
3478 
3479 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr)
3480 {
3481     if (oemDiagStr == "OnDemand")
3482     {
3483         return OEMDiagnosticType::onDemand;
3484     }
3485     if (oemDiagStr == "Telemetry")
3486     {
3487         return OEMDiagnosticType::telemetry;
3488     }
3489 
3490     return OEMDiagnosticType::invalid;
3491 }
3492 
3493 inline void requestRoutesCrashdumpCollect(App& app)
3494 {
3495     // Note: Deviated from redfish privilege registry for GET & HEAD
3496     // method for security reasons.
3497     BMCWEB_ROUTE(
3498         app,
3499         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
3500         // The below is incorrect;  Should be ConfigureManager
3501         //.privileges(redfish::privileges::postLogService)
3502         .privileges({{"ConfigureComponents"}})
3503         .methods(boost::beast::http::verb::post)(
3504             [&app](const crow::Request& req,
3505                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3506                    const std::string& systemName) {
3507         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3508         {
3509             return;
3510         }
3511 
3512         if constexpr (bmcwebEnableMultiHost)
3513         {
3514             // Option currently returns no systems.  TBD
3515             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3516                                        systemName);
3517             return;
3518         }
3519         if (systemName != "system")
3520         {
3521             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3522                                        systemName);
3523             return;
3524         }
3525 
3526         std::string diagnosticDataType;
3527         std::string oemDiagnosticDataType;
3528         if (!redfish::json_util::readJsonAction(
3529                 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3530                 "OEMDiagnosticDataType", oemDiagnosticDataType))
3531         {
3532             return;
3533         }
3534 
3535         if (diagnosticDataType != "OEM")
3536         {
3537             BMCWEB_LOG_ERROR
3538                 << "Only OEM DiagnosticDataType supported for Crashdump";
3539             messages::actionParameterValueFormatError(
3540                 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3541                 "CollectDiagnosticData");
3542             return;
3543         }
3544 
3545         OEMDiagnosticType oemDiagType =
3546             getOEMDiagnosticType(oemDiagnosticDataType);
3547 
3548         std::string iface;
3549         std::string method;
3550         std::string taskMatchStr;
3551         if (oemDiagType == OEMDiagnosticType::onDemand)
3552         {
3553             iface = crashdumpOnDemandInterface;
3554             method = "GenerateOnDemandLog";
3555             taskMatchStr = "type='signal',"
3556                            "interface='org.freedesktop.DBus.Properties',"
3557                            "member='PropertiesChanged',"
3558                            "arg0namespace='com.intel.crashdump'";
3559         }
3560         else if (oemDiagType == OEMDiagnosticType::telemetry)
3561         {
3562             iface = crashdumpTelemetryInterface;
3563             method = "GenerateTelemetryLog";
3564             taskMatchStr = "type='signal',"
3565                            "interface='org.freedesktop.DBus.Properties',"
3566                            "member='PropertiesChanged',"
3567                            "arg0namespace='com.intel.crashdump'";
3568         }
3569         else
3570         {
3571             BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3572                              << oemDiagnosticDataType;
3573             messages::actionParameterValueFormatError(
3574                 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3575                 "CollectDiagnosticData");
3576             return;
3577         }
3578 
3579         auto collectCrashdumpCallback =
3580             [asyncResp, payload(task::Payload(req)),
3581              taskMatchStr](const boost::system::error_code& ec,
3582                            const std::string&) mutable {
3583             if (ec)
3584             {
3585                 if (ec.value() == boost::system::errc::operation_not_supported)
3586                 {
3587                     messages::resourceInStandby(asyncResp->res);
3588                 }
3589                 else if (ec.value() ==
3590                          boost::system::errc::device_or_resource_busy)
3591                 {
3592                     messages::serviceTemporarilyUnavailable(asyncResp->res,
3593                                                             "60");
3594                 }
3595                 else
3596                 {
3597                     messages::internalError(asyncResp->res);
3598                 }
3599                 return;
3600             }
3601             std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
3602                 [](const boost::system::error_code& ec2, sdbusplus::message_t&,
3603                    const std::shared_ptr<task::TaskData>& taskData) {
3604                 if (!ec2)
3605                 {
3606                     taskData->messages.emplace_back(messages::taskCompletedOK(
3607                         std::to_string(taskData->index)));
3608                     taskData->state = "Completed";
3609                 }
3610                 return task::completed;
3611                 },
3612                 taskMatchStr);
3613 
3614             task->startTimer(std::chrono::minutes(5));
3615             task->populateResp(asyncResp->res);
3616             task->payload.emplace(std::move(payload));
3617         };
3618 
3619         crow::connections::systemBus->async_method_call(
3620             std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3621             iface, method);
3622         });
3623 }
3624 
3625 /**
3626  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3627  */
3628 inline void requestRoutesDBusLogServiceActionsClear(App& app)
3629 {
3630     /**
3631      * Function handles POST method request.
3632      * The Clear Log actions does not require any parameter.The action deletes
3633      * all entries found in the Entries collection for this Log Service.
3634      */
3635 
3636     BMCWEB_ROUTE(
3637         app,
3638         "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
3639         .privileges(redfish::privileges::postLogService)
3640         .methods(boost::beast::http::verb::post)(
3641             [&app](const crow::Request& req,
3642                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3643                    const std::string& systemName) {
3644         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3645         {
3646             return;
3647         }
3648         if constexpr (bmcwebEnableMultiHost)
3649         {
3650             // Option currently returns no systems.  TBD
3651             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3652                                        systemName);
3653             return;
3654         }
3655         if (systemName != "system")
3656         {
3657             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3658                                        systemName);
3659             return;
3660         }
3661         BMCWEB_LOG_DEBUG << "Do delete all entries.";
3662 
3663         // Process response from Logging service.
3664         auto respHandler = [asyncResp](const boost::system::error_code& ec) {
3665             BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3666             if (ec)
3667             {
3668                 // TODO Handle for specific error code
3669                 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3670                 asyncResp->res.result(
3671                     boost::beast::http::status::internal_server_error);
3672                 return;
3673             }
3674 
3675             asyncResp->res.result(boost::beast::http::status::no_content);
3676         };
3677 
3678         // Make call to Logging service to request Clear Log
3679         crow::connections::systemBus->async_method_call(
3680             respHandler, "xyz.openbmc_project.Logging",
3681             "/xyz/openbmc_project/logging",
3682             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3683         });
3684 }
3685 
3686 /****************************************************
3687  * Redfish PostCode interfaces
3688  * using DBUS interface: getPostCodesTS
3689  ******************************************************/
3690 inline void requestRoutesPostCodesLogService(App& app)
3691 {
3692     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/")
3693         .privileges(redfish::privileges::getLogService)
3694         .methods(boost::beast::http::verb::get)(
3695             [&app](const crow::Request& req,
3696                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3697                    const std::string& systemName) {
3698         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3699         {
3700             return;
3701         }
3702         if constexpr (bmcwebEnableMultiHost)
3703         {
3704             // Option currently returns no systems.  TBD
3705             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3706                                        systemName);
3707             return;
3708         }
3709         if (systemName != "system")
3710         {
3711             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3712                                        systemName);
3713             return;
3714         }
3715         asyncResp->res.jsonValue["@odata.id"] =
3716             "/redfish/v1/Systems/system/LogServices/PostCodes";
3717         asyncResp->res.jsonValue["@odata.type"] =
3718             "#LogService.v1_1_0.LogService";
3719         asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3720         asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3721         asyncResp->res.jsonValue["Id"] = "PostCodes";
3722         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3723         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3724             "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3725 
3726         std::pair<std::string, std::string> redfishDateTimeOffset =
3727             redfish::time_utils::getDateTimeOffsetNow();
3728         asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3729         asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3730             redfishDateTimeOffset.second;
3731 
3732         asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3733             {"target",
3734              "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3735         });
3736 }
3737 
3738 inline void requestRoutesPostCodesClear(App& app)
3739 {
3740     BMCWEB_ROUTE(
3741         app,
3742         "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/")
3743         // The following privilege is incorrect;  It should be ConfigureManager
3744         //.privileges(redfish::privileges::postLogService)
3745         .privileges({{"ConfigureComponents"}})
3746         .methods(boost::beast::http::verb::post)(
3747             [&app](const crow::Request& req,
3748                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3749                    const std::string& systemName) {
3750         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3751         {
3752             return;
3753         }
3754         if constexpr (bmcwebEnableMultiHost)
3755         {
3756             // Option currently returns no systems.  TBD
3757             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3758                                        systemName);
3759             return;
3760         }
3761         if (systemName != "system")
3762         {
3763             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3764                                        systemName);
3765             return;
3766         }
3767         BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3768 
3769         // Make call to post-code service to request clear all
3770         crow::connections::systemBus->async_method_call(
3771             [asyncResp](const boost::system::error_code& ec) {
3772             if (ec)
3773             {
3774                 // TODO Handle for specific error code
3775                 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3776                                  << ec;
3777                 asyncResp->res.result(
3778                     boost::beast::http::status::internal_server_error);
3779                 messages::internalError(asyncResp->res);
3780                 return;
3781             }
3782             },
3783             "xyz.openbmc_project.State.Boot.PostCode0",
3784             "/xyz/openbmc_project/State/Boot/PostCode0",
3785             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3786         });
3787 }
3788 
3789 /**
3790  * @brief Parse post code ID and get the current value and index value
3791  *        eg: postCodeID=B1-2, currentValue=1, index=2
3792  *
3793  * @param[in]  postCodeID     Post Code ID
3794  * @param[out] currentValue   Current value
3795  * @param[out] index          Index value
3796  *
3797  * @return bool true if the parsing is successful, false the parsing fails
3798  */
3799 inline static bool parsePostCode(const std::string& postCodeID,
3800                                  uint64_t& currentValue, uint16_t& index)
3801 {
3802     std::vector<std::string> split;
3803     bmcweb::split(split, postCodeID, '-');
3804     if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3805     {
3806         return false;
3807     }
3808 
3809     auto start = std::next(split[0].begin());
3810     auto end = split[0].end();
3811     auto [ptrIndex, ecIndex] = std::from_chars(&*start, &*end, index);
3812 
3813     if (ptrIndex != &*end || ecIndex != std::errc())
3814     {
3815         return false;
3816     }
3817 
3818     start = split[1].begin();
3819     end = split[1].end();
3820 
3821     auto [ptrValue, ecValue] = std::from_chars(&*start, &*end, currentValue);
3822 
3823     return ptrValue == &*end && ecValue == std::errc();
3824 }
3825 
3826 static bool fillPostCodeEntry(
3827     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3828     const boost::container::flat_map<
3829         uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
3830     const uint16_t bootIndex, const uint64_t codeIndex = 0,
3831     const uint64_t skip = 0, const uint64_t top = 0)
3832 {
3833     // Get the Message from the MessageRegistry
3834     const registries::Message* message =
3835         registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
3836 
3837     uint64_t currentCodeIndex = 0;
3838     uint64_t firstCodeTimeUs = 0;
3839     for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3840              code : postcode)
3841     {
3842         currentCodeIndex++;
3843         std::string postcodeEntryID =
3844             "B" + std::to_string(bootIndex) + "-" +
3845             std::to_string(currentCodeIndex); // 1 based index in EntryID string
3846 
3847         uint64_t usecSinceEpoch = code.first;
3848         uint64_t usTimeOffset = 0;
3849 
3850         if (1 == currentCodeIndex)
3851         { // already incremented
3852             firstCodeTimeUs = code.first;
3853         }
3854         else
3855         {
3856             usTimeOffset = code.first - firstCodeTimeUs;
3857         }
3858 
3859         // skip if no specific codeIndex is specified and currentCodeIndex does
3860         // not fall between top and skip
3861         if ((codeIndex == 0) &&
3862             (currentCodeIndex <= skip || currentCodeIndex > top))
3863         {
3864             continue;
3865         }
3866 
3867         // skip if a specific codeIndex is specified and does not match the
3868         // currentIndex
3869         if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3870         {
3871             // This is done for simplicity. 1st entry is needed to calculate
3872             // time offset. To improve efficiency, one can get to the entry
3873             // directly (possibly with flatmap's nth method)
3874             continue;
3875         }
3876 
3877         // currentCodeIndex is within top and skip or equal to specified code
3878         // index
3879 
3880         // Get the Created time from the timestamp
3881         std::string entryTimeStr;
3882         entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch);
3883 
3884         // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3885         std::ostringstream hexCode;
3886         hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3887                 << std::get<0>(code.second);
3888         std::ostringstream timeOffsetStr;
3889         // Set Fixed -Point Notation
3890         timeOffsetStr << std::fixed;
3891         // Set precision to 4 digits
3892         timeOffsetStr << std::setprecision(4);
3893         // Add double to stream
3894         timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3895 
3896         std::string bootIndexStr = std::to_string(bootIndex);
3897         std::string timeOffsetString = timeOffsetStr.str();
3898         std::string hexCodeStr = hexCode.str();
3899 
3900         std::array<std::string_view, 3> messageArgs = {
3901             bootIndexStr, timeOffsetString, hexCodeStr};
3902 
3903         std::string msg =
3904             redfish::registries::fillMessageArgs(messageArgs, message->message);
3905         if (msg.empty())
3906         {
3907             messages::internalError(asyncResp->res);
3908             return false;
3909         }
3910 
3911         // Get Severity template from message registry
3912         std::string severity;
3913         if (message != nullptr)
3914         {
3915             severity = message->messageSeverity;
3916         }
3917 
3918         // Format entry
3919         nlohmann::json::object_t bmcLogEntry;
3920         bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
3921         bmcLogEntry["@odata.id"] = boost::urls::format(
3922             "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/{}",
3923             postcodeEntryID);
3924         bmcLogEntry["Name"] = "POST Code Log Entry";
3925         bmcLogEntry["Id"] = postcodeEntryID;
3926         bmcLogEntry["Message"] = std::move(msg);
3927         bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3928         bmcLogEntry["MessageArgs"] = messageArgs;
3929         bmcLogEntry["EntryType"] = "Event";
3930         bmcLogEntry["Severity"] = std::move(severity);
3931         bmcLogEntry["Created"] = entryTimeStr;
3932         if (!std::get<std::vector<uint8_t>>(code.second).empty())
3933         {
3934             bmcLogEntry["AdditionalDataURI"] =
3935                 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3936                 postcodeEntryID + "/attachment";
3937         }
3938 
3939         // codeIndex is only specified when querying single entry, return only
3940         // that entry in this case
3941         if (codeIndex != 0)
3942         {
3943             asyncResp->res.jsonValue.update(bmcLogEntry);
3944             return true;
3945         }
3946 
3947         nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
3948         logEntryArray.emplace_back(std::move(bmcLogEntry));
3949     }
3950 
3951     // Return value is always false when querying multiple entries
3952     return false;
3953 }
3954 
3955 static void
3956     getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3957                         const std::string& entryId)
3958 {
3959     uint16_t bootIndex = 0;
3960     uint64_t codeIndex = 0;
3961     if (!parsePostCode(entryId, codeIndex, bootIndex))
3962     {
3963         // Requested ID was not found
3964         messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
3965         return;
3966     }
3967 
3968     if (bootIndex == 0 || codeIndex == 0)
3969     {
3970         // 0 is an invalid index
3971         messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
3972         return;
3973     }
3974 
3975     crow::connections::systemBus->async_method_call(
3976         [asyncResp, entryId, bootIndex,
3977          codeIndex](const boost::system::error_code& ec,
3978                     const boost::container::flat_map<
3979                         uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3980                         postcode) {
3981         if (ec)
3982         {
3983             BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3984             messages::internalError(asyncResp->res);
3985             return;
3986         }
3987 
3988         if (postcode.empty())
3989         {
3990             messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
3991             return;
3992         }
3993 
3994         if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex))
3995         {
3996             messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
3997             return;
3998         }
3999         },
4000         "xyz.openbmc_project.State.Boot.PostCode0",
4001         "/xyz/openbmc_project/State/Boot/PostCode0",
4002         "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
4003         bootIndex);
4004 }
4005 
4006 static void
4007     getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4008                        const uint16_t bootIndex, const uint16_t bootCount,
4009                        const uint64_t entryCount, size_t skip, size_t top)
4010 {
4011     crow::connections::systemBus->async_method_call(
4012         [asyncResp, bootIndex, bootCount, entryCount, skip,
4013          top](const boost::system::error_code& ec,
4014               const boost::container::flat_map<
4015                   uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
4016                   postcode) {
4017         if (ec)
4018         {
4019             BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
4020             messages::internalError(asyncResp->res);
4021             return;
4022         }
4023 
4024         uint64_t endCount = entryCount;
4025         if (!postcode.empty())
4026         {
4027             endCount = entryCount + postcode.size();
4028             if (skip < endCount && (top + skip) > entryCount)
4029             {
4030                 uint64_t thisBootSkip = std::max(static_cast<uint64_t>(skip),
4031                                                  entryCount) -
4032                                         entryCount;
4033                 uint64_t thisBootTop =
4034                     std::min(static_cast<uint64_t>(top + skip), endCount) -
4035                     entryCount;
4036 
4037                 fillPostCodeEntry(asyncResp, postcode, bootIndex, 0,
4038                                   thisBootSkip, thisBootTop);
4039             }
4040             asyncResp->res.jsonValue["Members@odata.count"] = endCount;
4041         }
4042 
4043         // continue to previous bootIndex
4044         if (bootIndex < bootCount)
4045         {
4046             getPostCodeForBoot(asyncResp, static_cast<uint16_t>(bootIndex + 1),
4047                                bootCount, endCount, skip, top);
4048         }
4049         else if (skip + top < endCount)
4050         {
4051             asyncResp->res.jsonValue["Members@odata.nextLink"] =
4052                 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
4053                 std::to_string(skip + top);
4054         }
4055         },
4056         "xyz.openbmc_project.State.Boot.PostCode0",
4057         "/xyz/openbmc_project/State/Boot/PostCode0",
4058         "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
4059         bootIndex);
4060 }
4061 
4062 static void
4063     getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4064                          size_t skip, size_t top)
4065 {
4066     uint64_t entryCount = 0;
4067     sdbusplus::asio::getProperty<uint16_t>(
4068         *crow::connections::systemBus,
4069         "xyz.openbmc_project.State.Boot.PostCode0",
4070         "/xyz/openbmc_project/State/Boot/PostCode0",
4071         "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
4072         [asyncResp, entryCount, skip, top](const boost::system::error_code& ec,
4073                                            const uint16_t bootCount) {
4074         if (ec)
4075         {
4076             BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
4077             messages::internalError(asyncResp->res);
4078             return;
4079         }
4080         getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top);
4081         });
4082 }
4083 
4084 inline void requestRoutesPostCodesEntryCollection(App& app)
4085 {
4086     BMCWEB_ROUTE(app,
4087                  "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/")
4088         .privileges(redfish::privileges::getLogEntryCollection)
4089         .methods(boost::beast::http::verb::get)(
4090             [&app](const crow::Request& req,
4091                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4092                    const std::string& systemName) {
4093         query_param::QueryCapabilities capabilities = {
4094             .canDelegateTop = true,
4095             .canDelegateSkip = true,
4096         };
4097         query_param::Query delegatedQuery;
4098         if (!redfish::setUpRedfishRouteWithDelegation(
4099                 app, req, asyncResp, delegatedQuery, capabilities))
4100         {
4101             return;
4102         }
4103         if constexpr (bmcwebEnableMultiHost)
4104         {
4105             // Option currently returns no systems.  TBD
4106             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
4107                                        systemName);
4108             return;
4109         }
4110 
4111         if (systemName != "system")
4112         {
4113             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
4114                                        systemName);
4115             return;
4116         }
4117         asyncResp->res.jsonValue["@odata.type"] =
4118             "#LogEntryCollection.LogEntryCollection";
4119         asyncResp->res.jsonValue["@odata.id"] =
4120             "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
4121         asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
4122         asyncResp->res.jsonValue["Description"] =
4123             "Collection of POST Code Log Entries";
4124         asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
4125         asyncResp->res.jsonValue["Members@odata.count"] = 0;
4126         size_t skip = delegatedQuery.skip.value_or(0);
4127         size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
4128         getCurrentBootNumber(asyncResp, skip, top);
4129         });
4130 }
4131 
4132 inline void requestRoutesPostCodesEntryAdditionalData(App& app)
4133 {
4134     BMCWEB_ROUTE(
4135         app,
4136         "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/")
4137         .privileges(redfish::privileges::getLogEntry)
4138         .methods(boost::beast::http::verb::get)(
4139             [&app](const crow::Request& req,
4140                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4141                    const std::string& systemName,
4142                    const std::string& postCodeID) {
4143         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
4144         {
4145             return;
4146         }
4147         if (!http_helpers::isContentTypeAllowed(
4148                 req.getHeaderValue("Accept"),
4149                 http_helpers::ContentType::OctetStream, true))
4150         {
4151             asyncResp->res.result(boost::beast::http::status::bad_request);
4152             return;
4153         }
4154         if constexpr (bmcwebEnableMultiHost)
4155         {
4156             // Option currently returns no systems.  TBD
4157             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
4158                                        systemName);
4159             return;
4160         }
4161         if (systemName != "system")
4162         {
4163             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
4164                                        systemName);
4165             return;
4166         }
4167 
4168         uint64_t currentValue = 0;
4169         uint16_t index = 0;
4170         if (!parsePostCode(postCodeID, currentValue, index))
4171         {
4172             messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
4173             return;
4174         }
4175 
4176         crow::connections::systemBus->async_method_call(
4177             [asyncResp, postCodeID, currentValue](
4178                 const boost::system::error_code& ec,
4179                 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
4180                     postcodes) {
4181             if (ec.value() == EBADR)
4182             {
4183                 messages::resourceNotFound(asyncResp->res, "LogEntry",
4184                                            postCodeID);
4185                 return;
4186             }
4187             if (ec)
4188             {
4189                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
4190                 messages::internalError(asyncResp->res);
4191                 return;
4192             }
4193 
4194             size_t value = static_cast<size_t>(currentValue) - 1;
4195             if (value == std::string::npos || postcodes.size() < currentValue)
4196             {
4197                 BMCWEB_LOG_WARNING << "Wrong currentValue value";
4198                 messages::resourceNotFound(asyncResp->res, "LogEntry",
4199                                            postCodeID);
4200                 return;
4201             }
4202 
4203             const auto& [tID, c] = postcodes[value];
4204             if (c.empty())
4205             {
4206                 BMCWEB_LOG_WARNING << "No found post code data";
4207                 messages::resourceNotFound(asyncResp->res, "LogEntry",
4208                                            postCodeID);
4209                 return;
4210             }
4211             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
4212             const char* d = reinterpret_cast<const char*>(c.data());
4213             std::string_view strData(d, c.size());
4214 
4215             asyncResp->res.addHeader(boost::beast::http::field::content_type,
4216                                      "application/octet-stream");
4217             asyncResp->res.addHeader(
4218                 boost::beast::http::field::content_transfer_encoding, "Base64");
4219             asyncResp->res.body() = crow::utility::base64encode(strData);
4220             },
4221             "xyz.openbmc_project.State.Boot.PostCode0",
4222             "/xyz/openbmc_project/State/Boot/PostCode0",
4223             "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
4224         });
4225 }
4226 
4227 inline void requestRoutesPostCodesEntry(App& app)
4228 {
4229     BMCWEB_ROUTE(
4230         app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/")
4231         .privileges(redfish::privileges::getLogEntry)
4232         .methods(boost::beast::http::verb::get)(
4233             [&app](const crow::Request& req,
4234                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4235                    const std::string& systemName, const std::string& targetID) {
4236         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
4237         {
4238             return;
4239         }
4240         if constexpr (bmcwebEnableMultiHost)
4241         {
4242             // Option currently returns no systems.  TBD
4243             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
4244                                        systemName);
4245             return;
4246         }
4247         if (systemName != "system")
4248         {
4249             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
4250                                        systemName);
4251             return;
4252         }
4253 
4254         getPostCodeForEntry(asyncResp, targetID);
4255         });
4256 }
4257 
4258 } // namespace redfish
4259