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