xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 4363d3b256d469c5c133e5714f3e3be2f29b5e65)
11da66f75SEd Tanous /*
21da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation
31da66f75SEd Tanous //
41da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License");
51da66f75SEd Tanous // you may not use this file except in compliance with the License.
61da66f75SEd Tanous // You may obtain a copy of the License at
71da66f75SEd Tanous //
81da66f75SEd Tanous //      http://www.apache.org/licenses/LICENSE-2.0
91da66f75SEd Tanous //
101da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software
111da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS,
121da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131da66f75SEd Tanous // See the License for the specific language governing permissions and
141da66f75SEd Tanous // limitations under the License.
151da66f75SEd Tanous */
161da66f75SEd Tanous #pragma once
171da66f75SEd Tanous 
181da66f75SEd Tanous #include "node.hpp"
194851d45dSJason M. Bills #include "registries.hpp"
204851d45dSJason M. Bills #include "registries/base_message_registry.hpp"
214851d45dSJason M. Bills #include "registries/openbmc_message_registry.hpp"
221da66f75SEd Tanous 
23e1f26343SJason M. Bills #include <systemd/sd-journal.h>
24e1f26343SJason M. Bills 
254851d45dSJason M. Bills #include <boost/algorithm/string/split.hpp>
264851d45dSJason M. Bills #include <boost/beast/core/span.hpp>
271da66f75SEd Tanous #include <boost/container/flat_map.hpp>
281ddcf01aSJason M. Bills #include <boost/system/linux_error.hpp>
29cb92c03bSAndrew Geissler #include <error_messages.hpp>
304418c7f0SJames Feist #include <filesystem>
31cd225da8SJason M. Bills #include <string_view>
32abf2add6SEd Tanous #include <variant>
331da66f75SEd Tanous 
341da66f75SEd Tanous namespace redfish
351da66f75SEd Tanous {
361da66f75SEd Tanous 
375b61b5e8SJason M. Bills constexpr char const *crashdumpObject = "com.intel.crashdump";
385b61b5e8SJason M. Bills constexpr char const *crashdumpPath = "/com/intel/crashdump";
395b61b5e8SJason M. Bills constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
405b61b5e8SJason M. Bills constexpr char const *crashdumpInterface = "com.intel.crashdump";
415b61b5e8SJason M. Bills constexpr char const *deleteAllInterface =
425b61b5e8SJason M. Bills     "xyz.openbmc_project.Collection.DeleteAll";
435b61b5e8SJason M. Bills constexpr char const *crashdumpOnDemandInterface =
44424c4176SJason M. Bills     "com.intel.crashdump.OnDemand";
455b61b5e8SJason M. Bills constexpr char const *crashdumpRawPECIInterface =
46424c4176SJason M. Bills     "com.intel.crashdump.SendRawPeci";
471da66f75SEd Tanous 
484851d45dSJason M. Bills namespace message_registries
494851d45dSJason M. Bills {
504851d45dSJason M. Bills static const Message *getMessageFromRegistry(
514851d45dSJason M. Bills     const std::string &messageKey,
524851d45dSJason M. Bills     const boost::beast::span<const MessageEntry> registry)
534851d45dSJason M. Bills {
544851d45dSJason M. Bills     boost::beast::span<const MessageEntry>::const_iterator messageIt =
554851d45dSJason M. Bills         std::find_if(registry.cbegin(), registry.cend(),
564851d45dSJason M. Bills                      [&messageKey](const MessageEntry &messageEntry) {
574851d45dSJason M. Bills                          return !std::strcmp(messageEntry.first,
584851d45dSJason M. Bills                                              messageKey.c_str());
594851d45dSJason M. Bills                      });
604851d45dSJason M. Bills     if (messageIt != registry.cend())
614851d45dSJason M. Bills     {
624851d45dSJason M. Bills         return &messageIt->second;
634851d45dSJason M. Bills     }
644851d45dSJason M. Bills 
654851d45dSJason M. Bills     return nullptr;
664851d45dSJason M. Bills }
674851d45dSJason M. Bills 
684851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID)
694851d45dSJason M. Bills {
704851d45dSJason M. Bills     // Redfish MessageIds are in the form
714851d45dSJason M. Bills     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
724851d45dSJason M. Bills     // the right Message
734851d45dSJason M. Bills     std::vector<std::string> fields;
744851d45dSJason M. Bills     fields.reserve(4);
754851d45dSJason M. Bills     boost::split(fields, messageID, boost::is_any_of("."));
764851d45dSJason M. Bills     std::string &registryName = fields[0];
774851d45dSJason M. Bills     std::string &messageKey = fields[3];
784851d45dSJason M. Bills 
794851d45dSJason M. Bills     // Find the right registry and check it for the MessageKey
804851d45dSJason M. Bills     if (std::string(base::header.registryPrefix) == registryName)
814851d45dSJason M. Bills     {
824851d45dSJason M. Bills         return getMessageFromRegistry(
834851d45dSJason M. Bills             messageKey, boost::beast::span<const MessageEntry>(base::registry));
844851d45dSJason M. Bills     }
854851d45dSJason M. Bills     if (std::string(openbmc::header.registryPrefix) == registryName)
864851d45dSJason M. Bills     {
874851d45dSJason M. Bills         return getMessageFromRegistry(
884851d45dSJason M. Bills             messageKey,
894851d45dSJason M. Bills             boost::beast::span<const MessageEntry>(openbmc::registry));
904851d45dSJason M. Bills     }
914851d45dSJason M. Bills     return nullptr;
924851d45dSJason M. Bills }
934851d45dSJason M. Bills } // namespace message_registries
944851d45dSJason M. Bills 
95f6150403SJames Feist namespace fs = std::filesystem;
961da66f75SEd Tanous 
97cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map<
98cb92c03bSAndrew Geissler     std::string,
99cb92c03bSAndrew Geissler     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
100cb92c03bSAndrew Geissler                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
101cb92c03bSAndrew Geissler 
102cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map<
103cb92c03bSAndrew Geissler     sdbusplus::message::object_path,
104cb92c03bSAndrew Geissler     boost::container::flat_map<std::string, GetManagedPropertyType>>;
105cb92c03bSAndrew Geissler 
106cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s)
107cb92c03bSAndrew Geissler {
108cb92c03bSAndrew Geissler     if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
109cb92c03bSAndrew Geissler     {
110cb92c03bSAndrew Geissler         return "Critical";
111cb92c03bSAndrew Geissler     }
112cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
113cb92c03bSAndrew Geissler     {
114cb92c03bSAndrew Geissler         return "Critical";
115cb92c03bSAndrew Geissler     }
116cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
117cb92c03bSAndrew Geissler     {
118cb92c03bSAndrew Geissler         return "OK";
119cb92c03bSAndrew Geissler     }
120cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
121cb92c03bSAndrew Geissler     {
122cb92c03bSAndrew Geissler         return "Critical";
123cb92c03bSAndrew Geissler     }
124cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
125cb92c03bSAndrew Geissler     {
126cb92c03bSAndrew Geissler         return "Critical";
127cb92c03bSAndrew Geissler     }
128cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
129cb92c03bSAndrew Geissler     {
130cb92c03bSAndrew Geissler         return "OK";
131cb92c03bSAndrew Geissler     }
132cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
133cb92c03bSAndrew Geissler     {
134cb92c03bSAndrew Geissler         return "OK";
135cb92c03bSAndrew Geissler     }
136cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
137cb92c03bSAndrew Geissler     {
138cb92c03bSAndrew Geissler         return "Warning";
139cb92c03bSAndrew Geissler     }
140cb92c03bSAndrew Geissler     return "";
141cb92c03bSAndrew Geissler }
142cb92c03bSAndrew Geissler 
14316428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
14439e77504SEd Tanous                               const std::string_view &field,
14539e77504SEd Tanous                               std::string_view &contents)
14616428a1aSJason M. Bills {
14716428a1aSJason M. Bills     const char *data = nullptr;
14816428a1aSJason M. Bills     size_t length = 0;
14916428a1aSJason M. Bills     int ret = 0;
15016428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
151271584abSEd Tanous     ret = sd_journal_get_data(journal, field.data(),
152271584abSEd Tanous                               reinterpret_cast<const void **>(&data), &length);
15316428a1aSJason M. Bills     if (ret < 0)
15416428a1aSJason M. Bills     {
15516428a1aSJason M. Bills         return ret;
15616428a1aSJason M. Bills     }
15739e77504SEd Tanous     contents = std::string_view(data, length);
15816428a1aSJason M. Bills     // Only use the content after the "=" character.
15916428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
16016428a1aSJason M. Bills     return ret;
16116428a1aSJason M. Bills }
16216428a1aSJason M. Bills 
16316428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
16439e77504SEd Tanous                               const std::string_view &field, const int &base,
165271584abSEd Tanous                               long int &contents)
16616428a1aSJason M. Bills {
16716428a1aSJason M. Bills     int ret = 0;
16839e77504SEd Tanous     std::string_view metadata;
16916428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
17016428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
17116428a1aSJason M. Bills     if (ret < 0)
17216428a1aSJason M. Bills     {
17316428a1aSJason M. Bills         return ret;
17416428a1aSJason M. Bills     }
175b01bf299SEd Tanous     contents = strtol(metadata.data(), nullptr, base);
17616428a1aSJason M. Bills     return ret;
17716428a1aSJason M. Bills }
17816428a1aSJason M. Bills 
17916428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
18016428a1aSJason M. Bills {
18116428a1aSJason M. Bills     int ret = 0;
18216428a1aSJason M. Bills     uint64_t timestamp = 0;
18316428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
18416428a1aSJason M. Bills     if (ret < 0)
18516428a1aSJason M. Bills     {
18616428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
18716428a1aSJason M. Bills                          << strerror(-ret);
18816428a1aSJason M. Bills         return false;
18916428a1aSJason M. Bills     }
19016428a1aSJason M. Bills     time_t t =
19116428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
19216428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
19316428a1aSJason M. Bills     char entryTime[64] = {};
19499131cd0SEd Tanous     if (nullptr != loctime)
19516428a1aSJason M. Bills     {
19616428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
19716428a1aSJason M. Bills     }
19816428a1aSJason M. Bills     // Insert the ':' into the timezone
19939e77504SEd Tanous     std::string_view t1(entryTime);
20039e77504SEd Tanous     std::string_view t2(entryTime);
20116428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
20216428a1aSJason M. Bills     {
20316428a1aSJason M. Bills         t1.remove_suffix(2);
20416428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
20516428a1aSJason M. Bills     }
20639e77504SEd Tanous     entryTimestamp = std::string(t1) + ":" + std::string(t2);
20716428a1aSJason M. Bills     return true;
20816428a1aSJason M. Bills }
20916428a1aSJason M. Bills 
21016428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
211271584abSEd Tanous                          uint64_t &skip)
21216428a1aSJason M. Bills {
21316428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
21416428a1aSJason M. Bills     if (skipParam != nullptr)
21516428a1aSJason M. Bills     {
21616428a1aSJason M. Bills         char *ptr = nullptr;
217271584abSEd Tanous         skip = std::strtoul(skipParam, &ptr, 10);
21816428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
21916428a1aSJason M. Bills         {
22016428a1aSJason M. Bills 
22116428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
22216428a1aSJason M. Bills                                                    "$skip");
22316428a1aSJason M. Bills             return false;
22416428a1aSJason M. Bills         }
22516428a1aSJason M. Bills     }
22616428a1aSJason M. Bills     return true;
22716428a1aSJason M. Bills }
22816428a1aSJason M. Bills 
229271584abSEd Tanous static constexpr const uint64_t maxEntriesPerPage = 1000;
23016428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
231271584abSEd Tanous                         uint64_t &top)
23216428a1aSJason M. Bills {
23316428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
23416428a1aSJason M. Bills     if (topParam != nullptr)
23516428a1aSJason M. Bills     {
23616428a1aSJason M. Bills         char *ptr = nullptr;
237271584abSEd Tanous         top = std::strtoul(topParam, &ptr, 10);
23816428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
23916428a1aSJason M. Bills         {
24016428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
24116428a1aSJason M. Bills                                                    "$top");
24216428a1aSJason M. Bills             return false;
24316428a1aSJason M. Bills         }
244271584abSEd Tanous         if (top < 1U || top > maxEntriesPerPage)
24516428a1aSJason M. Bills         {
24616428a1aSJason M. Bills 
24716428a1aSJason M. Bills             messages::queryParameterOutOfRange(
24816428a1aSJason M. Bills                 res, std::to_string(top), "$top",
24916428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
25016428a1aSJason M. Bills             return false;
25116428a1aSJason M. Bills         }
25216428a1aSJason M. Bills     }
25316428a1aSJason M. Bills     return true;
25416428a1aSJason M. Bills }
25516428a1aSJason M. Bills 
256e85d6b16SJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
257e85d6b16SJason M. Bills                              const bool firstEntry = true)
25816428a1aSJason M. Bills {
25916428a1aSJason M. Bills     int ret = 0;
26016428a1aSJason M. Bills     static uint64_t prevTs = 0;
26116428a1aSJason M. Bills     static int index = 0;
262e85d6b16SJason M. Bills     if (firstEntry)
263e85d6b16SJason M. Bills     {
264e85d6b16SJason M. Bills         prevTs = 0;
265e85d6b16SJason M. Bills     }
266e85d6b16SJason M. Bills 
26716428a1aSJason M. Bills     // Get the entry timestamp
26816428a1aSJason M. Bills     uint64_t curTs = 0;
26916428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &curTs);
27016428a1aSJason M. Bills     if (ret < 0)
27116428a1aSJason M. Bills     {
27216428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
27316428a1aSJason M. Bills                          << strerror(-ret);
27416428a1aSJason M. Bills         return false;
27516428a1aSJason M. Bills     }
27616428a1aSJason M. Bills     // If the timestamp isn't unique, increment the index
27716428a1aSJason M. Bills     if (curTs == prevTs)
27816428a1aSJason M. Bills     {
27916428a1aSJason M. Bills         index++;
28016428a1aSJason M. Bills     }
28116428a1aSJason M. Bills     else
28216428a1aSJason M. Bills     {
28316428a1aSJason M. Bills         // Otherwise, reset it
28416428a1aSJason M. Bills         index = 0;
28516428a1aSJason M. Bills     }
28616428a1aSJason M. Bills     // Save the timestamp
28716428a1aSJason M. Bills     prevTs = curTs;
28816428a1aSJason M. Bills 
28916428a1aSJason M. Bills     entryID = std::to_string(curTs);
29016428a1aSJason M. Bills     if (index > 0)
29116428a1aSJason M. Bills     {
29216428a1aSJason M. Bills         entryID += "_" + std::to_string(index);
29316428a1aSJason M. Bills     }
29416428a1aSJason M. Bills     return true;
29516428a1aSJason M. Bills }
29616428a1aSJason M. Bills 
297e85d6b16SJason M. Bills static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
298e85d6b16SJason M. Bills                              const bool firstEntry = true)
29995820184SJason M. Bills {
300271584abSEd Tanous     static time_t prevTs = 0;
30195820184SJason M. Bills     static int index = 0;
302e85d6b16SJason M. Bills     if (firstEntry)
303e85d6b16SJason M. Bills     {
304e85d6b16SJason M. Bills         prevTs = 0;
305e85d6b16SJason M. Bills     }
306e85d6b16SJason M. Bills 
30795820184SJason M. Bills     // Get the entry timestamp
308271584abSEd Tanous     std::time_t curTs = 0;
30995820184SJason M. Bills     std::tm timeStruct = {};
31095820184SJason M. Bills     std::istringstream entryStream(logEntry);
31195820184SJason M. Bills     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
31295820184SJason M. Bills     {
31395820184SJason M. Bills         curTs = std::mktime(&timeStruct);
31495820184SJason M. Bills     }
31595820184SJason M. Bills     // If the timestamp isn't unique, increment the index
31695820184SJason M. Bills     if (curTs == prevTs)
31795820184SJason M. Bills     {
31895820184SJason M. Bills         index++;
31995820184SJason M. Bills     }
32095820184SJason M. Bills     else
32195820184SJason M. Bills     {
32295820184SJason M. Bills         // Otherwise, reset it
32395820184SJason M. Bills         index = 0;
32495820184SJason M. Bills     }
32595820184SJason M. Bills     // Save the timestamp
32695820184SJason M. Bills     prevTs = curTs;
32795820184SJason M. Bills 
32895820184SJason M. Bills     entryID = std::to_string(curTs);
32995820184SJason M. Bills     if (index > 0)
33095820184SJason M. Bills     {
33195820184SJason M. Bills         entryID += "_" + std::to_string(index);
33295820184SJason M. Bills     }
33395820184SJason M. Bills     return true;
33495820184SJason M. Bills }
33595820184SJason M. Bills 
33616428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
337271584abSEd Tanous                                uint64_t &timestamp, uint64_t &index)
33816428a1aSJason M. Bills {
33916428a1aSJason M. Bills     if (entryID.empty())
34016428a1aSJason M. Bills     {
34116428a1aSJason M. Bills         return false;
34216428a1aSJason M. Bills     }
34316428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
34439e77504SEd Tanous     std::string_view tsStr(entryID);
34516428a1aSJason M. Bills 
34616428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
34716428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
34816428a1aSJason M. Bills     {
34916428a1aSJason M. Bills         // Timestamp has an index
35016428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
35139e77504SEd Tanous         std::string_view indexStr(entryID);
35216428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
35316428a1aSJason M. Bills         std::size_t pos;
35416428a1aSJason M. Bills         try
35516428a1aSJason M. Bills         {
35639e77504SEd Tanous             index = std::stoul(std::string(indexStr), &pos);
35716428a1aSJason M. Bills         }
358271584abSEd Tanous         catch (std::invalid_argument &)
35916428a1aSJason M. Bills         {
36016428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
36116428a1aSJason M. Bills             return false;
36216428a1aSJason M. Bills         }
363271584abSEd Tanous         catch (std::out_of_range &)
36416428a1aSJason M. Bills         {
36516428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
36616428a1aSJason M. Bills             return false;
36716428a1aSJason M. Bills         }
36816428a1aSJason M. Bills         if (pos != indexStr.size())
36916428a1aSJason M. Bills         {
37016428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
37116428a1aSJason M. Bills             return false;
37216428a1aSJason M. Bills         }
37316428a1aSJason M. Bills     }
37416428a1aSJason M. Bills     // Timestamp has no index
37516428a1aSJason M. Bills     std::size_t pos;
37616428a1aSJason M. Bills     try
37716428a1aSJason M. Bills     {
37839e77504SEd Tanous         timestamp = std::stoull(std::string(tsStr), &pos);
37916428a1aSJason M. Bills     }
380271584abSEd Tanous     catch (std::invalid_argument &)
38116428a1aSJason M. Bills     {
38216428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
38316428a1aSJason M. Bills         return false;
38416428a1aSJason M. Bills     }
385271584abSEd Tanous     catch (std::out_of_range &)
38616428a1aSJason M. Bills     {
38716428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
38816428a1aSJason M. Bills         return false;
38916428a1aSJason M. Bills     }
39016428a1aSJason M. Bills     if (pos != tsStr.size())
39116428a1aSJason M. Bills     {
39216428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
39316428a1aSJason M. Bills         return false;
39416428a1aSJason M. Bills     }
39516428a1aSJason M. Bills     return true;
39616428a1aSJason M. Bills }
39716428a1aSJason M. Bills 
39895820184SJason M. Bills static bool
39995820184SJason M. Bills     getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
40095820184SJason M. Bills {
40195820184SJason M. Bills     static const std::filesystem::path redfishLogDir = "/var/log";
40295820184SJason M. Bills     static const std::string redfishLogFilename = "redfish";
40395820184SJason M. Bills 
40495820184SJason M. Bills     // Loop through the directory looking for redfish log files
40595820184SJason M. Bills     for (const std::filesystem::directory_entry &dirEnt :
40695820184SJason M. Bills          std::filesystem::directory_iterator(redfishLogDir))
40795820184SJason M. Bills     {
40895820184SJason M. Bills         // If we find a redfish log file, save the path
40995820184SJason M. Bills         std::string filename = dirEnt.path().filename();
41095820184SJason M. Bills         if (boost::starts_with(filename, redfishLogFilename))
41195820184SJason M. Bills         {
41295820184SJason M. Bills             redfishLogFiles.emplace_back(redfishLogDir / filename);
41395820184SJason M. Bills         }
41495820184SJason M. Bills     }
41595820184SJason M. Bills     // As the log files rotate, they are appended with a ".#" that is higher for
41695820184SJason M. Bills     // the older logs. Since we don't expect more than 10 log files, we
41795820184SJason M. Bills     // can just sort the list to get them in order from newest to oldest
41895820184SJason M. Bills     std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
41995820184SJason M. Bills 
42095820184SJason M. Bills     return !redfishLogFiles.empty();
42195820184SJason M. Bills }
42295820184SJason M. Bills 
423c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
4241da66f75SEd Tanous {
4251da66f75SEd Tanous   public:
4261da66f75SEd Tanous     template <typename CrowApp>
427c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
428029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/")
429c4bf6374SJason M. Bills     {
430c4bf6374SJason M. Bills         entityPrivileges = {
431c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
432c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
433c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
434c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
435c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
436c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
437c4bf6374SJason M. Bills     }
438c4bf6374SJason M. Bills 
439c4bf6374SJason M. Bills   private:
440c4bf6374SJason M. Bills     /**
441c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
442c4bf6374SJason M. Bills      */
443c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
444c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
445c4bf6374SJason M. Bills     {
446c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
447c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
448c4bf6374SJason M. Bills         // it has a duplicate entry for members
449c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
450c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
451c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
452c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
453c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
454029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices";
455c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
456c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
457c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
458c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
459c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
460029573d4SEd Tanous         logServiceArray.push_back(
461029573d4SEd Tanous             {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
462d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
463d53dd41fSJason M. Bills         logServiceArray.push_back(
464cb92c03bSAndrew Geissler             {{"@odata.id",
465424c4176SJason M. Bills               "/redfish/v1/Systems/system/LogServices/Crashdump"}});
466d53dd41fSJason M. Bills #endif
467c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
468c4bf6374SJason M. Bills             logServiceArray.size();
469c4bf6374SJason M. Bills     }
470c4bf6374SJason M. Bills };
471c4bf6374SJason M. Bills 
472c4bf6374SJason M. Bills class EventLogService : public Node
473c4bf6374SJason M. Bills {
474c4bf6374SJason M. Bills   public:
475c4bf6374SJason M. Bills     template <typename CrowApp>
476c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
477029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
478c4bf6374SJason M. Bills     {
479c4bf6374SJason M. Bills         entityPrivileges = {
480c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
481c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
482c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
483c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
484c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
485c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
486c4bf6374SJason M. Bills     }
487c4bf6374SJason M. Bills 
488c4bf6374SJason M. Bills   private:
489c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
490c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
491c4bf6374SJason M. Bills     {
492c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
493c4bf6374SJason M. Bills 
494c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
495029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog";
496c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
497c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
498c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
499c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
500c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
501c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
502c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
503c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
504c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
505c4bf6374SJason M. Bills             {"@odata.id",
506029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
507e7d6c8b2SGunnar Mills         asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
508e7d6c8b2SGunnar Mills 
509e7d6c8b2SGunnar Mills             {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
510e7d6c8b2SGunnar Mills                        "Actions/LogService.ClearLog"}};
511489640c6SJason M. Bills     }
512489640c6SJason M. Bills };
513489640c6SJason M. Bills 
5141f56a3a6STim Lee class JournalEventLogClear : public Node
515489640c6SJason M. Bills {
516489640c6SJason M. Bills   public:
5171f56a3a6STim Lee     JournalEventLogClear(CrowApp &app) :
518489640c6SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
519489640c6SJason M. Bills                   "LogService.ClearLog/")
520489640c6SJason M. Bills     {
521489640c6SJason M. Bills         entityPrivileges = {
522489640c6SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
523489640c6SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
524489640c6SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
525489640c6SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
526489640c6SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
527489640c6SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
528489640c6SJason M. Bills     }
529489640c6SJason M. Bills 
530489640c6SJason M. Bills   private:
531489640c6SJason M. Bills     void doPost(crow::Response &res, const crow::Request &req,
532489640c6SJason M. Bills                 const std::vector<std::string> &params) override
533489640c6SJason M. Bills     {
534489640c6SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
535489640c6SJason M. Bills 
536489640c6SJason M. Bills         // Clear the EventLog by deleting the log files
537489640c6SJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
538489640c6SJason M. Bills         if (getRedfishLogFiles(redfishLogFiles))
539489640c6SJason M. Bills         {
540489640c6SJason M. Bills             for (const std::filesystem::path &file : redfishLogFiles)
541489640c6SJason M. Bills             {
542489640c6SJason M. Bills                 std::error_code ec;
543489640c6SJason M. Bills                 std::filesystem::remove(file, ec);
544489640c6SJason M. Bills             }
545489640c6SJason M. Bills         }
546489640c6SJason M. Bills 
547489640c6SJason M. Bills         // Reload rsyslog so it knows to start new log files
548489640c6SJason M. Bills         crow::connections::systemBus->async_method_call(
549489640c6SJason M. Bills             [asyncResp](const boost::system::error_code ec) {
550489640c6SJason M. Bills                 if (ec)
551489640c6SJason M. Bills                 {
552489640c6SJason M. Bills                     BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
553489640c6SJason M. Bills                     messages::internalError(asyncResp->res);
554489640c6SJason M. Bills                     return;
555489640c6SJason M. Bills                 }
556489640c6SJason M. Bills 
557489640c6SJason M. Bills                 messages::success(asyncResp->res);
558489640c6SJason M. Bills             },
559489640c6SJason M. Bills             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
560489640c6SJason M. Bills             "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
561489640c6SJason M. Bills             "replace");
562c4bf6374SJason M. Bills     }
563c4bf6374SJason M. Bills };
564c4bf6374SJason M. Bills 
56595820184SJason M. Bills static int fillEventLogEntryJson(const std::string &logEntryID,
56695820184SJason M. Bills                                  const std::string logEntry,
56795820184SJason M. Bills                                  nlohmann::json &logEntryJson)
568c4bf6374SJason M. Bills {
56995820184SJason M. Bills     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
570cd225da8SJason M. Bills     // First get the Timestamp
571cd225da8SJason M. Bills     size_t space = logEntry.find_first_of(" ");
572cd225da8SJason M. Bills     if (space == std::string::npos)
57395820184SJason M. Bills     {
57495820184SJason M. Bills         return 1;
57595820184SJason M. Bills     }
576cd225da8SJason M. Bills     std::string timestamp = logEntry.substr(0, space);
577cd225da8SJason M. Bills     // Then get the log contents
578cd225da8SJason M. Bills     size_t entryStart = logEntry.find_first_not_of(" ", space);
579cd225da8SJason M. Bills     if (entryStart == std::string::npos)
580cd225da8SJason M. Bills     {
581cd225da8SJason M. Bills         return 1;
582cd225da8SJason M. Bills     }
583cd225da8SJason M. Bills     std::string_view entry(logEntry);
584cd225da8SJason M. Bills     entry.remove_prefix(entryStart);
585cd225da8SJason M. Bills     // Use split to separate the entry into its fields
586cd225da8SJason M. Bills     std::vector<std::string> logEntryFields;
587cd225da8SJason M. Bills     boost::split(logEntryFields, entry, boost::is_any_of(","),
588cd225da8SJason M. Bills                  boost::token_compress_on);
589cd225da8SJason M. Bills     // We need at least a MessageId to be valid
590cd225da8SJason M. Bills     if (logEntryFields.size() < 1)
591cd225da8SJason M. Bills     {
592cd225da8SJason M. Bills         return 1;
593cd225da8SJason M. Bills     }
594cd225da8SJason M. Bills     std::string &messageID = logEntryFields[0];
59595820184SJason M. Bills 
5964851d45dSJason M. Bills     // Get the Message from the MessageRegistry
5974851d45dSJason M. Bills     const message_registries::Message *message =
5984851d45dSJason M. Bills         message_registries::getMessage(messageID);
599c4bf6374SJason M. Bills 
6004851d45dSJason M. Bills     std::string msg;
6014851d45dSJason M. Bills     std::string severity;
6024851d45dSJason M. Bills     if (message != nullptr)
603c4bf6374SJason M. Bills     {
6044851d45dSJason M. Bills         msg = message->message;
6054851d45dSJason M. Bills         severity = message->severity;
606c4bf6374SJason M. Bills     }
607c4bf6374SJason M. Bills 
60815a86ff6SJason M. Bills     // Get the MessageArgs from the log if there are any
60915a86ff6SJason M. Bills     boost::beast::span<std::string> messageArgs;
61015a86ff6SJason M. Bills     if (logEntryFields.size() > 1)
61115a86ff6SJason M. Bills     {
61215a86ff6SJason M. Bills         std::string &messageArgsStart = logEntryFields[1];
61315a86ff6SJason M. Bills         // If the first string is empty, assume there are no MessageArgs
61415a86ff6SJason M. Bills         std::size_t messageArgsSize = 0;
61515a86ff6SJason M. Bills         if (!messageArgsStart.empty())
61615a86ff6SJason M. Bills         {
61715a86ff6SJason M. Bills             messageArgsSize = logEntryFields.size() - 1;
61815a86ff6SJason M. Bills         }
61915a86ff6SJason M. Bills 
62015a86ff6SJason M. Bills         messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
621c4bf6374SJason M. Bills 
6224851d45dSJason M. Bills         // Fill the MessageArgs into the Message
62395820184SJason M. Bills         int i = 0;
62495820184SJason M. Bills         for (const std::string &messageArg : messageArgs)
6254851d45dSJason M. Bills         {
62695820184SJason M. Bills             std::string argStr = "%" + std::to_string(++i);
6274851d45dSJason M. Bills             size_t argPos = msg.find(argStr);
6284851d45dSJason M. Bills             if (argPos != std::string::npos)
6294851d45dSJason M. Bills             {
63095820184SJason M. Bills                 msg.replace(argPos, argStr.length(), messageArg);
6314851d45dSJason M. Bills             }
6324851d45dSJason M. Bills         }
63315a86ff6SJason M. Bills     }
6344851d45dSJason M. Bills 
63595820184SJason M. Bills     // Get the Created time from the timestamp. The log timestamp is in RFC3339
63695820184SJason M. Bills     // format which matches the Redfish format except for the fractional seconds
63795820184SJason M. Bills     // between the '.' and the '+', so just remove them.
63895820184SJason M. Bills     std::size_t dot = timestamp.find_first_of(".");
63995820184SJason M. Bills     std::size_t plus = timestamp.find_first_of("+");
64095820184SJason M. Bills     if (dot != std::string::npos && plus != std::string::npos)
641c4bf6374SJason M. Bills     {
64295820184SJason M. Bills         timestamp.erase(dot, plus - dot);
643c4bf6374SJason M. Bills     }
644c4bf6374SJason M. Bills 
645c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
64695820184SJason M. Bills     logEntryJson = {
647cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
648c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
649029573d4SEd Tanous         {"@odata.id",
650897967deSJason M. Bills          "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
65195820184SJason M. Bills              logEntryID},
652c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
65395820184SJason M. Bills         {"Id", logEntryID},
65495820184SJason M. Bills         {"Message", std::move(msg)},
65595820184SJason M. Bills         {"MessageId", std::move(messageID)},
656c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
657c4bf6374SJason M. Bills         {"EntryType", "Event"},
65895820184SJason M. Bills         {"Severity", std::move(severity)},
65995820184SJason M. Bills         {"Created", std::move(timestamp)}};
660c4bf6374SJason M. Bills     return 0;
661c4bf6374SJason M. Bills }
662c4bf6374SJason M. Bills 
66327062605SAnthony Wilson class JournalEventLogEntryCollection : public Node
664c4bf6374SJason M. Bills {
665c4bf6374SJason M. Bills   public:
666c4bf6374SJason M. Bills     template <typename CrowApp>
66727062605SAnthony Wilson     JournalEventLogEntryCollection(CrowApp &app) :
668029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
669c4bf6374SJason M. Bills     {
670c4bf6374SJason M. Bills         entityPrivileges = {
671c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
672c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
673c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
674c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
675c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
676c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
677c4bf6374SJason M. Bills     }
678c4bf6374SJason M. Bills 
679c4bf6374SJason M. Bills   private:
680c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
681c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
682c4bf6374SJason M. Bills     {
683c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
684271584abSEd Tanous         uint64_t skip = 0;
685271584abSEd Tanous         uint64_t top = maxEntriesPerPage; // Show max entries by default
686c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
687c4bf6374SJason M. Bills         {
688c4bf6374SJason M. Bills             return;
689c4bf6374SJason M. Bills         }
690c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
691c4bf6374SJason M. Bills         {
692c4bf6374SJason M. Bills             return;
693c4bf6374SJason M. Bills         }
694c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
695c4bf6374SJason M. Bills         // it has a duplicate entry for members
696c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
697c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
698c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
699c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
700c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
701029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
702c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
703c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
704c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
705cb92c03bSAndrew Geissler 
706c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
707c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
70895820184SJason M. Bills         // Go through the log files and create a unique ID for each entry
70995820184SJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
71095820184SJason M. Bills         getRedfishLogFiles(redfishLogFiles);
711b01bf299SEd Tanous         uint64_t entryCount = 0;
712cd225da8SJason M. Bills         std::string logEntry;
71395820184SJason M. Bills 
71495820184SJason M. Bills         // Oldest logs are in the last file, so start there and loop backwards
715cd225da8SJason M. Bills         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
716cd225da8SJason M. Bills              it++)
717c4bf6374SJason M. Bills         {
718cd225da8SJason M. Bills             std::ifstream logStream(*it);
71995820184SJason M. Bills             if (!logStream.is_open())
720c4bf6374SJason M. Bills             {
721c4bf6374SJason M. Bills                 continue;
722c4bf6374SJason M. Bills             }
723c4bf6374SJason M. Bills 
724e85d6b16SJason M. Bills             // Reset the unique ID on the first entry
725e85d6b16SJason M. Bills             bool firstEntry = true;
72695820184SJason M. Bills             while (std::getline(logStream, logEntry))
72795820184SJason M. Bills             {
728c4bf6374SJason M. Bills                 entryCount++;
729c4bf6374SJason M. Bills                 // Handle paging using skip (number of entries to skip from the
730c4bf6374SJason M. Bills                 // start) and top (number of entries to display)
731c4bf6374SJason M. Bills                 if (entryCount <= skip || entryCount > skip + top)
732c4bf6374SJason M. Bills                 {
733c4bf6374SJason M. Bills                     continue;
734c4bf6374SJason M. Bills                 }
735c4bf6374SJason M. Bills 
736c4bf6374SJason M. Bills                 std::string idStr;
737e85d6b16SJason M. Bills                 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
738c4bf6374SJason M. Bills                 {
739c4bf6374SJason M. Bills                     continue;
740c4bf6374SJason M. Bills                 }
741c4bf6374SJason M. Bills 
742e85d6b16SJason M. Bills                 if (firstEntry)
743e85d6b16SJason M. Bills                 {
744e85d6b16SJason M. Bills                     firstEntry = false;
745e85d6b16SJason M. Bills                 }
746e85d6b16SJason M. Bills 
747c4bf6374SJason M. Bills                 logEntryArray.push_back({});
748c4bf6374SJason M. Bills                 nlohmann::json &bmcLogEntry = logEntryArray.back();
74995820184SJason M. Bills                 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
750c4bf6374SJason M. Bills                 {
751c4bf6374SJason M. Bills                     messages::internalError(asyncResp->res);
752c4bf6374SJason M. Bills                     return;
753c4bf6374SJason M. Bills                 }
754c4bf6374SJason M. Bills             }
75595820184SJason M. Bills         }
756c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
757c4bf6374SJason M. Bills         if (skip + top < entryCount)
758c4bf6374SJason M. Bills         {
759c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
76095820184SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/EventLog/"
76195820184SJason M. Bills                 "Entries?$skip=" +
762c4bf6374SJason M. Bills                 std::to_string(skip + top);
763c4bf6374SJason M. Bills         }
76408a4e4b5SAnthony Wilson     }
76508a4e4b5SAnthony Wilson };
76608a4e4b5SAnthony Wilson 
767897967deSJason M. Bills class JournalEventLogEntry : public Node
768897967deSJason M. Bills {
769897967deSJason M. Bills   public:
770897967deSJason M. Bills     JournalEventLogEntry(CrowApp &app) :
771897967deSJason M. Bills         Node(app,
772897967deSJason M. Bills              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
773897967deSJason M. Bills              std::string())
774897967deSJason M. Bills     {
775897967deSJason M. Bills         entityPrivileges = {
776897967deSJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
777897967deSJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
778897967deSJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
779897967deSJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
780897967deSJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
781897967deSJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
782897967deSJason M. Bills     }
783897967deSJason M. Bills 
784897967deSJason M. Bills   private:
785897967deSJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
786897967deSJason M. Bills                const std::vector<std::string> &params) override
787897967deSJason M. Bills     {
788897967deSJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
789897967deSJason M. Bills         if (params.size() != 1)
790897967deSJason M. Bills         {
791897967deSJason M. Bills             messages::internalError(asyncResp->res);
792897967deSJason M. Bills             return;
793897967deSJason M. Bills         }
794897967deSJason M. Bills         const std::string &targetID = params[0];
795897967deSJason M. Bills 
796897967deSJason M. Bills         // Go through the log files and check the unique ID for each entry to
797897967deSJason M. Bills         // find the target entry
798897967deSJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
799897967deSJason M. Bills         getRedfishLogFiles(redfishLogFiles);
800897967deSJason M. Bills         std::string logEntry;
801897967deSJason M. Bills 
802897967deSJason M. Bills         // Oldest logs are in the last file, so start there and loop backwards
803897967deSJason M. Bills         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
804897967deSJason M. Bills              it++)
805897967deSJason M. Bills         {
806897967deSJason M. Bills             std::ifstream logStream(*it);
807897967deSJason M. Bills             if (!logStream.is_open())
808897967deSJason M. Bills             {
809897967deSJason M. Bills                 continue;
810897967deSJason M. Bills             }
811897967deSJason M. Bills 
812897967deSJason M. Bills             // Reset the unique ID on the first entry
813897967deSJason M. Bills             bool firstEntry = true;
814897967deSJason M. Bills             while (std::getline(logStream, logEntry))
815897967deSJason M. Bills             {
816897967deSJason M. Bills                 std::string idStr;
817897967deSJason M. Bills                 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
818897967deSJason M. Bills                 {
819897967deSJason M. Bills                     continue;
820897967deSJason M. Bills                 }
821897967deSJason M. Bills 
822897967deSJason M. Bills                 if (firstEntry)
823897967deSJason M. Bills                 {
824897967deSJason M. Bills                     firstEntry = false;
825897967deSJason M. Bills                 }
826897967deSJason M. Bills 
827897967deSJason M. Bills                 if (idStr == targetID)
828897967deSJason M. Bills                 {
829897967deSJason M. Bills                     if (fillEventLogEntryJson(idStr, logEntry,
830897967deSJason M. Bills                                               asyncResp->res.jsonValue) != 0)
831897967deSJason M. Bills                     {
832897967deSJason M. Bills                         messages::internalError(asyncResp->res);
833897967deSJason M. Bills                         return;
834897967deSJason M. Bills                     }
835897967deSJason M. Bills                     return;
836897967deSJason M. Bills                 }
837897967deSJason M. Bills             }
838897967deSJason M. Bills         }
839897967deSJason M. Bills         // Requested ID was not found
840897967deSJason M. Bills         messages::resourceMissingAtURI(asyncResp->res, targetID);
841897967deSJason M. Bills     }
842897967deSJason M. Bills };
843897967deSJason M. Bills 
84408a4e4b5SAnthony Wilson class DBusEventLogEntryCollection : public Node
84508a4e4b5SAnthony Wilson {
84608a4e4b5SAnthony Wilson   public:
84708a4e4b5SAnthony Wilson     template <typename CrowApp>
84808a4e4b5SAnthony Wilson     DBusEventLogEntryCollection(CrowApp &app) :
84908a4e4b5SAnthony Wilson         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
85008a4e4b5SAnthony Wilson     {
85108a4e4b5SAnthony Wilson         entityPrivileges = {
85208a4e4b5SAnthony Wilson             {boost::beast::http::verb::get, {{"Login"}}},
85308a4e4b5SAnthony Wilson             {boost::beast::http::verb::head, {{"Login"}}},
85408a4e4b5SAnthony Wilson             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
85508a4e4b5SAnthony Wilson             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
85608a4e4b5SAnthony Wilson             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
85708a4e4b5SAnthony Wilson             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
85808a4e4b5SAnthony Wilson     }
85908a4e4b5SAnthony Wilson 
86008a4e4b5SAnthony Wilson   private:
86108a4e4b5SAnthony Wilson     void doGet(crow::Response &res, const crow::Request &req,
86208a4e4b5SAnthony Wilson                const std::vector<std::string> &params) override
86308a4e4b5SAnthony Wilson     {
86408a4e4b5SAnthony Wilson         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
86508a4e4b5SAnthony Wilson 
86608a4e4b5SAnthony Wilson         // Collections don't include the static data added by SubRoute because
86708a4e4b5SAnthony Wilson         // it has a duplicate entry for members
86808a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["@odata.type"] =
86908a4e4b5SAnthony Wilson             "#LogEntryCollection.LogEntryCollection";
87008a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["@odata.context"] =
87108a4e4b5SAnthony Wilson             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
87208a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["@odata.id"] =
87308a4e4b5SAnthony Wilson             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
87408a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
87508a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["Description"] =
87608a4e4b5SAnthony Wilson             "Collection of System Event Log Entries";
87708a4e4b5SAnthony Wilson 
878cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
879cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
880cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
881cb92c03bSAndrew Geissler             [asyncResp](const boost::system::error_code ec,
882cb92c03bSAndrew Geissler                         GetManagedObjectsType &resp) {
883cb92c03bSAndrew Geissler                 if (ec)
884cb92c03bSAndrew Geissler                 {
885cb92c03bSAndrew Geissler                     // TODO Handle for specific error code
886cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
887cb92c03bSAndrew Geissler                         << "getLogEntriesIfaceData resp_handler got error "
888cb92c03bSAndrew Geissler                         << ec;
889cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
890cb92c03bSAndrew Geissler                     return;
891cb92c03bSAndrew Geissler                 }
892cb92c03bSAndrew Geissler                 nlohmann::json &entriesArray =
893cb92c03bSAndrew Geissler                     asyncResp->res.jsonValue["Members"];
894cb92c03bSAndrew Geissler                 entriesArray = nlohmann::json::array();
895cb92c03bSAndrew Geissler                 for (auto &objectPath : resp)
896cb92c03bSAndrew Geissler                 {
897cb92c03bSAndrew Geissler                     for (auto &interfaceMap : objectPath.second)
898cb92c03bSAndrew Geissler                     {
899cb92c03bSAndrew Geissler                         if (interfaceMap.first !=
900cb92c03bSAndrew Geissler                             "xyz.openbmc_project.Logging.Entry")
901cb92c03bSAndrew Geissler                         {
902cb92c03bSAndrew Geissler                             BMCWEB_LOG_DEBUG << "Bailing early on "
903cb92c03bSAndrew Geissler                                              << interfaceMap.first;
904cb92c03bSAndrew Geissler                             continue;
905cb92c03bSAndrew Geissler                         }
906cb92c03bSAndrew Geissler                         entriesArray.push_back({});
907cb92c03bSAndrew Geissler                         nlohmann::json &thisEntry = entriesArray.back();
90866664f25SEd Tanous                         uint32_t *id = nullptr;
90966664f25SEd Tanous                         std::time_t timestamp{};
91066664f25SEd Tanous                         std::string *severity = nullptr;
91166664f25SEd Tanous                         std::string *message = nullptr;
912cb92c03bSAndrew Geissler                         for (auto &propertyMap : interfaceMap.second)
913cb92c03bSAndrew Geissler                         {
914cb92c03bSAndrew Geissler                             if (propertyMap.first == "Id")
915cb92c03bSAndrew Geissler                             {
916cb92c03bSAndrew Geissler                                 id = sdbusplus::message::variant_ns::get_if<
917cb92c03bSAndrew Geissler                                     uint32_t>(&propertyMap.second);
918cb92c03bSAndrew Geissler                                 if (id == nullptr)
919cb92c03bSAndrew Geissler                                 {
920cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
921cb92c03bSAndrew Geissler                                                               "Id");
922cb92c03bSAndrew Geissler                                 }
923cb92c03bSAndrew Geissler                             }
924cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Timestamp")
925cb92c03bSAndrew Geissler                             {
926cb92c03bSAndrew Geissler                                 const uint64_t *millisTimeStamp =
927cb92c03bSAndrew Geissler                                     std::get_if<uint64_t>(&propertyMap.second);
928cb92c03bSAndrew Geissler                                 if (millisTimeStamp == nullptr)
929cb92c03bSAndrew Geissler                                 {
930cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
931cb92c03bSAndrew Geissler                                                               "Timestamp");
932271584abSEd Tanous                                     continue;
933cb92c03bSAndrew Geissler                                 }
934cb92c03bSAndrew Geissler                                 // Retrieve Created property with format:
935cb92c03bSAndrew Geissler                                 // yyyy-mm-ddThh:mm:ss
936cb92c03bSAndrew Geissler                                 std::chrono::milliseconds chronoTimeStamp(
937cb92c03bSAndrew Geissler                                     *millisTimeStamp);
938271584abSEd Tanous                                 timestamp = std::chrono::duration_cast<
939271584abSEd Tanous                                                 std::chrono::duration<int>>(
940271584abSEd Tanous                                                 chronoTimeStamp)
941cb92c03bSAndrew Geissler                                                 .count();
942cb92c03bSAndrew Geissler                             }
943cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Severity")
944cb92c03bSAndrew Geissler                             {
945cb92c03bSAndrew Geissler                                 severity = std::get_if<std::string>(
946cb92c03bSAndrew Geissler                                     &propertyMap.second);
947cb92c03bSAndrew Geissler                                 if (severity == nullptr)
948cb92c03bSAndrew Geissler                                 {
949cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
950cb92c03bSAndrew Geissler                                                               "Severity");
951cb92c03bSAndrew Geissler                                 }
952cb92c03bSAndrew Geissler                             }
953cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Message")
954cb92c03bSAndrew Geissler                             {
955cb92c03bSAndrew Geissler                                 message = std::get_if<std::string>(
956cb92c03bSAndrew Geissler                                     &propertyMap.second);
957cb92c03bSAndrew Geissler                                 if (message == nullptr)
958cb92c03bSAndrew Geissler                                 {
959cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
960cb92c03bSAndrew Geissler                                                               "Message");
961cb92c03bSAndrew Geissler                                 }
962cb92c03bSAndrew Geissler                             }
963cb92c03bSAndrew Geissler                         }
964cb92c03bSAndrew Geissler                         thisEntry = {
965cb92c03bSAndrew Geissler                             {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
966cb92c03bSAndrew Geissler                             {"@odata.context", "/redfish/v1/"
967cb92c03bSAndrew Geissler                                                "$metadata#LogEntry.LogEntry"},
968cb92c03bSAndrew Geissler                             {"@odata.id",
969cb92c03bSAndrew Geissler                              "/redfish/v1/Systems/system/LogServices/EventLog/"
970cb92c03bSAndrew Geissler                              "Entries/" +
971cb92c03bSAndrew Geissler                                  std::to_string(*id)},
97227062605SAnthony Wilson                             {"Name", "System Event Log Entry"},
973cb92c03bSAndrew Geissler                             {"Id", std::to_string(*id)},
974cb92c03bSAndrew Geissler                             {"Message", *message},
975cb92c03bSAndrew Geissler                             {"EntryType", "Event"},
976cb92c03bSAndrew Geissler                             {"Severity",
977cb92c03bSAndrew Geissler                              translateSeverityDbusToRedfish(*severity)},
978cb92c03bSAndrew Geissler                             {"Created", crow::utility::getDateTime(timestamp)}};
979cb92c03bSAndrew Geissler                     }
980cb92c03bSAndrew Geissler                 }
981cb92c03bSAndrew Geissler                 std::sort(entriesArray.begin(), entriesArray.end(),
982cb92c03bSAndrew Geissler                           [](const nlohmann::json &left,
983cb92c03bSAndrew Geissler                              const nlohmann::json &right) {
984cb92c03bSAndrew Geissler                               return (left["Id"] <= right["Id"]);
985cb92c03bSAndrew Geissler                           });
986cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue["Members@odata.count"] =
987cb92c03bSAndrew Geissler                     entriesArray.size();
988cb92c03bSAndrew Geissler             },
989cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
990cb92c03bSAndrew Geissler             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
991c4bf6374SJason M. Bills     }
992c4bf6374SJason M. Bills };
993c4bf6374SJason M. Bills 
99408a4e4b5SAnthony Wilson class DBusEventLogEntry : public Node
995c4bf6374SJason M. Bills {
996c4bf6374SJason M. Bills   public:
99708a4e4b5SAnthony Wilson     DBusEventLogEntry(CrowApp &app) :
998c4bf6374SJason M. Bills         Node(app,
999029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1000029573d4SEd Tanous              std::string())
1001c4bf6374SJason M. Bills     {
1002c4bf6374SJason M. Bills         entityPrivileges = {
1003c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1004c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1005c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1006c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1007c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1008c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1009c4bf6374SJason M. Bills     }
1010c4bf6374SJason M. Bills 
1011c4bf6374SJason M. Bills   private:
1012c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1013c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
1014c4bf6374SJason M. Bills     {
1015c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1016029573d4SEd Tanous         if (params.size() != 1)
1017c4bf6374SJason M. Bills         {
1018c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
1019c4bf6374SJason M. Bills             return;
1020c4bf6374SJason M. Bills         }
1021029573d4SEd Tanous         const std::string &entryID = params[0];
1022cb92c03bSAndrew Geissler 
1023cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
1024cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
1025cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
1026cb92c03bSAndrew Geissler             [asyncResp, entryID](const boost::system::error_code ec,
1027cb92c03bSAndrew Geissler                                  GetManagedPropertyType &resp) {
1028cb92c03bSAndrew Geissler                 if (ec)
1029cb92c03bSAndrew Geissler                 {
1030cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
1031cb92c03bSAndrew Geissler                         << "EventLogEntry (DBus) resp_handler got error " << ec;
1032cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
1033cb92c03bSAndrew Geissler                     return;
1034cb92c03bSAndrew Geissler                 }
103566664f25SEd Tanous                 uint32_t *id = nullptr;
103666664f25SEd Tanous                 std::time_t timestamp{};
103766664f25SEd Tanous                 std::string *severity = nullptr;
103866664f25SEd Tanous                 std::string *message = nullptr;
1039cb92c03bSAndrew Geissler                 for (auto &propertyMap : resp)
1040cb92c03bSAndrew Geissler                 {
1041cb92c03bSAndrew Geissler                     if (propertyMap.first == "Id")
1042cb92c03bSAndrew Geissler                     {
1043cb92c03bSAndrew Geissler                         id = std::get_if<uint32_t>(&propertyMap.second);
1044cb92c03bSAndrew Geissler                         if (id == nullptr)
1045cb92c03bSAndrew Geissler                         {
1046cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res, "Id");
1047cb92c03bSAndrew Geissler                         }
1048cb92c03bSAndrew Geissler                     }
1049cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Timestamp")
1050cb92c03bSAndrew Geissler                     {
1051cb92c03bSAndrew Geissler                         const uint64_t *millisTimeStamp =
1052cb92c03bSAndrew Geissler                             std::get_if<uint64_t>(&propertyMap.second);
1053cb92c03bSAndrew Geissler                         if (millisTimeStamp == nullptr)
1054cb92c03bSAndrew Geissler                         {
1055cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
1056cb92c03bSAndrew Geissler                                                       "Timestamp");
1057271584abSEd Tanous                             continue;
1058cb92c03bSAndrew Geissler                         }
1059cb92c03bSAndrew Geissler                         // Retrieve Created property with format:
1060cb92c03bSAndrew Geissler                         // yyyy-mm-ddThh:mm:ss
1061cb92c03bSAndrew Geissler                         std::chrono::milliseconds chronoTimeStamp(
1062cb92c03bSAndrew Geissler                             *millisTimeStamp);
1063cb92c03bSAndrew Geissler                         timestamp =
1064271584abSEd Tanous                             std::chrono::duration_cast<
1065271584abSEd Tanous                                 std::chrono::duration<int>>(chronoTimeStamp)
1066cb92c03bSAndrew Geissler                                 .count();
1067cb92c03bSAndrew Geissler                     }
1068cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Severity")
1069cb92c03bSAndrew Geissler                     {
1070cb92c03bSAndrew Geissler                         severity =
1071cb92c03bSAndrew Geissler                             std::get_if<std::string>(&propertyMap.second);
1072cb92c03bSAndrew Geissler                         if (severity == nullptr)
1073cb92c03bSAndrew Geissler                         {
1074cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
1075cb92c03bSAndrew Geissler                                                       "Severity");
1076cb92c03bSAndrew Geissler                         }
1077cb92c03bSAndrew Geissler                     }
1078cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Message")
1079cb92c03bSAndrew Geissler                     {
1080cb92c03bSAndrew Geissler                         message = std::get_if<std::string>(&propertyMap.second);
1081cb92c03bSAndrew Geissler                         if (message == nullptr)
1082cb92c03bSAndrew Geissler                         {
1083cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
1084cb92c03bSAndrew Geissler                                                       "Message");
1085cb92c03bSAndrew Geissler                         }
1086cb92c03bSAndrew Geissler                     }
1087cb92c03bSAndrew Geissler                 }
1088271584abSEd Tanous                 if (id == nullptr || message == nullptr || severity == nullptr)
1089271584abSEd Tanous                 {
1090271584abSEd Tanous                     return;
1091271584abSEd Tanous                 }
1092cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue = {
1093cb92c03bSAndrew Geissler                     {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1094cb92c03bSAndrew Geissler                     {"@odata.context", "/redfish/v1/"
1095cb92c03bSAndrew Geissler                                        "$metadata#LogEntry.LogEntry"},
1096cb92c03bSAndrew Geissler                     {"@odata.id",
1097cb92c03bSAndrew Geissler                      "/redfish/v1/Systems/system/LogServices/EventLog/"
1098cb92c03bSAndrew Geissler                      "Entries/" +
1099cb92c03bSAndrew Geissler                          std::to_string(*id)},
110027062605SAnthony Wilson                     {"Name", "System Event Log Entry"},
1101cb92c03bSAndrew Geissler                     {"Id", std::to_string(*id)},
1102cb92c03bSAndrew Geissler                     {"Message", *message},
1103cb92c03bSAndrew Geissler                     {"EntryType", "Event"},
1104cb92c03bSAndrew Geissler                     {"Severity", translateSeverityDbusToRedfish(*severity)},
110508a4e4b5SAnthony Wilson                     {"Created", crow::utility::getDateTime(timestamp)}};
1106cb92c03bSAndrew Geissler             },
1107cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging",
1108cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging/entry/" + entryID,
1109cb92c03bSAndrew Geissler             "org.freedesktop.DBus.Properties", "GetAll",
1110cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging.Entry");
1111c4bf6374SJason M. Bills     }
1112336e96c6SChicago Duan 
1113336e96c6SChicago Duan     void doDelete(crow::Response &res, const crow::Request &req,
1114336e96c6SChicago Duan                   const std::vector<std::string> &params) override
1115336e96c6SChicago Duan     {
1116336e96c6SChicago Duan 
1117336e96c6SChicago Duan         BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1118336e96c6SChicago Duan 
1119336e96c6SChicago Duan         auto asyncResp = std::make_shared<AsyncResp>(res);
1120336e96c6SChicago Duan 
1121336e96c6SChicago Duan         if (params.size() != 1)
1122336e96c6SChicago Duan         {
1123336e96c6SChicago Duan             messages::internalError(asyncResp->res);
1124336e96c6SChicago Duan             return;
1125336e96c6SChicago Duan         }
1126336e96c6SChicago Duan         std::string entryID = params[0];
1127336e96c6SChicago Duan 
1128336e96c6SChicago Duan         dbus::utility::escapePathForDbus(entryID);
1129336e96c6SChicago Duan 
1130336e96c6SChicago Duan         // Process response from Logging service.
1131336e96c6SChicago Duan         auto respHandler = [asyncResp](const boost::system::error_code ec) {
1132336e96c6SChicago Duan             BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1133336e96c6SChicago Duan             if (ec)
1134336e96c6SChicago Duan             {
1135336e96c6SChicago Duan                 // TODO Handle for specific error code
1136336e96c6SChicago Duan                 BMCWEB_LOG_ERROR
1137336e96c6SChicago Duan                     << "EventLogEntry (DBus) doDelete respHandler got error "
1138336e96c6SChicago Duan                     << ec;
1139336e96c6SChicago Duan                 asyncResp->res.result(
1140336e96c6SChicago Duan                     boost::beast::http::status::internal_server_error);
1141336e96c6SChicago Duan                 return;
1142336e96c6SChicago Duan             }
1143336e96c6SChicago Duan 
1144336e96c6SChicago Duan             asyncResp->res.result(boost::beast::http::status::ok);
1145336e96c6SChicago Duan         };
1146336e96c6SChicago Duan 
1147336e96c6SChicago Duan         // Make call to Logging service to request Delete Log
1148336e96c6SChicago Duan         crow::connections::systemBus->async_method_call(
1149336e96c6SChicago Duan             respHandler, "xyz.openbmc_project.Logging",
1150336e96c6SChicago Duan             "/xyz/openbmc_project/logging/entry/" + entryID,
1151336e96c6SChicago Duan             "xyz.openbmc_project.Object.Delete", "Delete");
1152336e96c6SChicago Duan     }
1153c4bf6374SJason M. Bills };
1154c4bf6374SJason M. Bills 
1155c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
1156c4bf6374SJason M. Bills {
1157c4bf6374SJason M. Bills   public:
1158c4bf6374SJason M. Bills     template <typename CrowApp>
1159c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
11604ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
11611da66f75SEd Tanous     {
11621da66f75SEd Tanous         entityPrivileges = {
1163e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1164e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1165e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1166e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1167e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1168e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
11691da66f75SEd Tanous     }
11701da66f75SEd Tanous 
11711da66f75SEd Tanous   private:
11721da66f75SEd Tanous     /**
11731da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
11741da66f75SEd Tanous      */
11751da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
11761da66f75SEd Tanous                const std::vector<std::string> &params) override
11771da66f75SEd Tanous     {
1178e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
11791da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
11801da66f75SEd Tanous         // it has a duplicate entry for members
1181e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
11821da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
1183e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1184c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
1185e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1186e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
1187e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1188e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
11891da66f75SEd Tanous             "Collection of LogServices for this Manager";
1190c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1191c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
1192c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1193c4bf6374SJason M. Bills         logServiceArray.push_back(
119408a4e4b5SAnthony Wilson             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
1195c4bf6374SJason M. Bills #endif
1196e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
1197c4bf6374SJason M. Bills             logServiceArray.size();
11981da66f75SEd Tanous     }
11991da66f75SEd Tanous };
12001da66f75SEd Tanous 
1201c4bf6374SJason M. Bills class BMCJournalLogService : public Node
12021da66f75SEd Tanous {
12031da66f75SEd Tanous   public:
12041da66f75SEd Tanous     template <typename CrowApp>
1205c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
1206c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
1207e1f26343SJason M. Bills     {
1208e1f26343SJason M. Bills         entityPrivileges = {
1209e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1210e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1211e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1212e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1213e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1214e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1215e1f26343SJason M. Bills     }
1216e1f26343SJason M. Bills 
1217e1f26343SJason M. Bills   private:
1218e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1219e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1220e1f26343SJason M. Bills     {
1221e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1222e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1223e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
12240f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
12250f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
1226e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1227e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1228c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1229c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1230c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
1231e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1232cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1233cd50aa42SJason M. Bills             {"@odata.id",
1234086be238SEd Tanous              "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
1235e1f26343SJason M. Bills     }
1236e1f26343SJason M. Bills };
1237e1f26343SJason M. Bills 
1238c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1239e1f26343SJason M. Bills                                       sd_journal *journal,
1240c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
1241e1f26343SJason M. Bills {
1242e1f26343SJason M. Bills     // Get the Log Entry contents
1243e1f26343SJason M. Bills     int ret = 0;
1244e1f26343SJason M. Bills 
124539e77504SEd Tanous     std::string_view msg;
124616428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
1247e1f26343SJason M. Bills     if (ret < 0)
1248e1f26343SJason M. Bills     {
1249e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1250e1f26343SJason M. Bills         return 1;
1251e1f26343SJason M. Bills     }
1252e1f26343SJason M. Bills 
1253e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
1254271584abSEd Tanous     long int severity = 8; // Default to an invalid priority
125516428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
1256e1f26343SJason M. Bills     if (ret < 0)
1257e1f26343SJason M. Bills     {
1258e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
1259e1f26343SJason M. Bills     }
1260e1f26343SJason M. Bills 
1261e1f26343SJason M. Bills     // Get the Created time from the timestamp
126216428a1aSJason M. Bills     std::string entryTimeStr;
126316428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
1264e1f26343SJason M. Bills     {
126516428a1aSJason M. Bills         return 1;
1266e1f26343SJason M. Bills     }
1267e1f26343SJason M. Bills 
1268e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
1269c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
1270cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1271e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1272c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1273c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
1274e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
1275c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
127616428a1aSJason M. Bills         {"Message", msg},
1277e1f26343SJason M. Bills         {"EntryType", "Oem"},
1278e1f26343SJason M. Bills         {"Severity",
1279b6a61a5eSJason M. Bills          severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
1280086be238SEd Tanous         {"OemRecordFormat", "BMC Journal Entry"},
1281e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
1282e1f26343SJason M. Bills     return 0;
1283e1f26343SJason M. Bills }
1284e1f26343SJason M. Bills 
1285c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
1286e1f26343SJason M. Bills {
1287e1f26343SJason M. Bills   public:
1288e1f26343SJason M. Bills     template <typename CrowApp>
1289c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
1290c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
1291e1f26343SJason M. Bills     {
1292e1f26343SJason M. Bills         entityPrivileges = {
1293e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1294e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1295e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1296e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1297e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1298e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1299e1f26343SJason M. Bills     }
1300e1f26343SJason M. Bills 
1301e1f26343SJason M. Bills   private:
1302e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1303e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1304e1f26343SJason M. Bills     {
1305e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1306193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
1307271584abSEd Tanous         uint64_t skip = 0;
1308271584abSEd Tanous         uint64_t top = maxEntriesPerPage; // Show max entries by default
130916428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
1310193ad2faSJason M. Bills         {
1311193ad2faSJason M. Bills             return;
1312193ad2faSJason M. Bills         }
131316428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
1314193ad2faSJason M. Bills         {
1315193ad2faSJason M. Bills             return;
1316193ad2faSJason M. Bills         }
1317e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
1318e1f26343SJason M. Bills         // it has a duplicate entry for members
1319e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1320e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
13210f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
13220f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1323e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1324c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1325e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1326c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1327e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1328e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
1329e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
13300f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
13310f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
1332e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1333e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
1334e1f26343SJason M. Bills 
1335e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
1336e1f26343SJason M. Bills         // for each entry
1337e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1338e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1339e1f26343SJason M. Bills         if (ret < 0)
1340e1f26343SJason M. Bills         {
1341e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1342f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1343e1f26343SJason M. Bills             return;
1344e1f26343SJason M. Bills         }
1345e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1346e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1347e1f26343SJason M. Bills         journalTmp = nullptr;
1348b01bf299SEd Tanous         uint64_t entryCount = 0;
1349e85d6b16SJason M. Bills         // Reset the unique ID on the first entry
1350e85d6b16SJason M. Bills         bool firstEntry = true;
1351e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
1352e1f26343SJason M. Bills         {
1353193ad2faSJason M. Bills             entryCount++;
1354193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
1355193ad2faSJason M. Bills             // start) and top (number of entries to display)
1356193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
1357193ad2faSJason M. Bills             {
1358193ad2faSJason M. Bills                 continue;
1359193ad2faSJason M. Bills             }
1360193ad2faSJason M. Bills 
136116428a1aSJason M. Bills             std::string idStr;
1362e85d6b16SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1363e1f26343SJason M. Bills             {
1364e1f26343SJason M. Bills                 continue;
1365e1f26343SJason M. Bills             }
1366e1f26343SJason M. Bills 
1367e85d6b16SJason M. Bills             if (firstEntry)
1368e85d6b16SJason M. Bills             {
1369e85d6b16SJason M. Bills                 firstEntry = false;
1370e85d6b16SJason M. Bills             }
1371e85d6b16SJason M. Bills 
1372e1f26343SJason M. Bills             logEntryArray.push_back({});
1373c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1374c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1375c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
1376e1f26343SJason M. Bills             {
1377f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
1378e1f26343SJason M. Bills                 return;
1379e1f26343SJason M. Bills             }
1380e1f26343SJason M. Bills         }
1381193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1382193ad2faSJason M. Bills         if (skip + top < entryCount)
1383193ad2faSJason M. Bills         {
1384193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
1385c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
1386193ad2faSJason M. Bills                 std::to_string(skip + top);
1387193ad2faSJason M. Bills         }
1388e1f26343SJason M. Bills     }
1389e1f26343SJason M. Bills };
1390e1f26343SJason M. Bills 
1391c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
1392e1f26343SJason M. Bills {
1393e1f26343SJason M. Bills   public:
1394c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
1395c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
1396e1f26343SJason M. Bills              std::string())
1397e1f26343SJason M. Bills     {
1398e1f26343SJason M. Bills         entityPrivileges = {
1399e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1400e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1401e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1402e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1403e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1404e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1405e1f26343SJason M. Bills     }
1406e1f26343SJason M. Bills 
1407e1f26343SJason M. Bills   private:
1408e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1409e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1410e1f26343SJason M. Bills     {
1411e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1412e1f26343SJason M. Bills         if (params.size() != 1)
1413e1f26343SJason M. Bills         {
1414f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1415e1f26343SJason M. Bills             return;
1416e1f26343SJason M. Bills         }
141716428a1aSJason M. Bills         const std::string &entryID = params[0];
1418e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
1419e1f26343SJason M. Bills         uint64_t ts = 0;
1420271584abSEd Tanous         uint64_t index = 0;
142116428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
1422e1f26343SJason M. Bills         {
142316428a1aSJason M. Bills             return;
1424e1f26343SJason M. Bills         }
1425e1f26343SJason M. Bills 
1426e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1427e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1428e1f26343SJason M. Bills         if (ret < 0)
1429e1f26343SJason M. Bills         {
1430e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1431f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1432e1f26343SJason M. Bills             return;
1433e1f26343SJason M. Bills         }
1434e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1435e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1436e1f26343SJason M. Bills         journalTmp = nullptr;
1437e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
1438af07e3f5SJason M. Bills         // tracking the unique ID
1439af07e3f5SJason M. Bills         std::string idStr;
1440af07e3f5SJason M. Bills         bool firstEntry = true;
1441e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1442271584abSEd Tanous         for (uint64_t i = 0; i <= index; i++)
1443e1f26343SJason M. Bills         {
1444e1f26343SJason M. Bills             sd_journal_next(journal.get());
1445af07e3f5SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1446af07e3f5SJason M. Bills             {
1447af07e3f5SJason M. Bills                 messages::internalError(asyncResp->res);
1448af07e3f5SJason M. Bills                 return;
1449af07e3f5SJason M. Bills             }
1450af07e3f5SJason M. Bills             if (firstEntry)
1451af07e3f5SJason M. Bills             {
1452af07e3f5SJason M. Bills                 firstEntry = false;
1453af07e3f5SJason M. Bills             }
1454e1f26343SJason M. Bills         }
1455c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
1456af07e3f5SJason M. Bills         if (idStr != entryID)
1457c4bf6374SJason M. Bills         {
1458c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
1459c4bf6374SJason M. Bills             return;
1460c4bf6374SJason M. Bills         }
1461c4bf6374SJason M. Bills 
1462c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1463e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
1464e1f26343SJason M. Bills         {
1465f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1466e1f26343SJason M. Bills             return;
1467e1f26343SJason M. Bills         }
1468e1f26343SJason M. Bills     }
1469e1f26343SJason M. Bills };
1470e1f26343SJason M. Bills 
1471424c4176SJason M. Bills class CrashdumpService : public Node
1472e1f26343SJason M. Bills {
1473e1f26343SJason M. Bills   public:
1474e1f26343SJason M. Bills     template <typename CrowApp>
1475424c4176SJason M. Bills     CrashdumpService(CrowApp &app) :
1476424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
14771da66f75SEd Tanous     {
14781da66f75SEd Tanous         entityPrivileges = {
1479e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1480e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1481e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1482e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1483e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1484e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
14851da66f75SEd Tanous     }
14861da66f75SEd Tanous 
14871da66f75SEd Tanous   private:
14881da66f75SEd Tanous     /**
14891da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
14901da66f75SEd Tanous      */
14911da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
14921da66f75SEd Tanous                const std::vector<std::string> &params) override
14931da66f75SEd Tanous     {
1494e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
14951da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
14960f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
1497424c4176SJason M. Bills             "/redfish/v1/Systems/system/LogServices/Crashdump";
1498e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1499e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
1500e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1501c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1502424c4176SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1503424c4176SJason M. Bills         asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1504424c4176SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Crashdump";
1505e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1506e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
1507cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1508cd50aa42SJason M. Bills             {"@odata.id",
1509424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
1510e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
15115b61b5e8SJason M. Bills             {"#LogService.ClearLog",
15125b61b5e8SJason M. Bills              {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
15135b61b5e8SJason M. Bills                          "Actions/LogService.ClearLog"}}},
15141da66f75SEd Tanous             {"Oem",
1515424c4176SJason M. Bills              {{"#Crashdump.OnDemand",
1516424c4176SJason M. Bills                {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1517424c4176SJason M. Bills                            "Actions/Oem/Crashdump.OnDemand"}}}}}};
15181da66f75SEd Tanous 
15191da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
1520e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
1521424c4176SJason M. Bills             {"#Crashdump.SendRawPeci",
152208a4e4b5SAnthony Wilson              {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1523424c4176SJason M. Bills                          "Actions/Oem/Crashdump.SendRawPeci"}}});
15241da66f75SEd Tanous #endif
15251da66f75SEd Tanous     }
15261da66f75SEd Tanous };
15271da66f75SEd Tanous 
15285b61b5e8SJason M. Bills class CrashdumpClear : public Node
15295b61b5e8SJason M. Bills {
15305b61b5e8SJason M. Bills   public:
15315b61b5e8SJason M. Bills     CrashdumpClear(CrowApp &app) :
15325b61b5e8SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
15335b61b5e8SJason M. Bills                   "LogService.ClearLog/")
15345b61b5e8SJason M. Bills     {
15355b61b5e8SJason M. Bills         entityPrivileges = {
15365b61b5e8SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
15375b61b5e8SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
15385b61b5e8SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
15395b61b5e8SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
15405b61b5e8SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
15415b61b5e8SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
15425b61b5e8SJason M. Bills     }
15435b61b5e8SJason M. Bills 
15445b61b5e8SJason M. Bills   private:
15455b61b5e8SJason M. Bills     void doPost(crow::Response &res, const crow::Request &req,
15465b61b5e8SJason M. Bills                 const std::vector<std::string> &params) override
15475b61b5e8SJason M. Bills     {
15485b61b5e8SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
15495b61b5e8SJason M. Bills 
15505b61b5e8SJason M. Bills         crow::connections::systemBus->async_method_call(
15515b61b5e8SJason M. Bills             [asyncResp](const boost::system::error_code ec,
15525b61b5e8SJason M. Bills                         const std::string &resp) {
15535b61b5e8SJason M. Bills                 if (ec)
15545b61b5e8SJason M. Bills                 {
15555b61b5e8SJason M. Bills                     messages::internalError(asyncResp->res);
15565b61b5e8SJason M. Bills                     return;
15575b61b5e8SJason M. Bills                 }
15585b61b5e8SJason M. Bills                 messages::success(asyncResp->res);
15595b61b5e8SJason M. Bills             },
15605b61b5e8SJason M. Bills             crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
15615b61b5e8SJason M. Bills     }
15625b61b5e8SJason M. Bills };
15635b61b5e8SJason M. Bills 
1564e855dd28SJason M. Bills std::string getLogCreatedTime(const std::string &crashdump)
1565e855dd28SJason M. Bills {
1566e855dd28SJason M. Bills     nlohmann::json crashdumpJson =
1567e855dd28SJason M. Bills         nlohmann::json::parse(crashdump, nullptr, false);
1568e855dd28SJason M. Bills     if (crashdumpJson.is_discarded())
1569e855dd28SJason M. Bills     {
1570e855dd28SJason M. Bills         return std::string();
1571e855dd28SJason M. Bills     }
1572e855dd28SJason M. Bills 
1573e855dd28SJason M. Bills     nlohmann::json::const_iterator cdIt = crashdumpJson.find("crash_data");
1574e855dd28SJason M. Bills     if (cdIt == crashdumpJson.end())
1575e855dd28SJason M. Bills     {
1576e855dd28SJason M. Bills         return std::string();
1577e855dd28SJason M. Bills     }
1578e855dd28SJason M. Bills 
1579e855dd28SJason M. Bills     nlohmann::json::const_iterator siIt = cdIt->find("METADATA");
1580e855dd28SJason M. Bills     if (siIt == cdIt->end())
1581e855dd28SJason M. Bills     {
1582e855dd28SJason M. Bills         return std::string();
1583e855dd28SJason M. Bills     }
1584e855dd28SJason M. Bills 
1585e855dd28SJason M. Bills     nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1586e855dd28SJason M. Bills     if (tsIt == siIt->end())
1587e855dd28SJason M. Bills     {
1588e855dd28SJason M. Bills         return std::string();
1589e855dd28SJason M. Bills     }
1590e855dd28SJason M. Bills 
1591e855dd28SJason M. Bills     const std::string *logTime = tsIt->get_ptr<const std::string *>();
1592e855dd28SJason M. Bills     if (logTime == nullptr)
1593e855dd28SJason M. Bills     {
1594e855dd28SJason M. Bills         return std::string();
1595e855dd28SJason M. Bills     }
1596e855dd28SJason M. Bills 
1597e855dd28SJason M. Bills     std::string redfishDateTime = *logTime;
1598e855dd28SJason M. Bills     if (redfishDateTime.length() > 2)
1599e855dd28SJason M. Bills     {
1600e855dd28SJason M. Bills         redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1601e855dd28SJason M. Bills     }
1602e855dd28SJason M. Bills 
1603e855dd28SJason M. Bills     return redfishDateTime;
1604e855dd28SJason M. Bills }
1605e855dd28SJason M. Bills 
1606e855dd28SJason M. Bills std::string getLogFileName(const std::string &logTime)
1607e855dd28SJason M. Bills {
1608e855dd28SJason M. Bills     // Set the crashdump file name to "crashdump_<logTime>.json" using the
1609e855dd28SJason M. Bills     // created time without the timezone info
1610e855dd28SJason M. Bills     std::string fileTime = logTime;
1611e855dd28SJason M. Bills     size_t plusPos = fileTime.rfind('+');
1612e855dd28SJason M. Bills     if (plusPos != std::string::npos)
1613e855dd28SJason M. Bills     {
1614e855dd28SJason M. Bills         fileTime.erase(plusPos);
1615e855dd28SJason M. Bills     }
1616e855dd28SJason M. Bills     return "crashdump_" + fileTime + ".json";
1617e855dd28SJason M. Bills }
1618e855dd28SJason M. Bills 
1619e855dd28SJason M. Bills static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1620e855dd28SJason M. Bills                               const std::string &logID,
1621e855dd28SJason M. Bills                               nlohmann::json &logEntryJson)
1622e855dd28SJason M. Bills {
1623e855dd28SJason M. Bills     auto getStoredLogCallback = [asyncResp, logID, &logEntryJson](
1624e855dd28SJason M. Bills                                     const boost::system::error_code ec,
1625e855dd28SJason M. Bills                                     const std::variant<std::string> &resp) {
1626e855dd28SJason M. Bills         if (ec)
1627e855dd28SJason M. Bills         {
1628e855dd28SJason M. Bills             BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
16291ddcf01aSJason M. Bills             if (ec.value() ==
16301ddcf01aSJason M. Bills                 boost::system::linux_error::bad_request_descriptor)
16311ddcf01aSJason M. Bills             {
16321ddcf01aSJason M. Bills                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
16331ddcf01aSJason M. Bills             }
16341ddcf01aSJason M. Bills             else
16351ddcf01aSJason M. Bills             {
1636e855dd28SJason M. Bills                 messages::internalError(asyncResp->res);
16371ddcf01aSJason M. Bills             }
1638e855dd28SJason M. Bills             return;
1639e855dd28SJason M. Bills         }
1640e855dd28SJason M. Bills         const std::string *log = std::get_if<std::string>(&resp);
1641e855dd28SJason M. Bills         if (log == nullptr)
1642e855dd28SJason M. Bills         {
1643e855dd28SJason M. Bills             messages::internalError(asyncResp->res);
1644e855dd28SJason M. Bills             return;
1645e855dd28SJason M. Bills         }
1646e855dd28SJason M. Bills         std::string logTime = getLogCreatedTime(*log);
1647e855dd28SJason M. Bills         std::string fileName = getLogFileName(logTime);
1648e855dd28SJason M. Bills 
1649e855dd28SJason M. Bills         logEntryJson = {
1650e855dd28SJason M. Bills             {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1651e855dd28SJason M. Bills             {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1652e855dd28SJason M. Bills             {"@odata.id",
1653e855dd28SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1654e855dd28SJason M. Bills                  logID},
1655e855dd28SJason M. Bills             {"Name", "CPU Crashdump"},
1656e855dd28SJason M. Bills             {"Id", logID},
1657e855dd28SJason M. Bills             {"EntryType", "Oem"},
1658e855dd28SJason M. Bills             {"OemRecordFormat", "Crashdump URI"},
1659e855dd28SJason M. Bills             {"Message",
1660e855dd28SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1661e855dd28SJason M. Bills                  logID + "/" + fileName},
1662e855dd28SJason M. Bills             {"Created", std::move(logTime)}};
1663e855dd28SJason M. Bills     };
1664e855dd28SJason M. Bills     crow::connections::systemBus->async_method_call(
16655b61b5e8SJason M. Bills         std::move(getStoredLogCallback), crashdumpObject,
16665b61b5e8SJason M. Bills         crashdumpPath + std::string("/") + logID,
16675b61b5e8SJason M. Bills         "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, "Log");
1668e855dd28SJason M. Bills }
1669e855dd28SJason M. Bills 
1670424c4176SJason M. Bills class CrashdumpEntryCollection : public Node
16711da66f75SEd Tanous {
16721da66f75SEd Tanous   public:
16731da66f75SEd Tanous     template <typename CrowApp>
1674424c4176SJason M. Bills     CrashdumpEntryCollection(CrowApp &app) :
1675424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
16761da66f75SEd Tanous     {
16771da66f75SEd Tanous         entityPrivileges = {
1678e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1679e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1680e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1681e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1682e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1683e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
16841da66f75SEd Tanous     }
16851da66f75SEd Tanous 
16861da66f75SEd Tanous   private:
16871da66f75SEd Tanous     /**
16881da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
16891da66f75SEd Tanous      */
16901da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
16911da66f75SEd Tanous                const std::vector<std::string> &params) override
16921da66f75SEd Tanous     {
1693e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
16941da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
16951da66f75SEd Tanous         // it has a duplicate entry for members
1696e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
1697e1f26343SJason M. Bills                                          const boost::system::error_code ec,
16981da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
16991da66f75SEd Tanous             if (ec)
17001da66f75SEd Tanous             {
17011da66f75SEd Tanous                 if (ec.value() !=
17021da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
17031da66f75SEd Tanous                 {
17041da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
17051da66f75SEd Tanous                                      << ec.message();
1706f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
17071da66f75SEd Tanous                     return;
17081da66f75SEd Tanous                 }
17091da66f75SEd Tanous             }
1710e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
17111da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
17120f74e643SEd Tanous             asyncResp->res.jsonValue["@odata.id"] =
1713424c4176SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
1714e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1715e855dd28SJason M. Bills                 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1716424c4176SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
1717e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1718424c4176SJason M. Bills                 "Collection of Crashdump Entries";
1719e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1720e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
1721e855dd28SJason M. Bills             std::vector<std::string> logIDs;
1722e855dd28SJason M. Bills             // Get the list of log entries and build up an empty array big
1723e855dd28SJason M. Bills             // enough to hold them
17241da66f75SEd Tanous             for (const std::string &objpath : resp)
17251da66f75SEd Tanous             {
1726e855dd28SJason M. Bills                 // Ignore the on-demand log
17275b61b5e8SJason M. Bills                 if (objpath.compare(crashdumpOnDemandPath) == 0)
17281da66f75SEd Tanous                 {
17291da66f75SEd Tanous                     continue;
17301da66f75SEd Tanous                 }
1731e855dd28SJason M. Bills 
1732e855dd28SJason M. Bills                 // Get the log ID
17334ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
1734e855dd28SJason M. Bills                 if (lastPos == std::string::npos)
17351da66f75SEd Tanous                 {
1736e855dd28SJason M. Bills                     continue;
17371da66f75SEd Tanous                 }
1738e855dd28SJason M. Bills                 logIDs.emplace_back(objpath.substr(lastPos + 1));
1739e855dd28SJason M. Bills 
1740e855dd28SJason M. Bills                 // Add a space for the log entry to the array
1741e855dd28SJason M. Bills                 logEntryArray.push_back({});
1742e855dd28SJason M. Bills             }
1743e855dd28SJason M. Bills             // Now go through and set up async calls to fill in the entries
1744e855dd28SJason M. Bills             size_t index = 0;
1745e855dd28SJason M. Bills             for (const std::string &logID : logIDs)
1746e855dd28SJason M. Bills             {
1747e855dd28SJason M. Bills                 // Add the log entry to the array
1748e855dd28SJason M. Bills                 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
17491da66f75SEd Tanous             }
1750e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1751e1f26343SJason M. Bills                 logEntryArray.size();
17521da66f75SEd Tanous         };
17531da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
17541da66f75SEd Tanous             std::move(getLogEntriesCallback),
17551da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
17561da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
17571da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
17585b61b5e8SJason M. Bills             std::array<const char *, 1>{crashdumpInterface});
17591da66f75SEd Tanous     }
17601da66f75SEd Tanous };
17611da66f75SEd Tanous 
1762424c4176SJason M. Bills class CrashdumpEntry : public Node
17631da66f75SEd Tanous {
17641da66f75SEd Tanous   public:
1765424c4176SJason M. Bills     CrashdumpEntry(CrowApp &app) :
1766d53dd41fSJason M. Bills         Node(app,
1767424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
17681da66f75SEd Tanous              std::string())
17691da66f75SEd Tanous     {
17701da66f75SEd Tanous         entityPrivileges = {
1771e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1772e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1773e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1774e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1775e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1776e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
17771da66f75SEd Tanous     }
17781da66f75SEd Tanous 
17791da66f75SEd Tanous   private:
17801da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
17811da66f75SEd Tanous                const std::vector<std::string> &params) override
17821da66f75SEd Tanous     {
1783e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
17841da66f75SEd Tanous         if (params.size() != 1)
17851da66f75SEd Tanous         {
1786f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
17871da66f75SEd Tanous             return;
17881da66f75SEd Tanous         }
1789e855dd28SJason M. Bills         const std::string &logID = params[0];
1790e855dd28SJason M. Bills         logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
1791e855dd28SJason M. Bills     }
1792e855dd28SJason M. Bills };
1793e855dd28SJason M. Bills 
1794e855dd28SJason M. Bills class CrashdumpFile : public Node
1795e855dd28SJason M. Bills {
1796e855dd28SJason M. Bills   public:
1797e855dd28SJason M. Bills     CrashdumpFile(CrowApp &app) :
1798e855dd28SJason M. Bills         Node(app,
1799e855dd28SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
1800e855dd28SJason M. Bills              "<str>/",
1801e855dd28SJason M. Bills              std::string(), std::string())
1802e855dd28SJason M. Bills     {
1803e855dd28SJason M. Bills         entityPrivileges = {
1804e855dd28SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1805e855dd28SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1806e855dd28SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1807e855dd28SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1808e855dd28SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1809e855dd28SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1810e855dd28SJason M. Bills     }
1811e855dd28SJason M. Bills 
1812e855dd28SJason M. Bills   private:
1813e855dd28SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1814e855dd28SJason M. Bills                const std::vector<std::string> &params) override
1815e855dd28SJason M. Bills     {
1816e855dd28SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1817e855dd28SJason M. Bills         if (params.size() != 2)
1818e855dd28SJason M. Bills         {
1819e855dd28SJason M. Bills             messages::internalError(asyncResp->res);
1820e855dd28SJason M. Bills             return;
1821e855dd28SJason M. Bills         }
1822e855dd28SJason M. Bills         const std::string &logID = params[0];
1823e855dd28SJason M. Bills         const std::string &fileName = params[1];
1824e855dd28SJason M. Bills 
1825e855dd28SJason M. Bills         auto getStoredLogCallback = [asyncResp, logID, fileName](
1826abf2add6SEd Tanous                                         const boost::system::error_code ec,
1827abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
18281da66f75SEd Tanous             if (ec)
18291da66f75SEd Tanous             {
1830abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1831f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
18321da66f75SEd Tanous                 return;
18331da66f75SEd Tanous             }
1834abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
18351da66f75SEd Tanous             if (log == nullptr)
18361da66f75SEd Tanous             {
1837f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
18381da66f75SEd Tanous                 return;
18391da66f75SEd Tanous             }
1840e855dd28SJason M. Bills 
1841e855dd28SJason M. Bills             // Verify the file name parameter is correct
1842e855dd28SJason M. Bills             if (fileName != getLogFileName(getLogCreatedTime(*log)))
18431da66f75SEd Tanous             {
1844e855dd28SJason M. Bills                 messages::resourceMissingAtURI(asyncResp->res, fileName);
18451da66f75SEd Tanous                 return;
18461da66f75SEd Tanous             }
1847e855dd28SJason M. Bills 
1848e855dd28SJason M. Bills             // Configure this to be a file download when accessed from a browser
1849e855dd28SJason M. Bills             asyncResp->res.addHeader("Content-Disposition", "attachment");
1850e855dd28SJason M. Bills             asyncResp->res.body() = *log;
18511da66f75SEd Tanous         };
18521da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
18535b61b5e8SJason M. Bills             std::move(getStoredLogCallback), crashdumpObject,
18545b61b5e8SJason M. Bills             crashdumpPath + std::string("/") + logID,
18555b61b5e8SJason M. Bills             "org.freedesktop.DBus.Properties", "Get", crashdumpInterface,
1856424c4176SJason M. Bills             "Log");
18571da66f75SEd Tanous     }
18581da66f75SEd Tanous };
18591da66f75SEd Tanous 
1860424c4176SJason M. Bills class OnDemandCrashdump : public Node
18611da66f75SEd Tanous {
18621da66f75SEd Tanous   public:
1863424c4176SJason M. Bills     OnDemandCrashdump(CrowApp &app) :
1864424c4176SJason M. Bills         Node(app,
1865424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1866424c4176SJason M. Bills              "Crashdump.OnDemand/")
18671da66f75SEd Tanous     {
18681da66f75SEd Tanous         entityPrivileges = {
1869e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1870e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1871e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1872e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1873e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1874e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
18751da66f75SEd Tanous     }
18761da66f75SEd Tanous 
18771da66f75SEd Tanous   private:
18781da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
18791da66f75SEd Tanous                 const std::vector<std::string> &params) override
18801da66f75SEd Tanous     {
1881e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
188248e4639eSJason M. Bills         static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
18831da66f75SEd Tanous 
188448e4639eSJason M. Bills         // Only allow one OnDemand Log request at a time
188548e4639eSJason M. Bills         if (onDemandLogMatcher != nullptr)
18861da66f75SEd Tanous         {
1887f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
18881da66f75SEd Tanous             return;
18891da66f75SEd Tanous         }
18901da66f75SEd Tanous         // Make this static so it survives outside this method
1891271584abSEd Tanous         static boost::asio::steady_timer timeout(*req.ioService);
18921da66f75SEd Tanous 
1893271584abSEd Tanous         timeout.expires_after(std::chrono::seconds(30));
1894e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
189548e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
18961da66f75SEd Tanous             if (ec)
18971da66f75SEd Tanous             {
18981da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
18991da66f75SEd Tanous                 // completion.
19001da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
19011da66f75SEd Tanous                 {
19021da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
19031da66f75SEd Tanous                 }
19041da66f75SEd Tanous                 return;
19051da66f75SEd Tanous             }
190648e4639eSJason M. Bills             BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
19071da66f75SEd Tanous 
1908f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
19091da66f75SEd Tanous         });
19101da66f75SEd Tanous 
19113cf8ea3cSJason M. Bills         auto onDemandLogMatcherCallback =
19123cf8ea3cSJason M. Bills             [asyncResp](sdbusplus::message::message &m) {
191348e4639eSJason M. Bills                 BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
1914271584abSEd Tanous                 timeout.cancel();
1915271584abSEd Tanous 
19164ed77cd5SEd Tanous                 sdbusplus::message::object_path objPath;
19171da66f75SEd Tanous                 boost::container::flat_map<
1918abf2add6SEd Tanous                     std::string, boost::container::flat_map<
1919abf2add6SEd Tanous                                      std::string, std::variant<std::string>>>
19204ed77cd5SEd Tanous                     interfacesAdded;
19214ed77cd5SEd Tanous                 m.read(objPath, interfacesAdded);
1922abf2add6SEd Tanous                 const std::string *log = std::get_if<std::string>(
19235b61b5e8SJason M. Bills                     &interfacesAdded[crashdumpInterface]["Log"]);
19241da66f75SEd Tanous                 if (log == nullptr)
19251da66f75SEd Tanous                 {
1926f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
19273cf8ea3cSJason M. Bills                     // Careful with onDemandLogMatcher.  It is a unique_ptr to
19283cf8ea3cSJason M. Bills                     // the match object inside which this lambda is executing.
19293cf8ea3cSJason M. Bills                     // Once it is set to nullptr, the match object will be
19303cf8ea3cSJason M. Bills                     // destroyed and the lambda will lose its context, including
19313cf8ea3cSJason M. Bills                     // res, so it needs to be the last thing done.
193248e4639eSJason M. Bills                     onDemandLogMatcher = nullptr;
19331da66f75SEd Tanous                     return;
19341da66f75SEd Tanous                 }
19353cf8ea3cSJason M. Bills                 nlohmann::json crashdumpJson =
19363cf8ea3cSJason M. Bills                     nlohmann::json::parse(*log, nullptr, false);
19373cf8ea3cSJason M. Bills                 if (crashdumpJson.is_discarded())
19381da66f75SEd Tanous                 {
1939f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
19403cf8ea3cSJason M. Bills                     // Careful with onDemandLogMatcher.  It is a unique_ptr to
19413cf8ea3cSJason M. Bills                     // the match object inside which this lambda is executing.
19423cf8ea3cSJason M. Bills                     // Once it is set to nullptr, the match object will be
19433cf8ea3cSJason M. Bills                     // destroyed and the lambda will lose its context, including
19443cf8ea3cSJason M. Bills                     // res, so it needs to be the last thing done.
19453cf8ea3cSJason M. Bills                     onDemandLogMatcher = nullptr;
19463cf8ea3cSJason M. Bills                     return;
19473cf8ea3cSJason M. Bills                 }
19483cf8ea3cSJason M. Bills                 asyncResp->res.jsonValue = crashdumpJson;
194948e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
19501da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
19511da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
19521da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
19531da66f75SEd Tanous                 // be the last thing done.
195448e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
19551da66f75SEd Tanous             };
195648e4639eSJason M. Bills         onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
19571da66f75SEd Tanous             *crow::connections::systemBus,
19581da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
1959424c4176SJason M. Bills                 sdbusplus::bus::match::rules::argNpath(0,
19605b61b5e8SJason M. Bills                                                        crashdumpOnDemandPath),
196148e4639eSJason M. Bills             std::move(onDemandLogMatcherCallback));
19621da66f75SEd Tanous 
196348e4639eSJason M. Bills         auto generateonDemandLogCallback =
1964e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
19651da66f75SEd Tanous                         const std::string &resp) {
19661da66f75SEd Tanous                 if (ec)
19671da66f75SEd Tanous                 {
19681da66f75SEd Tanous                     if (ec.value() ==
19691da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
19701da66f75SEd Tanous                     {
1971f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
19721da66f75SEd Tanous                     }
1973*4363d3b2SJason M. Bills                     else if (ec.value() ==
1974*4363d3b2SJason M. Bills                              boost::system::errc::device_or_resource_busy)
1975*4363d3b2SJason M. Bills                     {
1976*4363d3b2SJason M. Bills                         messages::serviceTemporarilyUnavailable(asyncResp->res,
1977*4363d3b2SJason M. Bills                                                                 "60");
1978*4363d3b2SJason M. Bills                     }
19791da66f75SEd Tanous                     else
19801da66f75SEd Tanous                     {
1981f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
19821da66f75SEd Tanous                     }
1983271584abSEd Tanous 
1984271584abSEd Tanous                     timeout.cancel();
198548e4639eSJason M. Bills                     onDemandLogMatcher = nullptr;
19861da66f75SEd Tanous                     return;
19871da66f75SEd Tanous                 }
19881da66f75SEd Tanous             };
19891da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
19905b61b5e8SJason M. Bills             std::move(generateonDemandLogCallback), crashdumpObject,
19915b61b5e8SJason M. Bills             crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
19921da66f75SEd Tanous     }
19931da66f75SEd Tanous };
19941da66f75SEd Tanous 
1995e1f26343SJason M. Bills class SendRawPECI : public Node
19961da66f75SEd Tanous {
19971da66f75SEd Tanous   public:
1998e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
1999424c4176SJason M. Bills         Node(app,
2000424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2001424c4176SJason M. Bills              "Crashdump.SendRawPeci/")
20021da66f75SEd Tanous     {
20031da66f75SEd Tanous         entityPrivileges = {
20041da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
20051da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
20061da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
20071da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
20081da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
20091da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
20101da66f75SEd Tanous     }
20111da66f75SEd Tanous 
20121da66f75SEd Tanous   private:
20131da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
20141da66f75SEd Tanous                 const std::vector<std::string> &params) override
20151da66f75SEd Tanous     {
2016e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2017b1556427SEd Tanous         uint8_t clientAddress = 0;
2018b1556427SEd Tanous         uint8_t readLength = 0;
20191da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
2020b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2021b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
2022b1556427SEd Tanous                                  peciCommand))
20231da66f75SEd Tanous         {
20241da66f75SEd Tanous             return;
20251da66f75SEd Tanous         }
2026b1556427SEd Tanous 
20271da66f75SEd Tanous         // Callback to return the Raw PECI response
2028e1f26343SJason M. Bills         auto sendRawPECICallback =
2029e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
20301da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
20311da66f75SEd Tanous                 if (ec)
20321da66f75SEd Tanous                 {
20331da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
20341da66f75SEd Tanous                                      << ec.message();
2035f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
20361da66f75SEd Tanous                     return;
20371da66f75SEd Tanous                 }
2038e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
20391da66f75SEd Tanous                                             {"PECIResponse", resp}};
20401da66f75SEd Tanous             };
20411da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
20421da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
20435b61b5e8SJason M. Bills             std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
20445b61b5e8SJason M. Bills             crashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
20454ed77cd5SEd Tanous             peciCommand);
20461da66f75SEd Tanous     }
20471da66f75SEd Tanous };
20481da66f75SEd Tanous 
2049cb92c03bSAndrew Geissler /**
2050cb92c03bSAndrew Geissler  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2051cb92c03bSAndrew Geissler  */
2052cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node
2053cb92c03bSAndrew Geissler {
2054cb92c03bSAndrew Geissler   public:
2055cb92c03bSAndrew Geissler     DBusLogServiceActionsClear(CrowApp &app) :
2056cb92c03bSAndrew Geissler         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
20571f56a3a6STim Lee                   "LogService.ClearLog")
2058cb92c03bSAndrew Geissler     {
2059cb92c03bSAndrew Geissler         entityPrivileges = {
2060cb92c03bSAndrew Geissler             {boost::beast::http::verb::get, {{"Login"}}},
2061cb92c03bSAndrew Geissler             {boost::beast::http::verb::head, {{"Login"}}},
2062cb92c03bSAndrew Geissler             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2063cb92c03bSAndrew Geissler             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2064cb92c03bSAndrew Geissler             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2065cb92c03bSAndrew Geissler             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2066cb92c03bSAndrew Geissler     }
2067cb92c03bSAndrew Geissler 
2068cb92c03bSAndrew Geissler   private:
2069cb92c03bSAndrew Geissler     /**
2070cb92c03bSAndrew Geissler      * Function handles POST method request.
2071cb92c03bSAndrew Geissler      * The Clear Log actions does not require any parameter.The action deletes
2072cb92c03bSAndrew Geissler      * all entries found in the Entries collection for this Log Service.
2073cb92c03bSAndrew Geissler      */
2074cb92c03bSAndrew Geissler     void doPost(crow::Response &res, const crow::Request &req,
2075cb92c03bSAndrew Geissler                 const std::vector<std::string> &params) override
2076cb92c03bSAndrew Geissler     {
2077cb92c03bSAndrew Geissler         BMCWEB_LOG_DEBUG << "Do delete all entries.";
2078cb92c03bSAndrew Geissler 
2079cb92c03bSAndrew Geissler         auto asyncResp = std::make_shared<AsyncResp>(res);
2080cb92c03bSAndrew Geissler         // Process response from Logging service.
2081cb92c03bSAndrew Geissler         auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2082cb92c03bSAndrew Geissler             BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2083cb92c03bSAndrew Geissler             if (ec)
2084cb92c03bSAndrew Geissler             {
2085cb92c03bSAndrew Geissler                 // TODO Handle for specific error code
2086cb92c03bSAndrew Geissler                 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2087cb92c03bSAndrew Geissler                 asyncResp->res.result(
2088cb92c03bSAndrew Geissler                     boost::beast::http::status::internal_server_error);
2089cb92c03bSAndrew Geissler                 return;
2090cb92c03bSAndrew Geissler             }
2091cb92c03bSAndrew Geissler 
2092cb92c03bSAndrew Geissler             asyncResp->res.result(boost::beast::http::status::no_content);
2093cb92c03bSAndrew Geissler         };
2094cb92c03bSAndrew Geissler 
2095cb92c03bSAndrew Geissler         // Make call to Logging service to request Clear Log
2096cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
2097cb92c03bSAndrew Geissler             resp_handler, "xyz.openbmc_project.Logging",
2098cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging",
2099cb92c03bSAndrew Geissler             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2100cb92c03bSAndrew Geissler     }
2101cb92c03bSAndrew Geissler };
21021da66f75SEd Tanous } // namespace redfish
2103