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