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