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