xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 271584ab78b4c1926f766aa26ddfde7da329059f)
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>
28cb92c03bSAndrew Geissler #include <error_messages.hpp>
294418c7f0SJames Feist #include <filesystem>
30cd225da8SJason M. Bills #include <string_view>
31abf2add6SEd Tanous #include <variant>
321da66f75SEd Tanous 
331da66f75SEd Tanous namespace redfish
341da66f75SEd Tanous {
351da66f75SEd Tanous 
36424c4176SJason M. Bills constexpr char const *CrashdumpObject = "com.intel.crashdump";
37424c4176SJason M. Bills constexpr char const *CrashdumpPath = "/com/intel/crashdump";
38424c4176SJason M. Bills constexpr char const *CrashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
39424c4176SJason M. Bills constexpr char const *CrashdumpInterface = "com.intel.crashdump";
40424c4176SJason M. Bills constexpr char const *CrashdumpOnDemandInterface =
41424c4176SJason M. Bills     "com.intel.crashdump.OnDemand";
42424c4176SJason M. Bills constexpr char const *CrashdumpRawPECIInterface =
43424c4176SJason M. Bills     "com.intel.crashdump.SendRawPeci";
441da66f75SEd Tanous 
454851d45dSJason M. Bills namespace message_registries
464851d45dSJason M. Bills {
474851d45dSJason M. Bills static const Message *getMessageFromRegistry(
484851d45dSJason M. Bills     const std::string &messageKey,
494851d45dSJason M. Bills     const boost::beast::span<const MessageEntry> registry)
504851d45dSJason M. Bills {
514851d45dSJason M. Bills     boost::beast::span<const MessageEntry>::const_iterator messageIt =
524851d45dSJason M. Bills         std::find_if(registry.cbegin(), registry.cend(),
534851d45dSJason M. Bills                      [&messageKey](const MessageEntry &messageEntry) {
544851d45dSJason M. Bills                          return !std::strcmp(messageEntry.first,
554851d45dSJason M. Bills                                              messageKey.c_str());
564851d45dSJason M. Bills                      });
574851d45dSJason M. Bills     if (messageIt != registry.cend())
584851d45dSJason M. Bills     {
594851d45dSJason M. Bills         return &messageIt->second;
604851d45dSJason M. Bills     }
614851d45dSJason M. Bills 
624851d45dSJason M. Bills     return nullptr;
634851d45dSJason M. Bills }
644851d45dSJason M. Bills 
654851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID)
664851d45dSJason M. Bills {
674851d45dSJason M. Bills     // Redfish MessageIds are in the form
684851d45dSJason M. Bills     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
694851d45dSJason M. Bills     // the right Message
704851d45dSJason M. Bills     std::vector<std::string> fields;
714851d45dSJason M. Bills     fields.reserve(4);
724851d45dSJason M. Bills     boost::split(fields, messageID, boost::is_any_of("."));
734851d45dSJason M. Bills     std::string &registryName = fields[0];
744851d45dSJason M. Bills     std::string &messageKey = fields[3];
754851d45dSJason M. Bills 
764851d45dSJason M. Bills     // Find the right registry and check it for the MessageKey
774851d45dSJason M. Bills     if (std::string(base::header.registryPrefix) == registryName)
784851d45dSJason M. Bills     {
794851d45dSJason M. Bills         return getMessageFromRegistry(
804851d45dSJason M. Bills             messageKey, boost::beast::span<const MessageEntry>(base::registry));
814851d45dSJason M. Bills     }
824851d45dSJason M. Bills     if (std::string(openbmc::header.registryPrefix) == registryName)
834851d45dSJason M. Bills     {
844851d45dSJason M. Bills         return getMessageFromRegistry(
854851d45dSJason M. Bills             messageKey,
864851d45dSJason M. Bills             boost::beast::span<const MessageEntry>(openbmc::registry));
874851d45dSJason M. Bills     }
884851d45dSJason M. Bills     return nullptr;
894851d45dSJason M. Bills }
904851d45dSJason M. Bills } // namespace message_registries
914851d45dSJason M. Bills 
92f6150403SJames Feist namespace fs = std::filesystem;
931da66f75SEd Tanous 
94cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map<
95cb92c03bSAndrew Geissler     std::string,
96cb92c03bSAndrew Geissler     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
97cb92c03bSAndrew Geissler                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
98cb92c03bSAndrew Geissler 
99cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map<
100cb92c03bSAndrew Geissler     sdbusplus::message::object_path,
101cb92c03bSAndrew Geissler     boost::container::flat_map<std::string, GetManagedPropertyType>>;
102cb92c03bSAndrew Geissler 
103cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s)
104cb92c03bSAndrew Geissler {
105cb92c03bSAndrew Geissler     if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
106cb92c03bSAndrew Geissler     {
107cb92c03bSAndrew Geissler         return "Critical";
108cb92c03bSAndrew Geissler     }
109cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
110cb92c03bSAndrew Geissler     {
111cb92c03bSAndrew Geissler         return "Critical";
112cb92c03bSAndrew Geissler     }
113cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
114cb92c03bSAndrew Geissler     {
115cb92c03bSAndrew Geissler         return "OK";
116cb92c03bSAndrew Geissler     }
117cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
118cb92c03bSAndrew Geissler     {
119cb92c03bSAndrew Geissler         return "Critical";
120cb92c03bSAndrew Geissler     }
121cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
122cb92c03bSAndrew Geissler     {
123cb92c03bSAndrew Geissler         return "Critical";
124cb92c03bSAndrew Geissler     }
125cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
126cb92c03bSAndrew Geissler     {
127cb92c03bSAndrew Geissler         return "OK";
128cb92c03bSAndrew Geissler     }
129cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
130cb92c03bSAndrew Geissler     {
131cb92c03bSAndrew Geissler         return "OK";
132cb92c03bSAndrew Geissler     }
133cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
134cb92c03bSAndrew Geissler     {
135cb92c03bSAndrew Geissler         return "Warning";
136cb92c03bSAndrew Geissler     }
137cb92c03bSAndrew Geissler     return "";
138cb92c03bSAndrew Geissler }
139cb92c03bSAndrew Geissler 
14016428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
14139e77504SEd Tanous                               const std::string_view &field,
14239e77504SEd Tanous                               std::string_view &contents)
14316428a1aSJason M. Bills {
14416428a1aSJason M. Bills     const char *data = nullptr;
14516428a1aSJason M. Bills     size_t length = 0;
14616428a1aSJason M. Bills     int ret = 0;
14716428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
148*271584abSEd Tanous     ret = sd_journal_get_data(journal, field.data(),
149*271584abSEd Tanous                               reinterpret_cast<const void **>(&data), &length);
15016428a1aSJason M. Bills     if (ret < 0)
15116428a1aSJason M. Bills     {
15216428a1aSJason M. Bills         return ret;
15316428a1aSJason M. Bills     }
15439e77504SEd Tanous     contents = std::string_view(data, length);
15516428a1aSJason M. Bills     // Only use the content after the "=" character.
15616428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
15716428a1aSJason M. Bills     return ret;
15816428a1aSJason M. Bills }
15916428a1aSJason M. Bills 
16016428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
16139e77504SEd Tanous                               const std::string_view &field, const int &base,
162*271584abSEd Tanous                               long int &contents)
16316428a1aSJason M. Bills {
16416428a1aSJason M. Bills     int ret = 0;
16539e77504SEd Tanous     std::string_view metadata;
16616428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
16716428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
16816428a1aSJason M. Bills     if (ret < 0)
16916428a1aSJason M. Bills     {
17016428a1aSJason M. Bills         return ret;
17116428a1aSJason M. Bills     }
172b01bf299SEd Tanous     contents = strtol(metadata.data(), nullptr, base);
17316428a1aSJason M. Bills     return ret;
17416428a1aSJason M. Bills }
17516428a1aSJason M. Bills 
17616428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
17716428a1aSJason M. Bills {
17816428a1aSJason M. Bills     int ret = 0;
17916428a1aSJason M. Bills     uint64_t timestamp = 0;
18016428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
18116428a1aSJason M. Bills     if (ret < 0)
18216428a1aSJason M. Bills     {
18316428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
18416428a1aSJason M. Bills                          << strerror(-ret);
18516428a1aSJason M. Bills         return false;
18616428a1aSJason M. Bills     }
18716428a1aSJason M. Bills     time_t t =
18816428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
18916428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
19016428a1aSJason M. Bills     char entryTime[64] = {};
19116428a1aSJason M. Bills     if (NULL != loctime)
19216428a1aSJason M. Bills     {
19316428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
19416428a1aSJason M. Bills     }
19516428a1aSJason M. Bills     // Insert the ':' into the timezone
19639e77504SEd Tanous     std::string_view t1(entryTime);
19739e77504SEd Tanous     std::string_view t2(entryTime);
19816428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
19916428a1aSJason M. Bills     {
20016428a1aSJason M. Bills         t1.remove_suffix(2);
20116428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
20216428a1aSJason M. Bills     }
20339e77504SEd Tanous     entryTimestamp = std::string(t1) + ":" + std::string(t2);
20416428a1aSJason M. Bills     return true;
20516428a1aSJason M. Bills }
20616428a1aSJason M. Bills 
20716428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
208*271584abSEd Tanous                          uint64_t &skip)
20916428a1aSJason M. Bills {
21016428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
21116428a1aSJason M. Bills     if (skipParam != nullptr)
21216428a1aSJason M. Bills     {
21316428a1aSJason M. Bills         char *ptr = nullptr;
214*271584abSEd Tanous         skip = std::strtoul(skipParam, &ptr, 10);
21516428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
21616428a1aSJason M. Bills         {
21716428a1aSJason M. Bills 
21816428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
21916428a1aSJason M. Bills                                                    "$skip");
22016428a1aSJason M. Bills             return false;
22116428a1aSJason M. Bills         }
22216428a1aSJason M. Bills     }
22316428a1aSJason M. Bills     return true;
22416428a1aSJason M. Bills }
22516428a1aSJason M. Bills 
226*271584abSEd Tanous static constexpr const uint64_t maxEntriesPerPage = 1000;
22716428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
228*271584abSEd Tanous                         uint64_t &top)
22916428a1aSJason M. Bills {
23016428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
23116428a1aSJason M. Bills     if (topParam != nullptr)
23216428a1aSJason M. Bills     {
23316428a1aSJason M. Bills         char *ptr = nullptr;
234*271584abSEd Tanous         top = std::strtoul(topParam, &ptr, 10);
23516428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
23616428a1aSJason M. Bills         {
23716428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
23816428a1aSJason M. Bills                                                    "$top");
23916428a1aSJason M. Bills             return false;
24016428a1aSJason M. Bills         }
241*271584abSEd Tanous         if (top < 1U || top > maxEntriesPerPage)
24216428a1aSJason M. Bills         {
24316428a1aSJason M. Bills 
24416428a1aSJason M. Bills             messages::queryParameterOutOfRange(
24516428a1aSJason M. Bills                 res, std::to_string(top), "$top",
24616428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
24716428a1aSJason M. Bills             return false;
24816428a1aSJason M. Bills         }
24916428a1aSJason M. Bills     }
25016428a1aSJason M. Bills     return true;
25116428a1aSJason M. Bills }
25216428a1aSJason M. Bills 
253e85d6b16SJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
254e85d6b16SJason M. Bills                              const bool firstEntry = true)
25516428a1aSJason M. Bills {
25616428a1aSJason M. Bills     int ret = 0;
25716428a1aSJason M. Bills     static uint64_t prevTs = 0;
25816428a1aSJason M. Bills     static int index = 0;
259e85d6b16SJason M. Bills     if (firstEntry)
260e85d6b16SJason M. Bills     {
261e85d6b16SJason M. Bills         prevTs = 0;
262e85d6b16SJason M. Bills     }
263e85d6b16SJason M. Bills 
26416428a1aSJason M. Bills     // Get the entry timestamp
26516428a1aSJason M. Bills     uint64_t curTs = 0;
26616428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &curTs);
26716428a1aSJason M. Bills     if (ret < 0)
26816428a1aSJason M. Bills     {
26916428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
27016428a1aSJason M. Bills                          << strerror(-ret);
27116428a1aSJason M. Bills         return false;
27216428a1aSJason M. Bills     }
27316428a1aSJason M. Bills     // If the timestamp isn't unique, increment the index
27416428a1aSJason M. Bills     if (curTs == prevTs)
27516428a1aSJason M. Bills     {
27616428a1aSJason M. Bills         index++;
27716428a1aSJason M. Bills     }
27816428a1aSJason M. Bills     else
27916428a1aSJason M. Bills     {
28016428a1aSJason M. Bills         // Otherwise, reset it
28116428a1aSJason M. Bills         index = 0;
28216428a1aSJason M. Bills     }
28316428a1aSJason M. Bills     // Save the timestamp
28416428a1aSJason M. Bills     prevTs = curTs;
28516428a1aSJason M. Bills 
28616428a1aSJason M. Bills     entryID = std::to_string(curTs);
28716428a1aSJason M. Bills     if (index > 0)
28816428a1aSJason M. Bills     {
28916428a1aSJason M. Bills         entryID += "_" + std::to_string(index);
29016428a1aSJason M. Bills     }
29116428a1aSJason M. Bills     return true;
29216428a1aSJason M. Bills }
29316428a1aSJason M. Bills 
294e85d6b16SJason M. Bills static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
295e85d6b16SJason M. Bills                              const bool firstEntry = true)
29695820184SJason M. Bills {
297*271584abSEd Tanous     static time_t prevTs = 0;
29895820184SJason M. Bills     static int index = 0;
299e85d6b16SJason M. Bills     if (firstEntry)
300e85d6b16SJason M. Bills     {
301e85d6b16SJason M. Bills         prevTs = 0;
302e85d6b16SJason M. Bills     }
303e85d6b16SJason M. Bills 
30495820184SJason M. Bills     // Get the entry timestamp
305*271584abSEd Tanous     std::time_t curTs = 0;
30695820184SJason M. Bills     std::tm timeStruct = {};
30795820184SJason M. Bills     std::istringstream entryStream(logEntry);
30895820184SJason M. Bills     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
30995820184SJason M. Bills     {
31095820184SJason M. Bills         curTs = std::mktime(&timeStruct);
31195820184SJason M. Bills     }
31295820184SJason M. Bills     // If the timestamp isn't unique, increment the index
31395820184SJason M. Bills     if (curTs == prevTs)
31495820184SJason M. Bills     {
31595820184SJason M. Bills         index++;
31695820184SJason M. Bills     }
31795820184SJason M. Bills     else
31895820184SJason M. Bills     {
31995820184SJason M. Bills         // Otherwise, reset it
32095820184SJason M. Bills         index = 0;
32195820184SJason M. Bills     }
32295820184SJason M. Bills     // Save the timestamp
32395820184SJason M. Bills     prevTs = curTs;
32495820184SJason M. Bills 
32595820184SJason M. Bills     entryID = std::to_string(curTs);
32695820184SJason M. Bills     if (index > 0)
32795820184SJason M. Bills     {
32895820184SJason M. Bills         entryID += "_" + std::to_string(index);
32995820184SJason M. Bills     }
33095820184SJason M. Bills     return true;
33195820184SJason M. Bills }
33295820184SJason M. Bills 
33316428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
334*271584abSEd Tanous                                uint64_t &timestamp, uint64_t &index)
33516428a1aSJason M. Bills {
33616428a1aSJason M. Bills     if (entryID.empty())
33716428a1aSJason M. Bills     {
33816428a1aSJason M. Bills         return false;
33916428a1aSJason M. Bills     }
34016428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
34139e77504SEd Tanous     std::string_view tsStr(entryID);
34216428a1aSJason M. Bills 
34316428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
34416428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
34516428a1aSJason M. Bills     {
34616428a1aSJason M. Bills         // Timestamp has an index
34716428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
34839e77504SEd Tanous         std::string_view indexStr(entryID);
34916428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
35016428a1aSJason M. Bills         std::size_t pos;
35116428a1aSJason M. Bills         try
35216428a1aSJason M. Bills         {
35339e77504SEd Tanous             index = std::stoul(std::string(indexStr), &pos);
35416428a1aSJason M. Bills         }
355*271584abSEd Tanous         catch (std::invalid_argument &)
35616428a1aSJason M. Bills         {
35716428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
35816428a1aSJason M. Bills             return false;
35916428a1aSJason M. Bills         }
360*271584abSEd Tanous         catch (std::out_of_range &)
36116428a1aSJason M. Bills         {
36216428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
36316428a1aSJason M. Bills             return false;
36416428a1aSJason M. Bills         }
36516428a1aSJason M. Bills         if (pos != indexStr.size())
36616428a1aSJason M. Bills         {
36716428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
36816428a1aSJason M. Bills             return false;
36916428a1aSJason M. Bills         }
37016428a1aSJason M. Bills     }
37116428a1aSJason M. Bills     // Timestamp has no index
37216428a1aSJason M. Bills     std::size_t pos;
37316428a1aSJason M. Bills     try
37416428a1aSJason M. Bills     {
37539e77504SEd Tanous         timestamp = std::stoull(std::string(tsStr), &pos);
37616428a1aSJason M. Bills     }
377*271584abSEd Tanous     catch (std::invalid_argument &)
37816428a1aSJason M. Bills     {
37916428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
38016428a1aSJason M. Bills         return false;
38116428a1aSJason M. Bills     }
382*271584abSEd Tanous     catch (std::out_of_range &)
38316428a1aSJason M. Bills     {
38416428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
38516428a1aSJason M. Bills         return false;
38616428a1aSJason M. Bills     }
38716428a1aSJason M. Bills     if (pos != tsStr.size())
38816428a1aSJason M. Bills     {
38916428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
39016428a1aSJason M. Bills         return false;
39116428a1aSJason M. Bills     }
39216428a1aSJason M. Bills     return true;
39316428a1aSJason M. Bills }
39416428a1aSJason M. Bills 
39595820184SJason M. Bills static bool
39695820184SJason M. Bills     getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
39795820184SJason M. Bills {
39895820184SJason M. Bills     static const std::filesystem::path redfishLogDir = "/var/log";
39995820184SJason M. Bills     static const std::string redfishLogFilename = "redfish";
40095820184SJason M. Bills 
40195820184SJason M. Bills     // Loop through the directory looking for redfish log files
40295820184SJason M. Bills     for (const std::filesystem::directory_entry &dirEnt :
40395820184SJason M. Bills          std::filesystem::directory_iterator(redfishLogDir))
40495820184SJason M. Bills     {
40595820184SJason M. Bills         // If we find a redfish log file, save the path
40695820184SJason M. Bills         std::string filename = dirEnt.path().filename();
40795820184SJason M. Bills         if (boost::starts_with(filename, redfishLogFilename))
40895820184SJason M. Bills         {
40995820184SJason M. Bills             redfishLogFiles.emplace_back(redfishLogDir / filename);
41095820184SJason M. Bills         }
41195820184SJason M. Bills     }
41295820184SJason M. Bills     // As the log files rotate, they are appended with a ".#" that is higher for
41395820184SJason M. Bills     // the older logs. Since we don't expect more than 10 log files, we
41495820184SJason M. Bills     // can just sort the list to get them in order from newest to oldest
41595820184SJason M. Bills     std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
41695820184SJason M. Bills 
41795820184SJason M. Bills     return !redfishLogFiles.empty();
41895820184SJason M. Bills }
41995820184SJason M. Bills 
420c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
4211da66f75SEd Tanous {
4221da66f75SEd Tanous   public:
4231da66f75SEd Tanous     template <typename CrowApp>
424c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
425029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/")
426c4bf6374SJason M. Bills     {
427c4bf6374SJason M. Bills         entityPrivileges = {
428c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
429c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
430c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
431c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
432c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
433c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
434c4bf6374SJason M. Bills     }
435c4bf6374SJason M. Bills 
436c4bf6374SJason M. Bills   private:
437c4bf6374SJason M. Bills     /**
438c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
439c4bf6374SJason M. Bills      */
440c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
441c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
442c4bf6374SJason M. Bills     {
443c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
444c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
445c4bf6374SJason M. Bills         // it has a duplicate entry for members
446c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
447c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
448c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
449c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
450c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
451029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices";
452c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
453c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
454c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
455c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
456c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
457029573d4SEd Tanous         logServiceArray.push_back(
458029573d4SEd Tanous             {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
459d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
460d53dd41fSJason M. Bills         logServiceArray.push_back(
461cb92c03bSAndrew Geissler             {{"@odata.id",
462424c4176SJason M. Bills               "/redfish/v1/Systems/system/LogServices/Crashdump"}});
463d53dd41fSJason M. Bills #endif
464c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
465c4bf6374SJason M. Bills             logServiceArray.size();
466c4bf6374SJason M. Bills     }
467c4bf6374SJason M. Bills };
468c4bf6374SJason M. Bills 
469c4bf6374SJason M. Bills class EventLogService : public Node
470c4bf6374SJason M. Bills {
471c4bf6374SJason M. Bills   public:
472c4bf6374SJason M. Bills     template <typename CrowApp>
473c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
474029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
475c4bf6374SJason M. Bills     {
476c4bf6374SJason M. Bills         entityPrivileges = {
477c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
478c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
479c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
480c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
481c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
482c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
483c4bf6374SJason M. Bills     }
484c4bf6374SJason M. Bills 
485c4bf6374SJason M. Bills   private:
486c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
487c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
488c4bf6374SJason M. Bills     {
489c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
490c4bf6374SJason M. Bills 
491c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
492029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog";
493c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
494c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
495c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
496c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
497c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
498c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
499c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
500c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
501c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
502c4bf6374SJason M. Bills             {"@odata.id",
503029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
504e7d6c8b2SGunnar Mills         asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
505e7d6c8b2SGunnar Mills 
506e7d6c8b2SGunnar Mills             {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
507e7d6c8b2SGunnar Mills                        "Actions/LogService.ClearLog"}};
508489640c6SJason M. Bills     }
509489640c6SJason M. Bills };
510489640c6SJason M. Bills 
511489640c6SJason M. Bills class EventLogClear : public Node
512489640c6SJason M. Bills {
513489640c6SJason M. Bills   public:
514489640c6SJason M. Bills     EventLogClear(CrowApp &app) :
515489640c6SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
516489640c6SJason M. Bills                   "LogService.ClearLog/")
517489640c6SJason M. Bills     {
518489640c6SJason M. Bills         entityPrivileges = {
519489640c6SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
520489640c6SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
521489640c6SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
522489640c6SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
523489640c6SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
524489640c6SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
525489640c6SJason M. Bills     }
526489640c6SJason M. Bills 
527489640c6SJason M. Bills   private:
528489640c6SJason M. Bills     void doPost(crow::Response &res, const crow::Request &req,
529489640c6SJason M. Bills                 const std::vector<std::string> &params) override
530489640c6SJason M. Bills     {
531489640c6SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
532489640c6SJason M. Bills 
533489640c6SJason M. Bills         // Clear the EventLog by deleting the log files
534489640c6SJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
535489640c6SJason M. Bills         if (getRedfishLogFiles(redfishLogFiles))
536489640c6SJason M. Bills         {
537489640c6SJason M. Bills             for (const std::filesystem::path &file : redfishLogFiles)
538489640c6SJason M. Bills             {
539489640c6SJason M. Bills                 std::error_code ec;
540489640c6SJason M. Bills                 std::filesystem::remove(file, ec);
541489640c6SJason M. Bills             }
542489640c6SJason M. Bills         }
543489640c6SJason M. Bills 
544489640c6SJason M. Bills         // Reload rsyslog so it knows to start new log files
545489640c6SJason M. Bills         crow::connections::systemBus->async_method_call(
546489640c6SJason M. Bills             [asyncResp](const boost::system::error_code ec) {
547489640c6SJason M. Bills                 if (ec)
548489640c6SJason M. Bills                 {
549489640c6SJason M. Bills                     BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
550489640c6SJason M. Bills                     messages::internalError(asyncResp->res);
551489640c6SJason M. Bills                     return;
552489640c6SJason M. Bills                 }
553489640c6SJason M. Bills 
554489640c6SJason M. Bills                 messages::success(asyncResp->res);
555489640c6SJason M. Bills             },
556489640c6SJason M. Bills             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
557489640c6SJason M. Bills             "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
558489640c6SJason M. Bills             "replace");
559c4bf6374SJason M. Bills     }
560c4bf6374SJason M. Bills };
561c4bf6374SJason M. Bills 
56295820184SJason M. Bills static int fillEventLogEntryJson(const std::string &logEntryID,
56395820184SJason M. Bills                                  const std::string logEntry,
56495820184SJason M. Bills                                  nlohmann::json &logEntryJson)
565c4bf6374SJason M. Bills {
56695820184SJason M. Bills     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
567cd225da8SJason M. Bills     // First get the Timestamp
568cd225da8SJason M. Bills     size_t space = logEntry.find_first_of(" ");
569cd225da8SJason M. Bills     if (space == std::string::npos)
57095820184SJason M. Bills     {
57195820184SJason M. Bills         return 1;
57295820184SJason M. Bills     }
573cd225da8SJason M. Bills     std::string timestamp = logEntry.substr(0, space);
574cd225da8SJason M. Bills     // Then get the log contents
575cd225da8SJason M. Bills     size_t entryStart = logEntry.find_first_not_of(" ", space);
576cd225da8SJason M. Bills     if (entryStart == std::string::npos)
577cd225da8SJason M. Bills     {
578cd225da8SJason M. Bills         return 1;
579cd225da8SJason M. Bills     }
580cd225da8SJason M. Bills     std::string_view entry(logEntry);
581cd225da8SJason M. Bills     entry.remove_prefix(entryStart);
582cd225da8SJason M. Bills     // Use split to separate the entry into its fields
583cd225da8SJason M. Bills     std::vector<std::string> logEntryFields;
584cd225da8SJason M. Bills     boost::split(logEntryFields, entry, boost::is_any_of(","),
585cd225da8SJason M. Bills                  boost::token_compress_on);
586cd225da8SJason M. Bills     // We need at least a MessageId to be valid
587cd225da8SJason M. Bills     if (logEntryFields.size() < 1)
588cd225da8SJason M. Bills     {
589cd225da8SJason M. Bills         return 1;
590cd225da8SJason M. Bills     }
591cd225da8SJason M. Bills     std::string &messageID = logEntryFields[0];
59295820184SJason M. Bills 
5934851d45dSJason M. Bills     // Get the Message from the MessageRegistry
5944851d45dSJason M. Bills     const message_registries::Message *message =
5954851d45dSJason M. Bills         message_registries::getMessage(messageID);
596c4bf6374SJason M. Bills 
5974851d45dSJason M. Bills     std::string msg;
5984851d45dSJason M. Bills     std::string severity;
5994851d45dSJason M. Bills     if (message != nullptr)
600c4bf6374SJason M. Bills     {
6014851d45dSJason M. Bills         msg = message->message;
6024851d45dSJason M. Bills         severity = message->severity;
603c4bf6374SJason M. Bills     }
604c4bf6374SJason M. Bills 
60515a86ff6SJason M. Bills     // Get the MessageArgs from the log if there are any
60615a86ff6SJason M. Bills     boost::beast::span<std::string> messageArgs;
60715a86ff6SJason M. Bills     if (logEntryFields.size() > 1)
60815a86ff6SJason M. Bills     {
60915a86ff6SJason M. Bills         std::string &messageArgsStart = logEntryFields[1];
61015a86ff6SJason M. Bills         // If the first string is empty, assume there are no MessageArgs
61115a86ff6SJason M. Bills         std::size_t messageArgsSize = 0;
61215a86ff6SJason M. Bills         if (!messageArgsStart.empty())
61315a86ff6SJason M. Bills         {
61415a86ff6SJason M. Bills             messageArgsSize = logEntryFields.size() - 1;
61515a86ff6SJason M. Bills         }
61615a86ff6SJason M. Bills 
61715a86ff6SJason M. Bills         messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
618c4bf6374SJason M. Bills 
6194851d45dSJason M. Bills         // Fill the MessageArgs into the Message
62095820184SJason M. Bills         int i = 0;
62195820184SJason M. Bills         for (const std::string &messageArg : messageArgs)
6224851d45dSJason M. Bills         {
62395820184SJason M. Bills             std::string argStr = "%" + std::to_string(++i);
6244851d45dSJason M. Bills             size_t argPos = msg.find(argStr);
6254851d45dSJason M. Bills             if (argPos != std::string::npos)
6264851d45dSJason M. Bills             {
62795820184SJason M. Bills                 msg.replace(argPos, argStr.length(), messageArg);
6284851d45dSJason M. Bills             }
6294851d45dSJason M. Bills         }
63015a86ff6SJason M. Bills     }
6314851d45dSJason M. Bills 
63295820184SJason M. Bills     // Get the Created time from the timestamp. The log timestamp is in RFC3339
63395820184SJason M. Bills     // format which matches the Redfish format except for the fractional seconds
63495820184SJason M. Bills     // between the '.' and the '+', so just remove them.
63595820184SJason M. Bills     std::size_t dot = timestamp.find_first_of(".");
63695820184SJason M. Bills     std::size_t plus = timestamp.find_first_of("+");
63795820184SJason M. Bills     if (dot != std::string::npos && plus != std::string::npos)
638c4bf6374SJason M. Bills     {
63995820184SJason M. Bills         timestamp.erase(dot, plus - dot);
640c4bf6374SJason M. Bills     }
641c4bf6374SJason M. Bills 
642c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
64395820184SJason M. Bills     logEntryJson = {
644cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
645c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
646029573d4SEd Tanous         {"@odata.id",
647897967deSJason M. Bills          "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
64895820184SJason M. Bills              logEntryID},
649c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
65095820184SJason M. Bills         {"Id", logEntryID},
65195820184SJason M. Bills         {"Message", std::move(msg)},
65295820184SJason M. Bills         {"MessageId", std::move(messageID)},
653c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
654c4bf6374SJason M. Bills         {"EntryType", "Event"},
65595820184SJason M. Bills         {"Severity", std::move(severity)},
65695820184SJason M. Bills         {"Created", std::move(timestamp)}};
657c4bf6374SJason M. Bills     return 0;
658c4bf6374SJason M. Bills }
659c4bf6374SJason M. Bills 
66027062605SAnthony Wilson class JournalEventLogEntryCollection : public Node
661c4bf6374SJason M. Bills {
662c4bf6374SJason M. Bills   public:
663c4bf6374SJason M. Bills     template <typename CrowApp>
66427062605SAnthony Wilson     JournalEventLogEntryCollection(CrowApp &app) :
665029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
666c4bf6374SJason M. Bills     {
667c4bf6374SJason M. Bills         entityPrivileges = {
668c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
669c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
670c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
671c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
672c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
673c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
674c4bf6374SJason M. Bills     }
675c4bf6374SJason M. Bills 
676c4bf6374SJason M. Bills   private:
677c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
678c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
679c4bf6374SJason M. Bills     {
680c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
681*271584abSEd Tanous         uint64_t skip = 0;
682*271584abSEd Tanous         uint64_t top = maxEntriesPerPage; // Show max entries by default
683c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
684c4bf6374SJason M. Bills         {
685c4bf6374SJason M. Bills             return;
686c4bf6374SJason M. Bills         }
687c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
688c4bf6374SJason M. Bills         {
689c4bf6374SJason M. Bills             return;
690c4bf6374SJason M. Bills         }
691c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
692c4bf6374SJason M. Bills         // it has a duplicate entry for members
693c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
694c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
695c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
696c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
697c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
698029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
699c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
700c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
701c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
702cb92c03bSAndrew Geissler 
703c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
704c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
70595820184SJason M. Bills         // Go through the log files and create a unique ID for each entry
70695820184SJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
70795820184SJason M. Bills         getRedfishLogFiles(redfishLogFiles);
708b01bf299SEd Tanous         uint64_t entryCount = 0;
709cd225da8SJason M. Bills         std::string logEntry;
71095820184SJason M. Bills 
71195820184SJason M. Bills         // Oldest logs are in the last file, so start there and loop backwards
712cd225da8SJason M. Bills         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
713cd225da8SJason M. Bills              it++)
714c4bf6374SJason M. Bills         {
715cd225da8SJason M. Bills             std::ifstream logStream(*it);
71695820184SJason M. Bills             if (!logStream.is_open())
717c4bf6374SJason M. Bills             {
718c4bf6374SJason M. Bills                 continue;
719c4bf6374SJason M. Bills             }
720c4bf6374SJason M. Bills 
721e85d6b16SJason M. Bills             // Reset the unique ID on the first entry
722e85d6b16SJason M. Bills             bool firstEntry = true;
72395820184SJason M. Bills             while (std::getline(logStream, logEntry))
72495820184SJason M. Bills             {
725c4bf6374SJason M. Bills                 entryCount++;
726c4bf6374SJason M. Bills                 // Handle paging using skip (number of entries to skip from the
727c4bf6374SJason M. Bills                 // start) and top (number of entries to display)
728c4bf6374SJason M. Bills                 if (entryCount <= skip || entryCount > skip + top)
729c4bf6374SJason M. Bills                 {
730c4bf6374SJason M. Bills                     continue;
731c4bf6374SJason M. Bills                 }
732c4bf6374SJason M. Bills 
733c4bf6374SJason M. Bills                 std::string idStr;
734e85d6b16SJason M. Bills                 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
735c4bf6374SJason M. Bills                 {
736c4bf6374SJason M. Bills                     continue;
737c4bf6374SJason M. Bills                 }
738c4bf6374SJason M. Bills 
739e85d6b16SJason M. Bills                 if (firstEntry)
740e85d6b16SJason M. Bills                 {
741e85d6b16SJason M. Bills                     firstEntry = false;
742e85d6b16SJason M. Bills                 }
743e85d6b16SJason M. Bills 
744c4bf6374SJason M. Bills                 logEntryArray.push_back({});
745c4bf6374SJason M. Bills                 nlohmann::json &bmcLogEntry = logEntryArray.back();
74695820184SJason M. Bills                 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
747c4bf6374SJason M. Bills                 {
748c4bf6374SJason M. Bills                     messages::internalError(asyncResp->res);
749c4bf6374SJason M. Bills                     return;
750c4bf6374SJason M. Bills                 }
751c4bf6374SJason M. Bills             }
75295820184SJason M. Bills         }
753c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
754c4bf6374SJason M. Bills         if (skip + top < entryCount)
755c4bf6374SJason M. Bills         {
756c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
75795820184SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/EventLog/"
75895820184SJason M. Bills                 "Entries?$skip=" +
759c4bf6374SJason M. Bills                 std::to_string(skip + top);
760c4bf6374SJason M. Bills         }
76108a4e4b5SAnthony Wilson     }
76208a4e4b5SAnthony Wilson };
76308a4e4b5SAnthony Wilson 
764897967deSJason M. Bills class JournalEventLogEntry : public Node
765897967deSJason M. Bills {
766897967deSJason M. Bills   public:
767897967deSJason M. Bills     JournalEventLogEntry(CrowApp &app) :
768897967deSJason M. Bills         Node(app,
769897967deSJason M. Bills              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
770897967deSJason M. Bills              std::string())
771897967deSJason M. Bills     {
772897967deSJason M. Bills         entityPrivileges = {
773897967deSJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
774897967deSJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
775897967deSJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
776897967deSJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
777897967deSJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
778897967deSJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
779897967deSJason M. Bills     }
780897967deSJason M. Bills 
781897967deSJason M. Bills   private:
782897967deSJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
783897967deSJason M. Bills                const std::vector<std::string> &params) override
784897967deSJason M. Bills     {
785897967deSJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
786897967deSJason M. Bills         if (params.size() != 1)
787897967deSJason M. Bills         {
788897967deSJason M. Bills             messages::internalError(asyncResp->res);
789897967deSJason M. Bills             return;
790897967deSJason M. Bills         }
791897967deSJason M. Bills         const std::string &targetID = params[0];
792897967deSJason M. Bills 
793897967deSJason M. Bills         // Go through the log files and check the unique ID for each entry to
794897967deSJason M. Bills         // find the target entry
795897967deSJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
796897967deSJason M. Bills         getRedfishLogFiles(redfishLogFiles);
797897967deSJason M. Bills         std::string logEntry;
798897967deSJason M. Bills 
799897967deSJason M. Bills         // Oldest logs are in the last file, so start there and loop backwards
800897967deSJason M. Bills         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
801897967deSJason M. Bills              it++)
802897967deSJason M. Bills         {
803897967deSJason M. Bills             std::ifstream logStream(*it);
804897967deSJason M. Bills             if (!logStream.is_open())
805897967deSJason M. Bills             {
806897967deSJason M. Bills                 continue;
807897967deSJason M. Bills             }
808897967deSJason M. Bills 
809897967deSJason M. Bills             // Reset the unique ID on the first entry
810897967deSJason M. Bills             bool firstEntry = true;
811897967deSJason M. Bills             while (std::getline(logStream, logEntry))
812897967deSJason M. Bills             {
813897967deSJason M. Bills                 std::string idStr;
814897967deSJason M. Bills                 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
815897967deSJason M. Bills                 {
816897967deSJason M. Bills                     continue;
817897967deSJason M. Bills                 }
818897967deSJason M. Bills 
819897967deSJason M. Bills                 if (firstEntry)
820897967deSJason M. Bills                 {
821897967deSJason M. Bills                     firstEntry = false;
822897967deSJason M. Bills                 }
823897967deSJason M. Bills 
824897967deSJason M. Bills                 if (idStr == targetID)
825897967deSJason M. Bills                 {
826897967deSJason M. Bills                     if (fillEventLogEntryJson(idStr, logEntry,
827897967deSJason M. Bills                                               asyncResp->res.jsonValue) != 0)
828897967deSJason M. Bills                     {
829897967deSJason M. Bills                         messages::internalError(asyncResp->res);
830897967deSJason M. Bills                         return;
831897967deSJason M. Bills                     }
832897967deSJason M. Bills                     return;
833897967deSJason M. Bills                 }
834897967deSJason M. Bills             }
835897967deSJason M. Bills         }
836897967deSJason M. Bills         // Requested ID was not found
837897967deSJason M. Bills         messages::resourceMissingAtURI(asyncResp->res, targetID);
838897967deSJason M. Bills     }
839897967deSJason M. Bills };
840897967deSJason M. Bills 
84108a4e4b5SAnthony Wilson class DBusEventLogEntryCollection : public Node
84208a4e4b5SAnthony Wilson {
84308a4e4b5SAnthony Wilson   public:
84408a4e4b5SAnthony Wilson     template <typename CrowApp>
84508a4e4b5SAnthony Wilson     DBusEventLogEntryCollection(CrowApp &app) :
84608a4e4b5SAnthony Wilson         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
84708a4e4b5SAnthony Wilson     {
84808a4e4b5SAnthony Wilson         entityPrivileges = {
84908a4e4b5SAnthony Wilson             {boost::beast::http::verb::get, {{"Login"}}},
85008a4e4b5SAnthony Wilson             {boost::beast::http::verb::head, {{"Login"}}},
85108a4e4b5SAnthony Wilson             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
85208a4e4b5SAnthony Wilson             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
85308a4e4b5SAnthony Wilson             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
85408a4e4b5SAnthony Wilson             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
85508a4e4b5SAnthony Wilson     }
85608a4e4b5SAnthony Wilson 
85708a4e4b5SAnthony Wilson   private:
85808a4e4b5SAnthony Wilson     void doGet(crow::Response &res, const crow::Request &req,
85908a4e4b5SAnthony Wilson                const std::vector<std::string> &params) override
86008a4e4b5SAnthony Wilson     {
86108a4e4b5SAnthony Wilson         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
86208a4e4b5SAnthony Wilson 
86308a4e4b5SAnthony Wilson         // Collections don't include the static data added by SubRoute because
86408a4e4b5SAnthony Wilson         // it has a duplicate entry for members
86508a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["@odata.type"] =
86608a4e4b5SAnthony Wilson             "#LogEntryCollection.LogEntryCollection";
86708a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["@odata.context"] =
86808a4e4b5SAnthony Wilson             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
86908a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["@odata.id"] =
87008a4e4b5SAnthony Wilson             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
87108a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
87208a4e4b5SAnthony Wilson         asyncResp->res.jsonValue["Description"] =
87308a4e4b5SAnthony Wilson             "Collection of System Event Log Entries";
87408a4e4b5SAnthony Wilson 
875cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
876cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
877cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
878cb92c03bSAndrew Geissler             [asyncResp](const boost::system::error_code ec,
879cb92c03bSAndrew Geissler                         GetManagedObjectsType &resp) {
880cb92c03bSAndrew Geissler                 if (ec)
881cb92c03bSAndrew Geissler                 {
882cb92c03bSAndrew Geissler                     // TODO Handle for specific error code
883cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
884cb92c03bSAndrew Geissler                         << "getLogEntriesIfaceData resp_handler got error "
885cb92c03bSAndrew Geissler                         << ec;
886cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
887cb92c03bSAndrew Geissler                     return;
888cb92c03bSAndrew Geissler                 }
889cb92c03bSAndrew Geissler                 nlohmann::json &entriesArray =
890cb92c03bSAndrew Geissler                     asyncResp->res.jsonValue["Members"];
891cb92c03bSAndrew Geissler                 entriesArray = nlohmann::json::array();
892cb92c03bSAndrew Geissler                 for (auto &objectPath : resp)
893cb92c03bSAndrew Geissler                 {
894cb92c03bSAndrew Geissler                     for (auto &interfaceMap : objectPath.second)
895cb92c03bSAndrew Geissler                     {
896cb92c03bSAndrew Geissler                         if (interfaceMap.first !=
897cb92c03bSAndrew Geissler                             "xyz.openbmc_project.Logging.Entry")
898cb92c03bSAndrew Geissler                         {
899cb92c03bSAndrew Geissler                             BMCWEB_LOG_DEBUG << "Bailing early on "
900cb92c03bSAndrew Geissler                                              << interfaceMap.first;
901cb92c03bSAndrew Geissler                             continue;
902cb92c03bSAndrew Geissler                         }
903cb92c03bSAndrew Geissler                         entriesArray.push_back({});
904cb92c03bSAndrew Geissler                         nlohmann::json &thisEntry = entriesArray.back();
905cb92c03bSAndrew Geissler                         uint32_t *id;
906cb92c03bSAndrew Geissler                         std::time_t timestamp;
907cb92c03bSAndrew Geissler                         std::string *severity, *message;
908cb92c03bSAndrew Geissler                         for (auto &propertyMap : interfaceMap.second)
909cb92c03bSAndrew Geissler                         {
910cb92c03bSAndrew Geissler                             if (propertyMap.first == "Id")
911cb92c03bSAndrew Geissler                             {
912cb92c03bSAndrew Geissler                                 id = sdbusplus::message::variant_ns::get_if<
913cb92c03bSAndrew Geissler                                     uint32_t>(&propertyMap.second);
914cb92c03bSAndrew Geissler                                 if (id == nullptr)
915cb92c03bSAndrew Geissler                                 {
916cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
917cb92c03bSAndrew Geissler                                                               "Id");
918cb92c03bSAndrew Geissler                                 }
919cb92c03bSAndrew Geissler                             }
920cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Timestamp")
921cb92c03bSAndrew Geissler                             {
922cb92c03bSAndrew Geissler                                 const uint64_t *millisTimeStamp =
923cb92c03bSAndrew Geissler                                     std::get_if<uint64_t>(&propertyMap.second);
924cb92c03bSAndrew Geissler                                 if (millisTimeStamp == nullptr)
925cb92c03bSAndrew Geissler                                 {
926cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
927cb92c03bSAndrew Geissler                                                               "Timestamp");
928*271584abSEd Tanous                                     continue;
929cb92c03bSAndrew Geissler                                 }
930cb92c03bSAndrew Geissler                                 // Retrieve Created property with format:
931cb92c03bSAndrew Geissler                                 // yyyy-mm-ddThh:mm:ss
932cb92c03bSAndrew Geissler                                 std::chrono::milliseconds chronoTimeStamp(
933cb92c03bSAndrew Geissler                                     *millisTimeStamp);
934*271584abSEd Tanous                                 timestamp = std::chrono::duration_cast<
935*271584abSEd Tanous                                                 std::chrono::duration<int>>(
936*271584abSEd Tanous                                                 chronoTimeStamp)
937cb92c03bSAndrew Geissler                                                 .count();
938cb92c03bSAndrew Geissler                             }
939cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Severity")
940cb92c03bSAndrew Geissler                             {
941cb92c03bSAndrew Geissler                                 severity = std::get_if<std::string>(
942cb92c03bSAndrew Geissler                                     &propertyMap.second);
943cb92c03bSAndrew Geissler                                 if (severity == nullptr)
944cb92c03bSAndrew Geissler                                 {
945cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
946cb92c03bSAndrew Geissler                                                               "Severity");
947cb92c03bSAndrew Geissler                                 }
948cb92c03bSAndrew Geissler                             }
949cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Message")
950cb92c03bSAndrew Geissler                             {
951cb92c03bSAndrew Geissler                                 message = std::get_if<std::string>(
952cb92c03bSAndrew Geissler                                     &propertyMap.second);
953cb92c03bSAndrew Geissler                                 if (message == nullptr)
954cb92c03bSAndrew Geissler                                 {
955cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
956cb92c03bSAndrew Geissler                                                               "Message");
957cb92c03bSAndrew Geissler                                 }
958cb92c03bSAndrew Geissler                             }
959cb92c03bSAndrew Geissler                         }
960cb92c03bSAndrew Geissler                         thisEntry = {
961cb92c03bSAndrew Geissler                             {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
962cb92c03bSAndrew Geissler                             {"@odata.context", "/redfish/v1/"
963cb92c03bSAndrew Geissler                                                "$metadata#LogEntry.LogEntry"},
964cb92c03bSAndrew Geissler                             {"@odata.id",
965cb92c03bSAndrew Geissler                              "/redfish/v1/Systems/system/LogServices/EventLog/"
966cb92c03bSAndrew Geissler                              "Entries/" +
967cb92c03bSAndrew Geissler                                  std::to_string(*id)},
96827062605SAnthony Wilson                             {"Name", "System Event Log Entry"},
969cb92c03bSAndrew Geissler                             {"Id", std::to_string(*id)},
970cb92c03bSAndrew Geissler                             {"Message", *message},
971cb92c03bSAndrew Geissler                             {"EntryType", "Event"},
972cb92c03bSAndrew Geissler                             {"Severity",
973cb92c03bSAndrew Geissler                              translateSeverityDbusToRedfish(*severity)},
974cb92c03bSAndrew Geissler                             {"Created", crow::utility::getDateTime(timestamp)}};
975cb92c03bSAndrew Geissler                     }
976cb92c03bSAndrew Geissler                 }
977cb92c03bSAndrew Geissler                 std::sort(entriesArray.begin(), entriesArray.end(),
978cb92c03bSAndrew Geissler                           [](const nlohmann::json &left,
979cb92c03bSAndrew Geissler                              const nlohmann::json &right) {
980cb92c03bSAndrew Geissler                               return (left["Id"] <= right["Id"]);
981cb92c03bSAndrew Geissler                           });
982cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue["Members@odata.count"] =
983cb92c03bSAndrew Geissler                     entriesArray.size();
984cb92c03bSAndrew Geissler             },
985cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
986cb92c03bSAndrew Geissler             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
987c4bf6374SJason M. Bills     }
988c4bf6374SJason M. Bills };
989c4bf6374SJason M. Bills 
99008a4e4b5SAnthony Wilson class DBusEventLogEntry : public Node
991c4bf6374SJason M. Bills {
992c4bf6374SJason M. Bills   public:
99308a4e4b5SAnthony Wilson     DBusEventLogEntry(CrowApp &app) :
994c4bf6374SJason M. Bills         Node(app,
995029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
996029573d4SEd Tanous              std::string())
997c4bf6374SJason M. Bills     {
998c4bf6374SJason M. Bills         entityPrivileges = {
999c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1000c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1001c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1002c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1003c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1004c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1005c4bf6374SJason M. Bills     }
1006c4bf6374SJason M. Bills 
1007c4bf6374SJason M. Bills   private:
1008c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1009c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
1010c4bf6374SJason M. Bills     {
1011c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1012029573d4SEd Tanous         if (params.size() != 1)
1013c4bf6374SJason M. Bills         {
1014c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
1015c4bf6374SJason M. Bills             return;
1016c4bf6374SJason M. Bills         }
1017029573d4SEd Tanous         const std::string &entryID = params[0];
1018cb92c03bSAndrew Geissler 
1019cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
1020cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
1021cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
1022cb92c03bSAndrew Geissler             [asyncResp, entryID](const boost::system::error_code ec,
1023cb92c03bSAndrew Geissler                                  GetManagedPropertyType &resp) {
1024cb92c03bSAndrew Geissler                 if (ec)
1025cb92c03bSAndrew Geissler                 {
1026cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
1027cb92c03bSAndrew Geissler                         << "EventLogEntry (DBus) resp_handler got error " << ec;
1028cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
1029cb92c03bSAndrew Geissler                     return;
1030cb92c03bSAndrew Geissler                 }
1031cb92c03bSAndrew Geissler                 uint32_t *id;
1032cb92c03bSAndrew Geissler                 std::time_t timestamp;
1033cb92c03bSAndrew Geissler                 std::string *severity, *message;
1034cb92c03bSAndrew Geissler                 for (auto &propertyMap : resp)
1035cb92c03bSAndrew Geissler                 {
1036cb92c03bSAndrew Geissler                     if (propertyMap.first == "Id")
1037cb92c03bSAndrew Geissler                     {
1038cb92c03bSAndrew Geissler                         id = std::get_if<uint32_t>(&propertyMap.second);
1039cb92c03bSAndrew Geissler                         if (id == nullptr)
1040cb92c03bSAndrew Geissler                         {
1041cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res, "Id");
1042cb92c03bSAndrew Geissler                         }
1043cb92c03bSAndrew Geissler                     }
1044cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Timestamp")
1045cb92c03bSAndrew Geissler                     {
1046cb92c03bSAndrew Geissler                         const uint64_t *millisTimeStamp =
1047cb92c03bSAndrew Geissler                             std::get_if<uint64_t>(&propertyMap.second);
1048cb92c03bSAndrew Geissler                         if (millisTimeStamp == nullptr)
1049cb92c03bSAndrew Geissler                         {
1050cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
1051cb92c03bSAndrew Geissler                                                       "Timestamp");
1052*271584abSEd Tanous                             continue;
1053cb92c03bSAndrew Geissler                         }
1054cb92c03bSAndrew Geissler                         // Retrieve Created property with format:
1055cb92c03bSAndrew Geissler                         // yyyy-mm-ddThh:mm:ss
1056cb92c03bSAndrew Geissler                         std::chrono::milliseconds chronoTimeStamp(
1057cb92c03bSAndrew Geissler                             *millisTimeStamp);
1058cb92c03bSAndrew Geissler                         timestamp =
1059*271584abSEd Tanous                             std::chrono::duration_cast<
1060*271584abSEd Tanous                                 std::chrono::duration<int>>(chronoTimeStamp)
1061cb92c03bSAndrew Geissler                                 .count();
1062cb92c03bSAndrew Geissler                     }
1063cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Severity")
1064cb92c03bSAndrew Geissler                     {
1065cb92c03bSAndrew Geissler                         severity =
1066cb92c03bSAndrew Geissler                             std::get_if<std::string>(&propertyMap.second);
1067cb92c03bSAndrew Geissler                         if (severity == nullptr)
1068cb92c03bSAndrew Geissler                         {
1069cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
1070cb92c03bSAndrew Geissler                                                       "Severity");
1071cb92c03bSAndrew Geissler                         }
1072cb92c03bSAndrew Geissler                     }
1073cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Message")
1074cb92c03bSAndrew Geissler                     {
1075cb92c03bSAndrew Geissler                         message = std::get_if<std::string>(&propertyMap.second);
1076cb92c03bSAndrew Geissler                         if (message == nullptr)
1077cb92c03bSAndrew Geissler                         {
1078cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
1079cb92c03bSAndrew Geissler                                                       "Message");
1080cb92c03bSAndrew Geissler                         }
1081cb92c03bSAndrew Geissler                     }
1082cb92c03bSAndrew Geissler                 }
1083*271584abSEd Tanous                 if (id == nullptr || message == nullptr || severity == nullptr)
1084*271584abSEd Tanous                 {
1085*271584abSEd Tanous                     return;
1086*271584abSEd Tanous                 }
1087cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue = {
1088cb92c03bSAndrew Geissler                     {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1089cb92c03bSAndrew Geissler                     {"@odata.context", "/redfish/v1/"
1090cb92c03bSAndrew Geissler                                        "$metadata#LogEntry.LogEntry"},
1091cb92c03bSAndrew Geissler                     {"@odata.id",
1092cb92c03bSAndrew Geissler                      "/redfish/v1/Systems/system/LogServices/EventLog/"
1093cb92c03bSAndrew Geissler                      "Entries/" +
1094cb92c03bSAndrew Geissler                          std::to_string(*id)},
109527062605SAnthony Wilson                     {"Name", "System Event Log Entry"},
1096cb92c03bSAndrew Geissler                     {"Id", std::to_string(*id)},
1097cb92c03bSAndrew Geissler                     {"Message", *message},
1098cb92c03bSAndrew Geissler                     {"EntryType", "Event"},
1099cb92c03bSAndrew Geissler                     {"Severity", translateSeverityDbusToRedfish(*severity)},
110008a4e4b5SAnthony Wilson                     {"Created", crow::utility::getDateTime(timestamp)}};
1101cb92c03bSAndrew Geissler             },
1102cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging",
1103cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging/entry/" + entryID,
1104cb92c03bSAndrew Geissler             "org.freedesktop.DBus.Properties", "GetAll",
1105cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging.Entry");
1106c4bf6374SJason M. Bills     }
1107336e96c6SChicago Duan 
1108336e96c6SChicago Duan     void doDelete(crow::Response &res, const crow::Request &req,
1109336e96c6SChicago Duan                   const std::vector<std::string> &params) override
1110336e96c6SChicago Duan     {
1111336e96c6SChicago Duan 
1112336e96c6SChicago Duan         BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1113336e96c6SChicago Duan 
1114336e96c6SChicago Duan         auto asyncResp = std::make_shared<AsyncResp>(res);
1115336e96c6SChicago Duan 
1116336e96c6SChicago Duan         if (params.size() != 1)
1117336e96c6SChicago Duan         {
1118336e96c6SChicago Duan             messages::internalError(asyncResp->res);
1119336e96c6SChicago Duan             return;
1120336e96c6SChicago Duan         }
1121336e96c6SChicago Duan         std::string entryID = params[0];
1122336e96c6SChicago Duan 
1123336e96c6SChicago Duan         dbus::utility::escapePathForDbus(entryID);
1124336e96c6SChicago Duan 
1125336e96c6SChicago Duan         // Process response from Logging service.
1126336e96c6SChicago Duan         auto respHandler = [asyncResp](const boost::system::error_code ec) {
1127336e96c6SChicago Duan             BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1128336e96c6SChicago Duan             if (ec)
1129336e96c6SChicago Duan             {
1130336e96c6SChicago Duan                 // TODO Handle for specific error code
1131336e96c6SChicago Duan                 BMCWEB_LOG_ERROR
1132336e96c6SChicago Duan                     << "EventLogEntry (DBus) doDelete respHandler got error "
1133336e96c6SChicago Duan                     << ec;
1134336e96c6SChicago Duan                 asyncResp->res.result(
1135336e96c6SChicago Duan                     boost::beast::http::status::internal_server_error);
1136336e96c6SChicago Duan                 return;
1137336e96c6SChicago Duan             }
1138336e96c6SChicago Duan 
1139336e96c6SChicago Duan             asyncResp->res.result(boost::beast::http::status::ok);
1140336e96c6SChicago Duan         };
1141336e96c6SChicago Duan 
1142336e96c6SChicago Duan         // Make call to Logging service to request Delete Log
1143336e96c6SChicago Duan         crow::connections::systemBus->async_method_call(
1144336e96c6SChicago Duan             respHandler, "xyz.openbmc_project.Logging",
1145336e96c6SChicago Duan             "/xyz/openbmc_project/logging/entry/" + entryID,
1146336e96c6SChicago Duan             "xyz.openbmc_project.Object.Delete", "Delete");
1147336e96c6SChicago Duan     }
1148c4bf6374SJason M. Bills };
1149c4bf6374SJason M. Bills 
1150c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
1151c4bf6374SJason M. Bills {
1152c4bf6374SJason M. Bills   public:
1153c4bf6374SJason M. Bills     template <typename CrowApp>
1154c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
11554ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
11561da66f75SEd Tanous     {
11571da66f75SEd Tanous         entityPrivileges = {
1158e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1159e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1160e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1161e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1162e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1163e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
11641da66f75SEd Tanous     }
11651da66f75SEd Tanous 
11661da66f75SEd Tanous   private:
11671da66f75SEd Tanous     /**
11681da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
11691da66f75SEd Tanous      */
11701da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
11711da66f75SEd Tanous                const std::vector<std::string> &params) override
11721da66f75SEd Tanous     {
1173e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
11741da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
11751da66f75SEd Tanous         // it has a duplicate entry for members
1176e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
11771da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
1178e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1179c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
1180e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1181e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
1182e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1183e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
11841da66f75SEd Tanous             "Collection of LogServices for this Manager";
1185c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1186c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
1187c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1188c4bf6374SJason M. Bills         logServiceArray.push_back(
118908a4e4b5SAnthony Wilson             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
1190c4bf6374SJason M. Bills #endif
1191e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
1192c4bf6374SJason M. Bills             logServiceArray.size();
11931da66f75SEd Tanous     }
11941da66f75SEd Tanous };
11951da66f75SEd Tanous 
1196c4bf6374SJason M. Bills class BMCJournalLogService : public Node
11971da66f75SEd Tanous {
11981da66f75SEd Tanous   public:
11991da66f75SEd Tanous     template <typename CrowApp>
1200c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
1201c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
1202e1f26343SJason M. Bills     {
1203e1f26343SJason M. Bills         entityPrivileges = {
1204e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1205e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1206e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1207e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1208e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1209e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1210e1f26343SJason M. Bills     }
1211e1f26343SJason M. Bills 
1212e1f26343SJason M. Bills   private:
1213e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1214e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1215e1f26343SJason M. Bills     {
1216e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1217e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1218e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
12190f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
12200f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
1221e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1222e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1223c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1224c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1225c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
1226e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1227cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1228cd50aa42SJason M. Bills             {"@odata.id",
1229086be238SEd Tanous              "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
1230e1f26343SJason M. Bills     }
1231e1f26343SJason M. Bills };
1232e1f26343SJason M. Bills 
1233c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1234e1f26343SJason M. Bills                                       sd_journal *journal,
1235c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
1236e1f26343SJason M. Bills {
1237e1f26343SJason M. Bills     // Get the Log Entry contents
1238e1f26343SJason M. Bills     int ret = 0;
1239e1f26343SJason M. Bills 
124039e77504SEd Tanous     std::string_view msg;
124116428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
1242e1f26343SJason M. Bills     if (ret < 0)
1243e1f26343SJason M. Bills     {
1244e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1245e1f26343SJason M. Bills         return 1;
1246e1f26343SJason M. Bills     }
1247e1f26343SJason M. Bills 
1248e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
1249*271584abSEd Tanous     long int severity = 8; // Default to an invalid priority
125016428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
1251e1f26343SJason M. Bills     if (ret < 0)
1252e1f26343SJason M. Bills     {
1253e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
1254e1f26343SJason M. Bills     }
1255e1f26343SJason M. Bills 
1256e1f26343SJason M. Bills     // Get the Created time from the timestamp
125716428a1aSJason M. Bills     std::string entryTimeStr;
125816428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
1259e1f26343SJason M. Bills     {
126016428a1aSJason M. Bills         return 1;
1261e1f26343SJason M. Bills     }
1262e1f26343SJason M. Bills 
1263e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
1264c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
1265cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1266e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1267c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1268c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
1269e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
1270c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
127116428a1aSJason M. Bills         {"Message", msg},
1272e1f26343SJason M. Bills         {"EntryType", "Oem"},
1273e1f26343SJason M. Bills         {"Severity",
1274b6a61a5eSJason M. Bills          severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
1275086be238SEd Tanous         {"OemRecordFormat", "BMC Journal Entry"},
1276e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
1277e1f26343SJason M. Bills     return 0;
1278e1f26343SJason M. Bills }
1279e1f26343SJason M. Bills 
1280c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
1281e1f26343SJason M. Bills {
1282e1f26343SJason M. Bills   public:
1283e1f26343SJason M. Bills     template <typename CrowApp>
1284c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
1285c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
1286e1f26343SJason M. Bills     {
1287e1f26343SJason M. Bills         entityPrivileges = {
1288e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1289e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1290e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1291e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1292e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1293e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1294e1f26343SJason M. Bills     }
1295e1f26343SJason M. Bills 
1296e1f26343SJason M. Bills   private:
1297e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1298e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1299e1f26343SJason M. Bills     {
1300e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1301193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
1302*271584abSEd Tanous         uint64_t skip = 0;
1303*271584abSEd Tanous         uint64_t top = maxEntriesPerPage; // Show max entries by default
130416428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
1305193ad2faSJason M. Bills         {
1306193ad2faSJason M. Bills             return;
1307193ad2faSJason M. Bills         }
130816428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
1309193ad2faSJason M. Bills         {
1310193ad2faSJason M. Bills             return;
1311193ad2faSJason M. Bills         }
1312e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
1313e1f26343SJason M. Bills         // it has a duplicate entry for members
1314e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1315e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
13160f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
13170f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1318e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1319c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1320e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1321c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1322e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1323e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
1324e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
13250f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
13260f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
1327e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1328e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
1329e1f26343SJason M. Bills 
1330e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
1331e1f26343SJason M. Bills         // for each entry
1332e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1333e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1334e1f26343SJason M. Bills         if (ret < 0)
1335e1f26343SJason M. Bills         {
1336e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1337f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1338e1f26343SJason M. Bills             return;
1339e1f26343SJason M. Bills         }
1340e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1341e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1342e1f26343SJason M. Bills         journalTmp = nullptr;
1343b01bf299SEd Tanous         uint64_t entryCount = 0;
1344e85d6b16SJason M. Bills         // Reset the unique ID on the first entry
1345e85d6b16SJason M. Bills         bool firstEntry = true;
1346e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
1347e1f26343SJason M. Bills         {
1348193ad2faSJason M. Bills             entryCount++;
1349193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
1350193ad2faSJason M. Bills             // start) and top (number of entries to display)
1351193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
1352193ad2faSJason M. Bills             {
1353193ad2faSJason M. Bills                 continue;
1354193ad2faSJason M. Bills             }
1355193ad2faSJason M. Bills 
135616428a1aSJason M. Bills             std::string idStr;
1357e85d6b16SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1358e1f26343SJason M. Bills             {
1359e1f26343SJason M. Bills                 continue;
1360e1f26343SJason M. Bills             }
1361e1f26343SJason M. Bills 
1362e85d6b16SJason M. Bills             if (firstEntry)
1363e85d6b16SJason M. Bills             {
1364e85d6b16SJason M. Bills                 firstEntry = false;
1365e85d6b16SJason M. Bills             }
1366e85d6b16SJason M. Bills 
1367e1f26343SJason M. Bills             logEntryArray.push_back({});
1368c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1369c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1370c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
1371e1f26343SJason M. Bills             {
1372f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
1373e1f26343SJason M. Bills                 return;
1374e1f26343SJason M. Bills             }
1375e1f26343SJason M. Bills         }
1376193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1377193ad2faSJason M. Bills         if (skip + top < entryCount)
1378193ad2faSJason M. Bills         {
1379193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
1380c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
1381193ad2faSJason M. Bills                 std::to_string(skip + top);
1382193ad2faSJason M. Bills         }
1383e1f26343SJason M. Bills     }
1384e1f26343SJason M. Bills };
1385e1f26343SJason M. Bills 
1386c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
1387e1f26343SJason M. Bills {
1388e1f26343SJason M. Bills   public:
1389c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
1390c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
1391e1f26343SJason M. Bills              std::string())
1392e1f26343SJason M. Bills     {
1393e1f26343SJason M. Bills         entityPrivileges = {
1394e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1395e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1396e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1397e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1398e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1399e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1400e1f26343SJason M. Bills     }
1401e1f26343SJason M. Bills 
1402e1f26343SJason M. Bills   private:
1403e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1404e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1405e1f26343SJason M. Bills     {
1406e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1407e1f26343SJason M. Bills         if (params.size() != 1)
1408e1f26343SJason M. Bills         {
1409f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1410e1f26343SJason M. Bills             return;
1411e1f26343SJason M. Bills         }
141216428a1aSJason M. Bills         const std::string &entryID = params[0];
1413e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
1414e1f26343SJason M. Bills         uint64_t ts = 0;
1415*271584abSEd Tanous         uint64_t index = 0;
141616428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
1417e1f26343SJason M. Bills         {
141816428a1aSJason M. Bills             return;
1419e1f26343SJason M. Bills         }
1420e1f26343SJason M. Bills 
1421e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1422e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1423e1f26343SJason M. Bills         if (ret < 0)
1424e1f26343SJason M. Bills         {
1425e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1426f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1427e1f26343SJason M. Bills             return;
1428e1f26343SJason M. Bills         }
1429e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1430e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1431e1f26343SJason M. Bills         journalTmp = nullptr;
1432e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
1433af07e3f5SJason M. Bills         // tracking the unique ID
1434af07e3f5SJason M. Bills         std::string idStr;
1435af07e3f5SJason M. Bills         bool firstEntry = true;
1436e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1437*271584abSEd Tanous         for (uint64_t i = 0; i <= index; i++)
1438e1f26343SJason M. Bills         {
1439e1f26343SJason M. Bills             sd_journal_next(journal.get());
1440af07e3f5SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1441af07e3f5SJason M. Bills             {
1442af07e3f5SJason M. Bills                 messages::internalError(asyncResp->res);
1443af07e3f5SJason M. Bills                 return;
1444af07e3f5SJason M. Bills             }
1445af07e3f5SJason M. Bills             if (firstEntry)
1446af07e3f5SJason M. Bills             {
1447af07e3f5SJason M. Bills                 firstEntry = false;
1448af07e3f5SJason M. Bills             }
1449e1f26343SJason M. Bills         }
1450c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
1451af07e3f5SJason M. Bills         if (idStr != entryID)
1452c4bf6374SJason M. Bills         {
1453c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
1454c4bf6374SJason M. Bills             return;
1455c4bf6374SJason M. Bills         }
1456c4bf6374SJason M. Bills 
1457c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1458e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
1459e1f26343SJason M. Bills         {
1460f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1461e1f26343SJason M. Bills             return;
1462e1f26343SJason M. Bills         }
1463e1f26343SJason M. Bills     }
1464e1f26343SJason M. Bills };
1465e1f26343SJason M. Bills 
1466424c4176SJason M. Bills class CrashdumpService : public Node
1467e1f26343SJason M. Bills {
1468e1f26343SJason M. Bills   public:
1469e1f26343SJason M. Bills     template <typename CrowApp>
1470424c4176SJason M. Bills     CrashdumpService(CrowApp &app) :
1471424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
14721da66f75SEd Tanous     {
14731da66f75SEd Tanous         entityPrivileges = {
1474e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1475e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1476e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1477e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1478e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1479e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
14801da66f75SEd Tanous     }
14811da66f75SEd Tanous 
14821da66f75SEd Tanous   private:
14831da66f75SEd Tanous     /**
14841da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
14851da66f75SEd Tanous      */
14861da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
14871da66f75SEd Tanous                const std::vector<std::string> &params) override
14881da66f75SEd Tanous     {
1489e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
14901da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
14910f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
1492424c4176SJason M. Bills             "/redfish/v1/Systems/system/LogServices/Crashdump";
1493e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1494e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
1495e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1496c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1497424c4176SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1498424c4176SJason M. Bills         asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1499424c4176SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Crashdump";
1500e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1501e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
1502cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1503cd50aa42SJason M. Bills             {"@odata.id",
1504424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
1505e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
15061da66f75SEd Tanous             {"Oem",
1507424c4176SJason M. Bills              {{"#Crashdump.OnDemand",
1508424c4176SJason M. Bills                {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1509424c4176SJason M. Bills                            "Actions/Oem/Crashdump.OnDemand"}}}}}};
15101da66f75SEd Tanous 
15111da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
1512e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
1513424c4176SJason M. Bills             {"#Crashdump.SendRawPeci",
151408a4e4b5SAnthony Wilson              {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1515424c4176SJason M. Bills                          "Actions/Oem/Crashdump.SendRawPeci"}}});
15161da66f75SEd Tanous #endif
15171da66f75SEd Tanous     }
15181da66f75SEd Tanous };
15191da66f75SEd Tanous 
1520424c4176SJason M. Bills class CrashdumpEntryCollection : public Node
15211da66f75SEd Tanous {
15221da66f75SEd Tanous   public:
15231da66f75SEd Tanous     template <typename CrowApp>
1524424c4176SJason M. Bills     CrashdumpEntryCollection(CrowApp &app) :
1525424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
15261da66f75SEd Tanous     {
15271da66f75SEd Tanous         entityPrivileges = {
1528e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1529e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1530e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1531e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1532e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1533e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
15341da66f75SEd Tanous     }
15351da66f75SEd Tanous 
15361da66f75SEd Tanous   private:
15371da66f75SEd Tanous     /**
15381da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
15391da66f75SEd Tanous      */
15401da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
15411da66f75SEd Tanous                const std::vector<std::string> &params) override
15421da66f75SEd Tanous     {
1543e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
15441da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
15451da66f75SEd Tanous         // it has a duplicate entry for members
1546e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
1547e1f26343SJason M. Bills                                          const boost::system::error_code ec,
15481da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
15491da66f75SEd Tanous             if (ec)
15501da66f75SEd Tanous             {
15511da66f75SEd Tanous                 if (ec.value() !=
15521da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
15531da66f75SEd Tanous                 {
15541da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
15551da66f75SEd Tanous                                      << ec.message();
1556f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
15571da66f75SEd Tanous                     return;
15581da66f75SEd Tanous                 }
15591da66f75SEd Tanous             }
1560e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
15611da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
15620f74e643SEd Tanous             asyncResp->res.jsonValue["@odata.id"] =
1563424c4176SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
1564e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1565d53dd41fSJason M. Bills                 "/redfish/v1/"
1566d53dd41fSJason M. Bills                 "$metadata#LogEntryCollection.LogEntryCollection";
1567424c4176SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
1568e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1569424c4176SJason M. Bills                 "Collection of Crashdump Entries";
1570e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1571e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
15721da66f75SEd Tanous             for (const std::string &objpath : resp)
15731da66f75SEd Tanous             {
157448e4639eSJason M. Bills                 // Don't list the on-demand log
1575424c4176SJason M. Bills                 if (objpath.compare(CrashdumpOnDemandPath) == 0)
15761da66f75SEd Tanous                 {
15771da66f75SEd Tanous                     continue;
15781da66f75SEd Tanous                 }
15794ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
15804ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
15811da66f75SEd Tanous                 {
1582e1f26343SJason M. Bills                     logEntryArray.push_back(
1583d53dd41fSJason M. Bills                         {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1584424c4176SJason M. Bills                                        "Crashdump/Entries/" +
15854ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
15861da66f75SEd Tanous                 }
15871da66f75SEd Tanous             }
1588e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1589e1f26343SJason M. Bills                 logEntryArray.size();
15901da66f75SEd Tanous         };
15911da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
15921da66f75SEd Tanous             std::move(getLogEntriesCallback),
15931da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
15941da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
15951da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
1596424c4176SJason M. Bills             std::array<const char *, 1>{CrashdumpInterface});
15971da66f75SEd Tanous     }
15981da66f75SEd Tanous };
15991da66f75SEd Tanous 
1600424c4176SJason M. Bills std::string getLogCreatedTime(const nlohmann::json &Crashdump)
16011da66f75SEd Tanous {
1602424c4176SJason M. Bills     nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1603424c4176SJason M. Bills     if (cdIt != Crashdump.end())
16041da66f75SEd Tanous     {
1605c4d00437SJason M. Bills         nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1606c4d00437SJason M. Bills         if (siIt != cdIt->end())
16071da66f75SEd Tanous         {
1608c4d00437SJason M. Bills             nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1609c4d00437SJason M. Bills             if (tsIt != siIt->end())
1610c4d00437SJason M. Bills             {
1611c4d00437SJason M. Bills                 const std::string *logTime =
1612c4d00437SJason M. Bills                     tsIt->get_ptr<const std::string *>();
16131da66f75SEd Tanous                 if (logTime != nullptr)
16141da66f75SEd Tanous                 {
16151da66f75SEd Tanous                     return *logTime;
16161da66f75SEd Tanous                 }
16171da66f75SEd Tanous             }
16181da66f75SEd Tanous         }
1619c4d00437SJason M. Bills     }
16201da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
16211da66f75SEd Tanous 
16221da66f75SEd Tanous     return std::string();
16231da66f75SEd Tanous }
16241da66f75SEd Tanous 
1625424c4176SJason M. Bills class CrashdumpEntry : public Node
16261da66f75SEd Tanous {
16271da66f75SEd Tanous   public:
1628424c4176SJason M. Bills     CrashdumpEntry(CrowApp &app) :
1629d53dd41fSJason M. Bills         Node(app,
1630424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
16311da66f75SEd Tanous              std::string())
16321da66f75SEd Tanous     {
16331da66f75SEd Tanous         entityPrivileges = {
1634e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1635e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1636e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1637e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1638e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1639e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
16401da66f75SEd Tanous     }
16411da66f75SEd Tanous 
16421da66f75SEd Tanous   private:
16431da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
16441da66f75SEd Tanous                const std::vector<std::string> &params) override
16451da66f75SEd Tanous     {
1646e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
16471da66f75SEd Tanous         if (params.size() != 1)
16481da66f75SEd Tanous         {
1649f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
16501da66f75SEd Tanous             return;
16511da66f75SEd Tanous         }
1652*271584abSEd Tanous         const int logId = std::atoi(params[0].c_str());
1653abf2add6SEd Tanous         auto getStoredLogCallback = [asyncResp, logId](
1654abf2add6SEd Tanous                                         const boost::system::error_code ec,
1655abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
16561da66f75SEd Tanous             if (ec)
16571da66f75SEd Tanous             {
1658abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1659f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
16601da66f75SEd Tanous                 return;
16611da66f75SEd Tanous             }
1662abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
16631da66f75SEd Tanous             if (log == nullptr)
16641da66f75SEd Tanous             {
1665f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
16661da66f75SEd Tanous                 return;
16671da66f75SEd Tanous             }
16681da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
16691da66f75SEd Tanous             if (j.is_discarded())
16701da66f75SEd Tanous             {
1671f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
16721da66f75SEd Tanous                 return;
16731da66f75SEd Tanous             }
16741da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1675e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1676cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1677abf2add6SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
16781da66f75SEd Tanous                 {"@odata.id",
1679424c4176SJason M. Bills                  "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
16804ed77cd5SEd Tanous                      std::to_string(logId)},
1681424c4176SJason M. Bills                 {"Name", "CPU Crashdump"},
16824ed77cd5SEd Tanous                 {"Id", logId},
16831da66f75SEd Tanous                 {"EntryType", "Oem"},
1684424c4176SJason M. Bills                 {"OemRecordFormat", "Intel Crashdump"},
16851da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
16861da66f75SEd Tanous                 {"Created", std::move(t)}};
16871da66f75SEd Tanous         };
16881da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1689424c4176SJason M. Bills             std::move(getStoredLogCallback), CrashdumpObject,
1690424c4176SJason M. Bills             CrashdumpPath + std::string("/") + std::to_string(logId),
1691424c4176SJason M. Bills             "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1692424c4176SJason M. Bills             "Log");
16931da66f75SEd Tanous     }
16941da66f75SEd Tanous };
16951da66f75SEd Tanous 
1696424c4176SJason M. Bills class OnDemandCrashdump : public Node
16971da66f75SEd Tanous {
16981da66f75SEd Tanous   public:
1699424c4176SJason M. Bills     OnDemandCrashdump(CrowApp &app) :
1700424c4176SJason M. Bills         Node(app,
1701424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1702424c4176SJason M. Bills              "Crashdump.OnDemand/")
17031da66f75SEd Tanous     {
17041da66f75SEd Tanous         entityPrivileges = {
1705e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1706e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1707e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1708e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1709e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1710e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
17111da66f75SEd Tanous     }
17121da66f75SEd Tanous 
17131da66f75SEd Tanous   private:
17141da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
17151da66f75SEd Tanous                 const std::vector<std::string> &params) override
17161da66f75SEd Tanous     {
1717e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
171848e4639eSJason M. Bills         static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
17191da66f75SEd Tanous 
172048e4639eSJason M. Bills         // Only allow one OnDemand Log request at a time
172148e4639eSJason M. Bills         if (onDemandLogMatcher != nullptr)
17221da66f75SEd Tanous         {
1723e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1724f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
17251da66f75SEd Tanous             return;
17261da66f75SEd Tanous         }
17271da66f75SEd Tanous         // Make this static so it survives outside this method
1728*271584abSEd Tanous         static boost::asio::steady_timer timeout(*req.ioService);
17291da66f75SEd Tanous 
1730*271584abSEd Tanous         timeout.expires_after(std::chrono::seconds(30));
1731e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
173248e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
17331da66f75SEd Tanous             if (ec)
17341da66f75SEd Tanous             {
17351da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
17361da66f75SEd Tanous                 // completion.
17371da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
17381da66f75SEd Tanous                 {
17391da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
17401da66f75SEd Tanous                 }
17411da66f75SEd Tanous                 return;
17421da66f75SEd Tanous             }
174348e4639eSJason M. Bills             BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
17441da66f75SEd Tanous 
1745f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
17461da66f75SEd Tanous         });
17471da66f75SEd Tanous 
174848e4639eSJason M. Bills         auto onDemandLogMatcherCallback = [asyncResp](
17491da66f75SEd Tanous                                               sdbusplus::message::message &m) {
175048e4639eSJason M. Bills             BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
1751*271584abSEd Tanous             timeout.cancel();
1752*271584abSEd Tanous 
17534ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
17541da66f75SEd Tanous             boost::container::flat_map<
1755abf2add6SEd Tanous                 std::string, boost::container::flat_map<
1756abf2add6SEd Tanous                                  std::string, std::variant<std::string>>>
17574ed77cd5SEd Tanous                 interfacesAdded;
17584ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1759abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(
1760424c4176SJason M. Bills                 &interfacesAdded[CrashdumpInterface]["Log"]);
17611da66f75SEd Tanous             if (log == nullptr)
17621da66f75SEd Tanous             {
1763f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
176448e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
17651da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
17661da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
17671da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
17681da66f75SEd Tanous                 // be the last thing done.
176948e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
17701da66f75SEd Tanous                 return;
17711da66f75SEd Tanous             }
17721da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
17731da66f75SEd Tanous             if (j.is_discarded())
17741da66f75SEd Tanous             {
1775f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
177648e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
17771da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
17781da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
17791da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
17801da66f75SEd Tanous                 // be the last thing done.
178148e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
17821da66f75SEd Tanous                 return;
17831da66f75SEd Tanous             }
17841da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1785e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1786cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
17871da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1788424c4176SJason M. Bills                 {"Name", "CPU Crashdump"},
17891da66f75SEd Tanous                 {"EntryType", "Oem"},
1790424c4176SJason M. Bills                 {"OemRecordFormat", "Intel Crashdump"},
17911da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
17921da66f75SEd Tanous                 {"Created", std::move(t)}};
179348e4639eSJason M. Bills             // Careful with onDemandLogMatcher.  It is a unique_ptr to the
17941da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
17951da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
17961da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
17971da66f75SEd Tanous             // thing done.
179848e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
17991da66f75SEd Tanous         };
180048e4639eSJason M. Bills         onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
18011da66f75SEd Tanous             *crow::connections::systemBus,
18021da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
1803424c4176SJason M. Bills                 sdbusplus::bus::match::rules::argNpath(0,
1804424c4176SJason M. Bills                                                        CrashdumpOnDemandPath),
180548e4639eSJason M. Bills             std::move(onDemandLogMatcherCallback));
18061da66f75SEd Tanous 
180748e4639eSJason M. Bills         auto generateonDemandLogCallback =
1808e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
18091da66f75SEd Tanous                         const std::string &resp) {
18101da66f75SEd Tanous                 if (ec)
18111da66f75SEd Tanous                 {
18121da66f75SEd Tanous                     if (ec.value() ==
18131da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
18141da66f75SEd Tanous                     {
1815f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
18161da66f75SEd Tanous                     }
18171da66f75SEd Tanous                     else
18181da66f75SEd Tanous                     {
1819f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
18201da66f75SEd Tanous                     }
1821*271584abSEd Tanous 
1822*271584abSEd Tanous                     timeout.cancel();
182348e4639eSJason M. Bills                     onDemandLogMatcher = nullptr;
18241da66f75SEd Tanous                     return;
18251da66f75SEd Tanous                 }
18261da66f75SEd Tanous             };
18271da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1828424c4176SJason M. Bills             std::move(generateonDemandLogCallback), CrashdumpObject,
1829424c4176SJason M. Bills             CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
18301da66f75SEd Tanous     }
18311da66f75SEd Tanous };
18321da66f75SEd Tanous 
1833e1f26343SJason M. Bills class SendRawPECI : public Node
18341da66f75SEd Tanous {
18351da66f75SEd Tanous   public:
1836e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
1837424c4176SJason M. Bills         Node(app,
1838424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1839424c4176SJason M. Bills              "Crashdump.SendRawPeci/")
18401da66f75SEd Tanous     {
18411da66f75SEd Tanous         entityPrivileges = {
18421da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
18431da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
18441da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
18451da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
18461da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
18471da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
18481da66f75SEd Tanous     }
18491da66f75SEd Tanous 
18501da66f75SEd Tanous   private:
18511da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
18521da66f75SEd Tanous                 const std::vector<std::string> &params) override
18531da66f75SEd Tanous     {
1854e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1855b1556427SEd Tanous         uint8_t clientAddress = 0;
1856b1556427SEd Tanous         uint8_t readLength = 0;
18571da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
1858b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1859b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
1860b1556427SEd Tanous                                  peciCommand))
18611da66f75SEd Tanous         {
18621da66f75SEd Tanous             return;
18631da66f75SEd Tanous         }
1864b1556427SEd Tanous 
18651da66f75SEd Tanous         // Callback to return the Raw PECI response
1866e1f26343SJason M. Bills         auto sendRawPECICallback =
1867e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
18681da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
18691da66f75SEd Tanous                 if (ec)
18701da66f75SEd Tanous                 {
18711da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
18721da66f75SEd Tanous                                      << ec.message();
1873f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
18741da66f75SEd Tanous                     return;
18751da66f75SEd Tanous                 }
1876e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
18771da66f75SEd Tanous                                             {"PECIResponse", resp}};
18781da66f75SEd Tanous             };
18791da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
18801da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1881424c4176SJason M. Bills             std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1882424c4176SJason M. Bills             CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
18834ed77cd5SEd Tanous             peciCommand);
18841da66f75SEd Tanous     }
18851da66f75SEd Tanous };
18861da66f75SEd Tanous 
1887cb92c03bSAndrew Geissler /**
1888cb92c03bSAndrew Geissler  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1889cb92c03bSAndrew Geissler  */
1890cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node
1891cb92c03bSAndrew Geissler {
1892cb92c03bSAndrew Geissler   public:
1893cb92c03bSAndrew Geissler     DBusLogServiceActionsClear(CrowApp &app) :
1894cb92c03bSAndrew Geissler         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1895cb92c03bSAndrew Geissler                   "LogService.Reset")
1896cb92c03bSAndrew Geissler     {
1897cb92c03bSAndrew Geissler         entityPrivileges = {
1898cb92c03bSAndrew Geissler             {boost::beast::http::verb::get, {{"Login"}}},
1899cb92c03bSAndrew Geissler             {boost::beast::http::verb::head, {{"Login"}}},
1900cb92c03bSAndrew Geissler             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1901cb92c03bSAndrew Geissler             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1902cb92c03bSAndrew Geissler             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1903cb92c03bSAndrew Geissler             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1904cb92c03bSAndrew Geissler     }
1905cb92c03bSAndrew Geissler 
1906cb92c03bSAndrew Geissler   private:
1907cb92c03bSAndrew Geissler     /**
1908cb92c03bSAndrew Geissler      * Function handles POST method request.
1909cb92c03bSAndrew Geissler      * The Clear Log actions does not require any parameter.The action deletes
1910cb92c03bSAndrew Geissler      * all entries found in the Entries collection for this Log Service.
1911cb92c03bSAndrew Geissler      */
1912cb92c03bSAndrew Geissler     void doPost(crow::Response &res, const crow::Request &req,
1913cb92c03bSAndrew Geissler                 const std::vector<std::string> &params) override
1914cb92c03bSAndrew Geissler     {
1915cb92c03bSAndrew Geissler         BMCWEB_LOG_DEBUG << "Do delete all entries.";
1916cb92c03bSAndrew Geissler 
1917cb92c03bSAndrew Geissler         auto asyncResp = std::make_shared<AsyncResp>(res);
1918cb92c03bSAndrew Geissler         // Process response from Logging service.
1919cb92c03bSAndrew Geissler         auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1920cb92c03bSAndrew Geissler             BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1921cb92c03bSAndrew Geissler             if (ec)
1922cb92c03bSAndrew Geissler             {
1923cb92c03bSAndrew Geissler                 // TODO Handle for specific error code
1924cb92c03bSAndrew Geissler                 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1925cb92c03bSAndrew Geissler                 asyncResp->res.result(
1926cb92c03bSAndrew Geissler                     boost::beast::http::status::internal_server_error);
1927cb92c03bSAndrew Geissler                 return;
1928cb92c03bSAndrew Geissler             }
1929cb92c03bSAndrew Geissler 
1930cb92c03bSAndrew Geissler             asyncResp->res.result(boost::beast::http::status::no_content);
1931cb92c03bSAndrew Geissler         };
1932cb92c03bSAndrew Geissler 
1933cb92c03bSAndrew Geissler         // Make call to Logging service to request Clear Log
1934cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
1935cb92c03bSAndrew Geissler             resp_handler, "xyz.openbmc_project.Logging",
1936cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging",
1937cb92c03bSAndrew Geissler             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1938cb92c03bSAndrew Geissler     }
1939cb92c03bSAndrew Geissler };
19401da66f75SEd Tanous } // namespace redfish
1941