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