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