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