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