xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 958201849f343b6acc298b8e165d7858bd4ed128)
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>
30abf2add6SEd Tanous #include <variant>
311da66f75SEd Tanous 
321da66f75SEd Tanous namespace redfish
331da66f75SEd Tanous {
341da66f75SEd Tanous 
35424c4176SJason M. Bills constexpr char const *CrashdumpObject = "com.intel.crashdump";
36424c4176SJason M. Bills constexpr char const *CrashdumpPath = "/com/intel/crashdump";
37424c4176SJason M. Bills constexpr char const *CrashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
38424c4176SJason M. Bills constexpr char const *CrashdumpInterface = "com.intel.crashdump";
39424c4176SJason M. Bills constexpr char const *CrashdumpOnDemandInterface =
40424c4176SJason M. Bills     "com.intel.crashdump.OnDemand";
41424c4176SJason M. Bills constexpr char const *CrashdumpRawPECIInterface =
42424c4176SJason M. Bills     "com.intel.crashdump.SendRawPeci";
431da66f75SEd Tanous 
444851d45dSJason M. Bills namespace message_registries
454851d45dSJason M. Bills {
464851d45dSJason M. Bills static const Message *getMessageFromRegistry(
474851d45dSJason M. Bills     const std::string &messageKey,
484851d45dSJason M. Bills     const boost::beast::span<const MessageEntry> registry)
494851d45dSJason M. Bills {
504851d45dSJason M. Bills     boost::beast::span<const MessageEntry>::const_iterator messageIt =
514851d45dSJason M. Bills         std::find_if(registry.cbegin(), registry.cend(),
524851d45dSJason M. Bills                      [&messageKey](const MessageEntry &messageEntry) {
534851d45dSJason M. Bills                          return !std::strcmp(messageEntry.first,
544851d45dSJason M. Bills                                              messageKey.c_str());
554851d45dSJason M. Bills                      });
564851d45dSJason M. Bills     if (messageIt != registry.cend())
574851d45dSJason M. Bills     {
584851d45dSJason M. Bills         return &messageIt->second;
594851d45dSJason M. Bills     }
604851d45dSJason M. Bills 
614851d45dSJason M. Bills     return nullptr;
624851d45dSJason M. Bills }
634851d45dSJason M. Bills 
644851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID)
654851d45dSJason M. Bills {
664851d45dSJason M. Bills     // Redfish MessageIds are in the form
674851d45dSJason M. Bills     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
684851d45dSJason M. Bills     // the right Message
694851d45dSJason M. Bills     std::vector<std::string> fields;
704851d45dSJason M. Bills     fields.reserve(4);
714851d45dSJason M. Bills     boost::split(fields, messageID, boost::is_any_of("."));
724851d45dSJason M. Bills     std::string &registryName = fields[0];
734851d45dSJason M. Bills     std::string &messageKey = fields[3];
744851d45dSJason M. Bills 
754851d45dSJason M. Bills     // Find the right registry and check it for the MessageKey
764851d45dSJason M. Bills     if (std::string(base::header.registryPrefix) == registryName)
774851d45dSJason M. Bills     {
784851d45dSJason M. Bills         return getMessageFromRegistry(
794851d45dSJason M. Bills             messageKey, boost::beast::span<const MessageEntry>(base::registry));
804851d45dSJason M. Bills     }
814851d45dSJason M. Bills     if (std::string(openbmc::header.registryPrefix) == registryName)
824851d45dSJason M. Bills     {
834851d45dSJason M. Bills         return getMessageFromRegistry(
844851d45dSJason M. Bills             messageKey,
854851d45dSJason M. Bills             boost::beast::span<const MessageEntry>(openbmc::registry));
864851d45dSJason M. Bills     }
874851d45dSJason M. Bills     return nullptr;
884851d45dSJason M. Bills }
894851d45dSJason M. Bills } // namespace message_registries
904851d45dSJason M. Bills 
91f6150403SJames Feist namespace fs = std::filesystem;
921da66f75SEd Tanous 
93cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map<
94cb92c03bSAndrew Geissler     std::string,
95cb92c03bSAndrew Geissler     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
96cb92c03bSAndrew Geissler                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
97cb92c03bSAndrew Geissler 
98cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map<
99cb92c03bSAndrew Geissler     sdbusplus::message::object_path,
100cb92c03bSAndrew Geissler     boost::container::flat_map<std::string, GetManagedPropertyType>>;
101cb92c03bSAndrew Geissler 
102cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s)
103cb92c03bSAndrew Geissler {
104cb92c03bSAndrew Geissler     if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
105cb92c03bSAndrew Geissler     {
106cb92c03bSAndrew Geissler         return "Critical";
107cb92c03bSAndrew Geissler     }
108cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
109cb92c03bSAndrew Geissler     {
110cb92c03bSAndrew Geissler         return "Critical";
111cb92c03bSAndrew Geissler     }
112cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
113cb92c03bSAndrew Geissler     {
114cb92c03bSAndrew Geissler         return "OK";
115cb92c03bSAndrew Geissler     }
116cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
117cb92c03bSAndrew Geissler     {
118cb92c03bSAndrew Geissler         return "Critical";
119cb92c03bSAndrew Geissler     }
120cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
121cb92c03bSAndrew Geissler     {
122cb92c03bSAndrew Geissler         return "Critical";
123cb92c03bSAndrew Geissler     }
124cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
125cb92c03bSAndrew Geissler     {
126cb92c03bSAndrew Geissler         return "OK";
127cb92c03bSAndrew Geissler     }
128cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
129cb92c03bSAndrew Geissler     {
130cb92c03bSAndrew Geissler         return "OK";
131cb92c03bSAndrew Geissler     }
132cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
133cb92c03bSAndrew Geissler     {
134cb92c03bSAndrew Geissler         return "Warning";
135cb92c03bSAndrew Geissler     }
136cb92c03bSAndrew Geissler     return "";
137cb92c03bSAndrew Geissler }
138cb92c03bSAndrew Geissler 
13916428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
14039e77504SEd Tanous                               const std::string_view &field,
14139e77504SEd Tanous                               std::string_view &contents)
14216428a1aSJason M. Bills {
14316428a1aSJason M. Bills     const char *data = nullptr;
14416428a1aSJason M. Bills     size_t length = 0;
14516428a1aSJason M. Bills     int ret = 0;
14616428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
147b01bf299SEd Tanous     ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
148b01bf299SEd Tanous                               &length);
14916428a1aSJason M. Bills     if (ret < 0)
15016428a1aSJason M. Bills     {
15116428a1aSJason M. Bills         return ret;
15216428a1aSJason M. Bills     }
15339e77504SEd Tanous     contents = std::string_view(data, length);
15416428a1aSJason M. Bills     // Only use the content after the "=" character.
15516428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
15616428a1aSJason M. Bills     return ret;
15716428a1aSJason M. Bills }
15816428a1aSJason M. Bills 
15916428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
16039e77504SEd Tanous                               const std::string_view &field, const int &base,
16116428a1aSJason M. Bills                               int &contents)
16216428a1aSJason M. Bills {
16316428a1aSJason M. Bills     int ret = 0;
16439e77504SEd Tanous     std::string_view metadata;
16516428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
16616428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
16716428a1aSJason M. Bills     if (ret < 0)
16816428a1aSJason M. Bills     {
16916428a1aSJason M. Bills         return ret;
17016428a1aSJason M. Bills     }
171b01bf299SEd Tanous     contents = strtol(metadata.data(), nullptr, base);
17216428a1aSJason M. Bills     return ret;
17316428a1aSJason M. Bills }
17416428a1aSJason M. Bills 
17516428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
17616428a1aSJason M. Bills {
17716428a1aSJason M. Bills     int ret = 0;
17816428a1aSJason M. Bills     uint64_t timestamp = 0;
17916428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
18016428a1aSJason M. Bills     if (ret < 0)
18116428a1aSJason M. Bills     {
18216428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
18316428a1aSJason M. Bills                          << strerror(-ret);
18416428a1aSJason M. Bills         return false;
18516428a1aSJason M. Bills     }
18616428a1aSJason M. Bills     time_t t =
18716428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
18816428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
18916428a1aSJason M. Bills     char entryTime[64] = {};
19016428a1aSJason M. Bills     if (NULL != loctime)
19116428a1aSJason M. Bills     {
19216428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
19316428a1aSJason M. Bills     }
19416428a1aSJason M. Bills     // Insert the ':' into the timezone
19539e77504SEd Tanous     std::string_view t1(entryTime);
19639e77504SEd Tanous     std::string_view t2(entryTime);
19716428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
19816428a1aSJason M. Bills     {
19916428a1aSJason M. Bills         t1.remove_suffix(2);
20016428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
20116428a1aSJason M. Bills     }
20239e77504SEd Tanous     entryTimestamp = std::string(t1) + ":" + std::string(t2);
20316428a1aSJason M. Bills     return true;
20416428a1aSJason M. Bills }
20516428a1aSJason M. Bills 
20616428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
20716428a1aSJason M. Bills                          long &skip)
20816428a1aSJason M. Bills {
20916428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
21016428a1aSJason M. Bills     if (skipParam != nullptr)
21116428a1aSJason M. Bills     {
21216428a1aSJason M. Bills         char *ptr = nullptr;
21316428a1aSJason M. Bills         skip = std::strtol(skipParam, &ptr, 10);
21416428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
21516428a1aSJason M. Bills         {
21616428a1aSJason M. Bills 
21716428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
21816428a1aSJason M. Bills                                                    "$skip");
21916428a1aSJason M. Bills             return false;
22016428a1aSJason M. Bills         }
22116428a1aSJason M. Bills         if (skip < 0)
22216428a1aSJason M. Bills         {
22316428a1aSJason M. Bills 
22416428a1aSJason M. Bills             messages::queryParameterOutOfRange(res, std::to_string(skip),
22516428a1aSJason M. Bills                                                "$skip", "greater than 0");
22616428a1aSJason M. Bills             return false;
22716428a1aSJason M. Bills         }
22816428a1aSJason M. Bills     }
22916428a1aSJason M. Bills     return true;
23016428a1aSJason M. Bills }
23116428a1aSJason M. Bills 
23216428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000;
23316428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
23416428a1aSJason M. Bills                         long &top)
23516428a1aSJason M. Bills {
23616428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
23716428a1aSJason M. Bills     if (topParam != nullptr)
23816428a1aSJason M. Bills     {
23916428a1aSJason M. Bills         char *ptr = nullptr;
24016428a1aSJason M. Bills         top = std::strtol(topParam, &ptr, 10);
24116428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
24216428a1aSJason M. Bills         {
24316428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
24416428a1aSJason M. Bills                                                    "$top");
24516428a1aSJason M. Bills             return false;
24616428a1aSJason M. Bills         }
24716428a1aSJason M. Bills         if (top < 1 || top > maxEntriesPerPage)
24816428a1aSJason M. Bills         {
24916428a1aSJason M. Bills 
25016428a1aSJason M. Bills             messages::queryParameterOutOfRange(
25116428a1aSJason M. Bills                 res, std::to_string(top), "$top",
25216428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
25316428a1aSJason M. Bills             return false;
25416428a1aSJason M. Bills         }
25516428a1aSJason M. Bills     }
25616428a1aSJason M. Bills     return true;
25716428a1aSJason M. Bills }
25816428a1aSJason M. Bills 
25916428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
26016428a1aSJason M. Bills {
26116428a1aSJason M. Bills     int ret = 0;
26216428a1aSJason M. Bills     static uint64_t prevTs = 0;
26316428a1aSJason M. Bills     static int index = 0;
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 
294*95820184SJason M. Bills static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID)
295*95820184SJason M. Bills {
296*95820184SJason M. Bills     static uint64_t prevTs = 0;
297*95820184SJason M. Bills     static int index = 0;
298*95820184SJason M. Bills     // Get the entry timestamp
299*95820184SJason M. Bills     uint64_t curTs = 0;
300*95820184SJason M. Bills     std::tm timeStruct = {};
301*95820184SJason M. Bills     std::istringstream entryStream(logEntry);
302*95820184SJason M. Bills     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
303*95820184SJason M. Bills     {
304*95820184SJason M. Bills         curTs = std::mktime(&timeStruct);
305*95820184SJason M. Bills     }
306*95820184SJason M. Bills     // If the timestamp isn't unique, increment the index
307*95820184SJason M. Bills     if (curTs == prevTs)
308*95820184SJason M. Bills     {
309*95820184SJason M. Bills         index++;
310*95820184SJason M. Bills     }
311*95820184SJason M. Bills     else
312*95820184SJason M. Bills     {
313*95820184SJason M. Bills         // Otherwise, reset it
314*95820184SJason M. Bills         index = 0;
315*95820184SJason M. Bills     }
316*95820184SJason M. Bills     // Save the timestamp
317*95820184SJason M. Bills     prevTs = curTs;
318*95820184SJason M. Bills 
319*95820184SJason M. Bills     entryID = std::to_string(curTs);
320*95820184SJason M. Bills     if (index > 0)
321*95820184SJason M. Bills     {
322*95820184SJason M. Bills         entryID += "_" + std::to_string(index);
323*95820184SJason M. Bills     }
324*95820184SJason M. Bills     return true;
325*95820184SJason M. Bills }
326*95820184SJason M. Bills 
32716428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
32816428a1aSJason M. Bills                                uint64_t &timestamp, uint16_t &index)
32916428a1aSJason M. Bills {
33016428a1aSJason M. Bills     if (entryID.empty())
33116428a1aSJason M. Bills     {
33216428a1aSJason M. Bills         return false;
33316428a1aSJason M. Bills     }
33416428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
33539e77504SEd Tanous     std::string_view tsStr(entryID);
33616428a1aSJason M. Bills 
33716428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
33816428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
33916428a1aSJason M. Bills     {
34016428a1aSJason M. Bills         // Timestamp has an index
34116428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
34239e77504SEd Tanous         std::string_view indexStr(entryID);
34316428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
34416428a1aSJason M. Bills         std::size_t pos;
34516428a1aSJason M. Bills         try
34616428a1aSJason M. Bills         {
34739e77504SEd Tanous             index = std::stoul(std::string(indexStr), &pos);
34816428a1aSJason M. Bills         }
349b01bf299SEd Tanous         catch (std::invalid_argument)
35016428a1aSJason M. Bills         {
35116428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
35216428a1aSJason M. Bills             return false;
35316428a1aSJason M. Bills         }
354b01bf299SEd Tanous         catch (std::out_of_range)
35516428a1aSJason M. Bills         {
35616428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
35716428a1aSJason M. Bills             return false;
35816428a1aSJason M. Bills         }
35916428a1aSJason M. Bills         if (pos != indexStr.size())
36016428a1aSJason M. Bills         {
36116428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
36216428a1aSJason M. Bills             return false;
36316428a1aSJason M. Bills         }
36416428a1aSJason M. Bills     }
36516428a1aSJason M. Bills     // Timestamp has no index
36616428a1aSJason M. Bills     std::size_t pos;
36716428a1aSJason M. Bills     try
36816428a1aSJason M. Bills     {
36939e77504SEd Tanous         timestamp = std::stoull(std::string(tsStr), &pos);
37016428a1aSJason M. Bills     }
371b01bf299SEd Tanous     catch (std::invalid_argument)
37216428a1aSJason M. Bills     {
37316428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
37416428a1aSJason M. Bills         return false;
37516428a1aSJason M. Bills     }
376b01bf299SEd Tanous     catch (std::out_of_range)
37716428a1aSJason M. Bills     {
37816428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
37916428a1aSJason M. Bills         return false;
38016428a1aSJason M. Bills     }
38116428a1aSJason M. Bills     if (pos != tsStr.size())
38216428a1aSJason M. Bills     {
38316428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
38416428a1aSJason M. Bills         return false;
38516428a1aSJason M. Bills     }
38616428a1aSJason M. Bills     return true;
38716428a1aSJason M. Bills }
38816428a1aSJason M. Bills 
389*95820184SJason M. Bills static bool
390*95820184SJason M. Bills     getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
391*95820184SJason M. Bills {
392*95820184SJason M. Bills     static const std::filesystem::path redfishLogDir = "/var/log";
393*95820184SJason M. Bills     static const std::string redfishLogFilename = "redfish";
394*95820184SJason M. Bills 
395*95820184SJason M. Bills     // Loop through the directory looking for redfish log files
396*95820184SJason M. Bills     for (const std::filesystem::directory_entry &dirEnt :
397*95820184SJason M. Bills          std::filesystem::directory_iterator(redfishLogDir))
398*95820184SJason M. Bills     {
399*95820184SJason M. Bills         // If we find a redfish log file, save the path
400*95820184SJason M. Bills         std::string filename = dirEnt.path().filename();
401*95820184SJason M. Bills         if (boost::starts_with(filename, redfishLogFilename))
402*95820184SJason M. Bills         {
403*95820184SJason M. Bills             redfishLogFiles.emplace_back(redfishLogDir / filename);
404*95820184SJason M. Bills         }
405*95820184SJason M. Bills     }
406*95820184SJason M. Bills     // As the log files rotate, they are appended with a ".#" that is higher for
407*95820184SJason M. Bills     // the older logs. Since we don't expect more than 10 log files, we
408*95820184SJason M. Bills     // can just sort the list to get them in order from newest to oldest
409*95820184SJason M. Bills     std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
410*95820184SJason M. Bills 
411*95820184SJason M. Bills     return !redfishLogFiles.empty();
412*95820184SJason M. Bills }
413*95820184SJason M. Bills 
414c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
4151da66f75SEd Tanous {
4161da66f75SEd Tanous   public:
4171da66f75SEd Tanous     template <typename CrowApp>
418c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
419029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/")
420c4bf6374SJason M. Bills     {
421c4bf6374SJason M. Bills         entityPrivileges = {
422c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
423c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
424c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
425c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
426c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
427c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
428c4bf6374SJason M. Bills     }
429c4bf6374SJason M. Bills 
430c4bf6374SJason M. Bills   private:
431c4bf6374SJason M. Bills     /**
432c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
433c4bf6374SJason M. Bills      */
434c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
435c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
436c4bf6374SJason M. Bills     {
437c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
438c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
439c4bf6374SJason M. Bills         // it has a duplicate entry for members
440c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
441c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
442c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
443c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
444c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
445029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices";
446c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
447c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
448c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
449c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
450c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
451029573d4SEd Tanous         logServiceArray.push_back(
452029573d4SEd Tanous             {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
453d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
454d53dd41fSJason M. Bills         logServiceArray.push_back(
455cb92c03bSAndrew Geissler             {{ "@odata.id",
456424c4176SJason M. Bills                "/redfish/v1/Systems/system/LogServices/Crashdump" }});
457d53dd41fSJason M. Bills #endif
458c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
459c4bf6374SJason M. Bills             logServiceArray.size();
460c4bf6374SJason M. Bills     }
461c4bf6374SJason M. Bills };
462c4bf6374SJason M. Bills 
463c4bf6374SJason M. Bills class EventLogService : public Node
464c4bf6374SJason M. Bills {
465c4bf6374SJason M. Bills   public:
466c4bf6374SJason M. Bills     template <typename CrowApp>
467c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
468029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
469c4bf6374SJason M. Bills     {
470c4bf6374SJason M. Bills         entityPrivileges = {
471c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
472c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
473c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
474c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
475c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
476c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
477c4bf6374SJason M. Bills     }
478c4bf6374SJason M. Bills 
479c4bf6374SJason M. Bills   private:
480c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
481c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
482c4bf6374SJason M. Bills     {
483c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
484c4bf6374SJason M. Bills 
485c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
486029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog";
487c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
488c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
489c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
490c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
491c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
492c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
493c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
494c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
495c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
496c4bf6374SJason M. Bills             {"@odata.id",
497029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
498c4bf6374SJason M. Bills     }
499c4bf6374SJason M. Bills };
500c4bf6374SJason M. Bills 
501*95820184SJason M. Bills static int fillEventLogEntryJson(const std::string &logEntryID,
502*95820184SJason M. Bills                                  const std::string logEntry,
503*95820184SJason M. Bills                                  nlohmann::json &logEntryJson)
504c4bf6374SJason M. Bills {
505*95820184SJason M. Bills     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
506*95820184SJason M. Bills     // Use split to separate the entry into its fields
507*95820184SJason M. Bills     std::vector<std::string> logEntryFields;
508*95820184SJason M. Bills     boost::split(logEntryFields, logEntry, boost::is_any_of(" ,"),
509*95820184SJason M. Bills                  boost::token_compress_on);
510*95820184SJason M. Bills     // We need at least a MessageId to be valid
511*95820184SJason M. Bills     if (logEntryFields.size() < 2)
512*95820184SJason M. Bills     {
513*95820184SJason M. Bills         return 1;
514*95820184SJason M. Bills     }
515*95820184SJason M. Bills     std::string &timestamp = logEntryFields[0];
516*95820184SJason M. Bills     std::string &messageID = logEntryFields[1];
517*95820184SJason M. Bills     std::string &messageArgsStart = logEntryFields[2];
518*95820184SJason M. Bills     std::size_t messageArgsSize = logEntryFields.size() - 2;
519*95820184SJason M. Bills 
5204851d45dSJason M. Bills     // Get the Message from the MessageRegistry
5214851d45dSJason M. Bills     const message_registries::Message *message =
5224851d45dSJason M. Bills         message_registries::getMessage(messageID);
523c4bf6374SJason M. Bills 
5244851d45dSJason M. Bills     std::string msg;
5254851d45dSJason M. Bills     std::string severity;
5264851d45dSJason M. Bills     if (message != nullptr)
527c4bf6374SJason M. Bills     {
5284851d45dSJason M. Bills         msg = message->message;
5294851d45dSJason M. Bills         severity = message->severity;
530c4bf6374SJason M. Bills     }
531c4bf6374SJason M. Bills 
532*95820184SJason M. Bills     // Get the MessageArgs from the log
533*95820184SJason M. Bills     boost::beast::span messageArgs(&messageArgsStart, messageArgsSize);
534c4bf6374SJason M. Bills 
5354851d45dSJason M. Bills     // Fill the MessageArgs into the Message
536*95820184SJason M. Bills     int i = 0;
537*95820184SJason M. Bills     for (const std::string &messageArg : messageArgs)
5384851d45dSJason M. Bills     {
539*95820184SJason M. Bills         std::string argStr = "%" + std::to_string(++i);
5404851d45dSJason M. Bills         size_t argPos = msg.find(argStr);
5414851d45dSJason M. Bills         if (argPos != std::string::npos)
5424851d45dSJason M. Bills         {
543*95820184SJason M. Bills             msg.replace(argPos, argStr.length(), messageArg);
5444851d45dSJason M. Bills         }
5454851d45dSJason M. Bills     }
5464851d45dSJason M. Bills 
547*95820184SJason M. Bills     // Get the Created time from the timestamp. The log timestamp is in RFC3339
548*95820184SJason M. Bills     // format which matches the Redfish format except for the fractional seconds
549*95820184SJason M. Bills     // between the '.' and the '+', so just remove them.
550*95820184SJason M. Bills     std::size_t dot = timestamp.find_first_of(".");
551*95820184SJason M. Bills     std::size_t plus = timestamp.find_first_of("+");
552*95820184SJason M. Bills     if (dot != std::string::npos && plus != std::string::npos)
553c4bf6374SJason M. Bills     {
554*95820184SJason M. Bills         timestamp.erase(dot, plus - dot);
555c4bf6374SJason M. Bills     }
556c4bf6374SJason M. Bills 
557c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
558*95820184SJason M. Bills     logEntryJson = {
559cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
560c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
561029573d4SEd Tanous         {"@odata.id",
562*95820184SJason M. Bills          "/redfish/v1/Systems/system/LogServices/EventLog/Entries/#" +
563*95820184SJason M. Bills              logEntryID},
564c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
565*95820184SJason M. Bills         {"Id", logEntryID},
566*95820184SJason M. Bills         {"Message", std::move(msg)},
567*95820184SJason M. Bills         {"MessageId", std::move(messageID)},
568c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
569c4bf6374SJason M. Bills         {"EntryType", "Event"},
570*95820184SJason M. Bills         {"Severity", std::move(severity)},
571*95820184SJason M. Bills         {"Created", std::move(timestamp)}};
572c4bf6374SJason M. Bills     return 0;
573c4bf6374SJason M. Bills }
574c4bf6374SJason M. Bills 
575c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
576c4bf6374SJason M. Bills {
577c4bf6374SJason M. Bills   public:
578c4bf6374SJason M. Bills     template <typename CrowApp>
579c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
580029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
581c4bf6374SJason M. Bills     {
582c4bf6374SJason M. Bills         entityPrivileges = {
583c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
584c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
585c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
586c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
587c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
588c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
589c4bf6374SJason M. Bills     }
590c4bf6374SJason M. Bills 
591c4bf6374SJason M. Bills   private:
592c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
593c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
594c4bf6374SJason M. Bills     {
595c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
596c4bf6374SJason M. Bills         long skip = 0;
597c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
598c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
599c4bf6374SJason M. Bills         {
600c4bf6374SJason M. Bills             return;
601c4bf6374SJason M. Bills         }
602c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
603c4bf6374SJason M. Bills         {
604c4bf6374SJason M. Bills             return;
605c4bf6374SJason M. Bills         }
606c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
607c4bf6374SJason M. Bills         // it has a duplicate entry for members
608c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
609c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
610c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
611c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
612c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
613029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
614c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
615c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
616c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
617cb92c03bSAndrew Geissler 
618cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
619c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
620c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
621*95820184SJason M. Bills         // Go through the log files and create a unique ID for each entry
622*95820184SJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
623*95820184SJason M. Bills         getRedfishLogFiles(redfishLogFiles);
624b01bf299SEd Tanous         uint64_t entryCount = 0;
625*95820184SJason M. Bills 
626*95820184SJason M. Bills         // Oldest logs are in the last file, so start there and loop backwards
627*95820184SJason M. Bills         for (size_t i = redfishLogFiles.size() - 1; i >= 0; i--)
628c4bf6374SJason M. Bills         {
629*95820184SJason M. Bills             std::ifstream logStream(redfishLogFiles[i]);
630*95820184SJason M. Bills             if (!logStream.is_open())
631c4bf6374SJason M. Bills             {
632c4bf6374SJason M. Bills                 continue;
633c4bf6374SJason M. Bills             }
634c4bf6374SJason M. Bills 
635*95820184SJason M. Bills             std::string logEntry;
636*95820184SJason M. Bills             while (std::getline(logStream, logEntry))
637*95820184SJason M. Bills             {
638c4bf6374SJason M. Bills                 entryCount++;
639c4bf6374SJason M. Bills                 // Handle paging using skip (number of entries to skip from the
640c4bf6374SJason M. Bills                 // start) and top (number of entries to display)
641c4bf6374SJason M. Bills                 if (entryCount <= skip || entryCount > skip + top)
642c4bf6374SJason M. Bills                 {
643c4bf6374SJason M. Bills                     continue;
644c4bf6374SJason M. Bills                 }
645c4bf6374SJason M. Bills 
646c4bf6374SJason M. Bills                 std::string idStr;
647*95820184SJason M. Bills                 if (!getUniqueEntryID(logEntry, idStr))
648c4bf6374SJason M. Bills                 {
649c4bf6374SJason M. Bills                     continue;
650c4bf6374SJason M. Bills                 }
651c4bf6374SJason M. Bills 
652c4bf6374SJason M. Bills                 logEntryArray.push_back({});
653c4bf6374SJason M. Bills                 nlohmann::json &bmcLogEntry = logEntryArray.back();
654*95820184SJason M. Bills                 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
655c4bf6374SJason M. Bills                 {
656c4bf6374SJason M. Bills                     messages::internalError(asyncResp->res);
657c4bf6374SJason M. Bills                     return;
658c4bf6374SJason M. Bills                 }
659c4bf6374SJason M. Bills             }
660*95820184SJason M. Bills         }
661c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
662c4bf6374SJason M. Bills         if (skip + top < entryCount)
663c4bf6374SJason M. Bills         {
664c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
665*95820184SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/EventLog/"
666*95820184SJason M. Bills                 "Entries?$skip=" +
667c4bf6374SJason M. Bills                 std::to_string(skip + top);
668c4bf6374SJason M. Bills         }
669cb92c03bSAndrew Geissler #else
670cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
671cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
672cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
673cb92c03bSAndrew Geissler             [asyncResp](const boost::system::error_code ec,
674cb92c03bSAndrew Geissler                         GetManagedObjectsType &resp) {
675cb92c03bSAndrew Geissler                 if (ec)
676cb92c03bSAndrew Geissler                 {
677cb92c03bSAndrew Geissler                     // TODO Handle for specific error code
678cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
679cb92c03bSAndrew Geissler                         << "getLogEntriesIfaceData resp_handler got error "
680cb92c03bSAndrew Geissler                         << ec;
681cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
682cb92c03bSAndrew Geissler                     return;
683cb92c03bSAndrew Geissler                 }
684cb92c03bSAndrew Geissler                 nlohmann::json &entriesArray =
685cb92c03bSAndrew Geissler                     asyncResp->res.jsonValue["Members"];
686cb92c03bSAndrew Geissler                 entriesArray = nlohmann::json::array();
687cb92c03bSAndrew Geissler                 for (auto &objectPath : resp)
688cb92c03bSAndrew Geissler                 {
689cb92c03bSAndrew Geissler                     for (auto &interfaceMap : objectPath.second)
690cb92c03bSAndrew Geissler                     {
691cb92c03bSAndrew Geissler                         if (interfaceMap.first !=
692cb92c03bSAndrew Geissler                             "xyz.openbmc_project.Logging.Entry")
693cb92c03bSAndrew Geissler                         {
694cb92c03bSAndrew Geissler                             BMCWEB_LOG_DEBUG << "Bailing early on "
695cb92c03bSAndrew Geissler                                              << interfaceMap.first;
696cb92c03bSAndrew Geissler                             continue;
697cb92c03bSAndrew Geissler                         }
698cb92c03bSAndrew Geissler                         entriesArray.push_back({});
699cb92c03bSAndrew Geissler                         nlohmann::json &thisEntry = entriesArray.back();
700cb92c03bSAndrew Geissler                         uint32_t *id;
701cb92c03bSAndrew Geissler                         std::time_t timestamp;
702cb92c03bSAndrew Geissler                         std::string *severity, *message;
703cb92c03bSAndrew Geissler                         bool *resolved;
704cb92c03bSAndrew Geissler                         for (auto &propertyMap : interfaceMap.second)
705cb92c03bSAndrew Geissler                         {
706cb92c03bSAndrew Geissler                             if (propertyMap.first == "Id")
707cb92c03bSAndrew Geissler                             {
708cb92c03bSAndrew Geissler                                 id = sdbusplus::message::variant_ns::get_if<
709cb92c03bSAndrew Geissler                                     uint32_t>(&propertyMap.second);
710cb92c03bSAndrew Geissler                                 if (id == nullptr)
711cb92c03bSAndrew Geissler                                 {
712cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
713cb92c03bSAndrew Geissler                                                               "Id");
714cb92c03bSAndrew Geissler                                 }
715cb92c03bSAndrew Geissler                             }
716cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Timestamp")
717cb92c03bSAndrew Geissler                             {
718cb92c03bSAndrew Geissler                                 const uint64_t *millisTimeStamp =
719cb92c03bSAndrew Geissler                                     std::get_if<uint64_t>(&propertyMap.second);
720cb92c03bSAndrew Geissler                                 if (millisTimeStamp == nullptr)
721cb92c03bSAndrew Geissler                                 {
722cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
723cb92c03bSAndrew Geissler                                                               "Timestamp");
724cb92c03bSAndrew Geissler                                 }
725cb92c03bSAndrew Geissler                                 // Retrieve Created property with format:
726cb92c03bSAndrew Geissler                                 // yyyy-mm-ddThh:mm:ss
727cb92c03bSAndrew Geissler                                 std::chrono::milliseconds chronoTimeStamp(
728cb92c03bSAndrew Geissler                                     *millisTimeStamp);
729cb92c03bSAndrew Geissler                                 timestamp =
730cb92c03bSAndrew Geissler                                     std::chrono::duration_cast<
731cb92c03bSAndrew Geissler                                         std::chrono::seconds>(chronoTimeStamp)
732cb92c03bSAndrew Geissler                                         .count();
733cb92c03bSAndrew Geissler                             }
734cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Severity")
735cb92c03bSAndrew Geissler                             {
736cb92c03bSAndrew Geissler                                 severity = std::get_if<std::string>(
737cb92c03bSAndrew Geissler                                     &propertyMap.second);
738cb92c03bSAndrew Geissler                                 if (severity == nullptr)
739cb92c03bSAndrew Geissler                                 {
740cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
741cb92c03bSAndrew Geissler                                                               "Severity");
742cb92c03bSAndrew Geissler                                 }
743cb92c03bSAndrew Geissler                             }
744cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Message")
745cb92c03bSAndrew Geissler                             {
746cb92c03bSAndrew Geissler                                 message = std::get_if<std::string>(
747cb92c03bSAndrew Geissler                                     &propertyMap.second);
748cb92c03bSAndrew Geissler                                 if (message == nullptr)
749cb92c03bSAndrew Geissler                                 {
750cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
751cb92c03bSAndrew Geissler                                                               "Message");
752cb92c03bSAndrew Geissler                                 }
753cb92c03bSAndrew Geissler                             }
754cb92c03bSAndrew Geissler                         }
755cb92c03bSAndrew Geissler                         thisEntry = {
756cb92c03bSAndrew Geissler                             {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
757cb92c03bSAndrew Geissler                             {"@odata.context", "/redfish/v1/"
758cb92c03bSAndrew Geissler                                                "$metadata#LogEntry.LogEntry"},
759cb92c03bSAndrew Geissler                             {"@odata.id",
760cb92c03bSAndrew Geissler                              "/redfish/v1/Systems/system/LogServices/EventLog/"
761cb92c03bSAndrew Geissler                              "Entries/" +
762cb92c03bSAndrew Geissler                                  std::to_string(*id)},
763cb92c03bSAndrew Geissler                             {"Name", "System DBus Event Log Entry"},
764cb92c03bSAndrew Geissler                             {"Id", std::to_string(*id)},
765cb92c03bSAndrew Geissler                             {"Message", *message},
766cb92c03bSAndrew Geissler                             {"EntryType", "Event"},
767cb92c03bSAndrew Geissler                             {"Severity",
768cb92c03bSAndrew Geissler                              translateSeverityDbusToRedfish(*severity)},
769cb92c03bSAndrew Geissler                             {"Created", crow::utility::getDateTime(timestamp)}};
770cb92c03bSAndrew Geissler                     }
771cb92c03bSAndrew Geissler                 }
772cb92c03bSAndrew Geissler                 std::sort(entriesArray.begin(), entriesArray.end(),
773cb92c03bSAndrew Geissler                           [](const nlohmann::json &left,
774cb92c03bSAndrew Geissler                              const nlohmann::json &right) {
775cb92c03bSAndrew Geissler                               return (left["Id"] <= right["Id"]);
776cb92c03bSAndrew Geissler                           });
777cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue["Members@odata.count"] =
778cb92c03bSAndrew Geissler                     entriesArray.size();
779cb92c03bSAndrew Geissler             },
780cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
781cb92c03bSAndrew Geissler             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
782cb92c03bSAndrew Geissler #endif
783c4bf6374SJason M. Bills     }
784c4bf6374SJason M. Bills };
785c4bf6374SJason M. Bills 
786c4bf6374SJason M. Bills class EventLogEntry : public Node
787c4bf6374SJason M. Bills {
788c4bf6374SJason M. Bills   public:
789c4bf6374SJason M. Bills     EventLogEntry(CrowApp &app) :
790c4bf6374SJason M. Bills         Node(app,
791029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
792029573d4SEd Tanous              std::string())
793c4bf6374SJason M. Bills     {
794c4bf6374SJason M. Bills         entityPrivileges = {
795c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
796c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
797c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
798c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
799c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
800c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
801c4bf6374SJason M. Bills     }
802c4bf6374SJason M. Bills 
803c4bf6374SJason M. Bills   private:
804c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
805c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
806c4bf6374SJason M. Bills     {
807c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
808029573d4SEd Tanous         if (params.size() != 1)
809c4bf6374SJason M. Bills         {
810c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
811c4bf6374SJason M. Bills             return;
812c4bf6374SJason M. Bills         }
813029573d4SEd Tanous         const std::string &entryID = params[0];
814cb92c03bSAndrew Geissler 
815*95820184SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
816cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
817cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
818cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
819cb92c03bSAndrew Geissler             [asyncResp, entryID](const boost::system::error_code ec,
820cb92c03bSAndrew Geissler                                  GetManagedPropertyType &resp) {
821cb92c03bSAndrew Geissler                 if (ec)
822cb92c03bSAndrew Geissler                 {
823cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
824cb92c03bSAndrew Geissler                         << "EventLogEntry (DBus) resp_handler got error " << ec;
825cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
826cb92c03bSAndrew Geissler                     return;
827cb92c03bSAndrew Geissler                 }
828cb92c03bSAndrew Geissler                 uint32_t *id;
829cb92c03bSAndrew Geissler                 std::time_t timestamp;
830cb92c03bSAndrew Geissler                 std::string *severity, *message;
831cb92c03bSAndrew Geissler                 bool *resolved;
832cb92c03bSAndrew Geissler                 for (auto &propertyMap : resp)
833cb92c03bSAndrew Geissler                 {
834cb92c03bSAndrew Geissler                     if (propertyMap.first == "Id")
835cb92c03bSAndrew Geissler                     {
836cb92c03bSAndrew Geissler                         id = std::get_if<uint32_t>(&propertyMap.second);
837cb92c03bSAndrew Geissler                         if (id == nullptr)
838cb92c03bSAndrew Geissler                         {
839cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res, "Id");
840cb92c03bSAndrew Geissler                         }
841cb92c03bSAndrew Geissler                     }
842cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Timestamp")
843cb92c03bSAndrew Geissler                     {
844cb92c03bSAndrew Geissler                         const uint64_t *millisTimeStamp =
845cb92c03bSAndrew Geissler                             std::get_if<uint64_t>(&propertyMap.second);
846cb92c03bSAndrew Geissler                         if (millisTimeStamp == nullptr)
847cb92c03bSAndrew Geissler                         {
848cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
849cb92c03bSAndrew Geissler                                                       "Timestamp");
850cb92c03bSAndrew Geissler                         }
851cb92c03bSAndrew Geissler                         // Retrieve Created property with format:
852cb92c03bSAndrew Geissler                         // yyyy-mm-ddThh:mm:ss
853cb92c03bSAndrew Geissler                         std::chrono::milliseconds chronoTimeStamp(
854cb92c03bSAndrew Geissler                             *millisTimeStamp);
855cb92c03bSAndrew Geissler                         timestamp =
856cb92c03bSAndrew Geissler                             std::chrono::duration_cast<std::chrono::seconds>(
857cb92c03bSAndrew Geissler                                 chronoTimeStamp)
858cb92c03bSAndrew Geissler                                 .count();
859cb92c03bSAndrew Geissler                     }
860cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Severity")
861cb92c03bSAndrew Geissler                     {
862cb92c03bSAndrew Geissler                         severity =
863cb92c03bSAndrew Geissler                             std::get_if<std::string>(&propertyMap.second);
864cb92c03bSAndrew Geissler                         if (severity == nullptr)
865cb92c03bSAndrew Geissler                         {
866cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
867cb92c03bSAndrew Geissler                                                       "Severity");
868cb92c03bSAndrew Geissler                         }
869cb92c03bSAndrew Geissler                     }
870cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Message")
871cb92c03bSAndrew Geissler                     {
872cb92c03bSAndrew Geissler                         message = std::get_if<std::string>(&propertyMap.second);
873cb92c03bSAndrew Geissler                         if (message == nullptr)
874cb92c03bSAndrew Geissler                         {
875cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
876cb92c03bSAndrew Geissler                                                       "Message");
877cb92c03bSAndrew Geissler                         }
878cb92c03bSAndrew Geissler                     }
879cb92c03bSAndrew Geissler                 }
880cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue = {
881cb92c03bSAndrew Geissler                     {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
882cb92c03bSAndrew Geissler                     {"@odata.context", "/redfish/v1/"
883cb92c03bSAndrew Geissler                                        "$metadata#LogEntry.LogEntry"},
884cb92c03bSAndrew Geissler                     {"@odata.id",
885cb92c03bSAndrew Geissler                      "/redfish/v1/Systems/system/LogServices/EventLog/"
886cb92c03bSAndrew Geissler                      "Entries/" +
887cb92c03bSAndrew Geissler                          std::to_string(*id)},
888cb92c03bSAndrew Geissler                     {"Name", "System DBus Event Log Entry"},
889cb92c03bSAndrew Geissler                     {"Id", std::to_string(*id)},
890cb92c03bSAndrew Geissler                     {"Message", *message},
891cb92c03bSAndrew Geissler                     {"EntryType", "Event"},
892cb92c03bSAndrew Geissler                     {"Severity", translateSeverityDbusToRedfish(*severity)},
893*95820184SJason M. Bills                     { "Created",
894*95820184SJason M. Bills                       crow::utility::getDateTime(timestamp) }};
895cb92c03bSAndrew Geissler             },
896cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging",
897cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging/entry/" + entryID,
898cb92c03bSAndrew Geissler             "org.freedesktop.DBus.Properties", "GetAll",
899cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging.Entry");
900cb92c03bSAndrew Geissler #endif
901c4bf6374SJason M. Bills     }
902c4bf6374SJason M. Bills };
903c4bf6374SJason M. Bills 
904c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
905c4bf6374SJason M. Bills {
906c4bf6374SJason M. Bills   public:
907c4bf6374SJason M. Bills     template <typename CrowApp>
908c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
9094ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
9101da66f75SEd Tanous     {
9111da66f75SEd Tanous         entityPrivileges = {
912e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
913e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
914e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
915e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
916e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
917e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9181da66f75SEd Tanous     }
9191da66f75SEd Tanous 
9201da66f75SEd Tanous   private:
9211da66f75SEd Tanous     /**
9221da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9231da66f75SEd Tanous      */
9241da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9251da66f75SEd Tanous                const std::vector<std::string> &params) override
9261da66f75SEd Tanous     {
927e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9281da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
9291da66f75SEd Tanous         // it has a duplicate entry for members
930e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
9311da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
932e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
933c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
934e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
935e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
936e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
937e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
9381da66f75SEd Tanous             "Collection of LogServices for this Manager";
939c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
940c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
941c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
942c4bf6374SJason M. Bills         logServiceArray.push_back(
943cb92c03bSAndrew Geissler             {{ "@odata.id",
944cb92c03bSAndrew Geissler                "/redfish/v1/Managers/bmc/LogServices/Journal" }});
945c4bf6374SJason M. Bills #endif
946e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
947c4bf6374SJason M. Bills             logServiceArray.size();
9481da66f75SEd Tanous     }
9491da66f75SEd Tanous };
9501da66f75SEd Tanous 
951c4bf6374SJason M. Bills class BMCJournalLogService : public Node
9521da66f75SEd Tanous {
9531da66f75SEd Tanous   public:
9541da66f75SEd Tanous     template <typename CrowApp>
955c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
956c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
957e1f26343SJason M. Bills     {
958e1f26343SJason M. Bills         entityPrivileges = {
959e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
960e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
961e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
962e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
963e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
964e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
965e1f26343SJason M. Bills     }
966e1f26343SJason M. Bills 
967e1f26343SJason M. Bills   private:
968e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
969e1f26343SJason M. Bills                const std::vector<std::string> &params) override
970e1f26343SJason M. Bills     {
971e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
972e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
973e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
9740f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
9750f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
976e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
977e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
978c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
979c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
980c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
981e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
982cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
983cd50aa42SJason M. Bills             {"@odata.id",
984cd50aa42SJason M. Bills              "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}};
985e1f26343SJason M. Bills     }
986e1f26343SJason M. Bills };
987e1f26343SJason M. Bills 
988c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
989e1f26343SJason M. Bills                                       sd_journal *journal,
990c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
991e1f26343SJason M. Bills {
992e1f26343SJason M. Bills     // Get the Log Entry contents
993e1f26343SJason M. Bills     int ret = 0;
994e1f26343SJason M. Bills 
99539e77504SEd Tanous     std::string_view msg;
99616428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
997e1f26343SJason M. Bills     if (ret < 0)
998e1f26343SJason M. Bills     {
999e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1000e1f26343SJason M. Bills         return 1;
1001e1f26343SJason M. Bills     }
1002e1f26343SJason M. Bills 
1003e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
1004e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
100516428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
1006e1f26343SJason M. Bills     if (ret < 0)
1007e1f26343SJason M. Bills     {
1008e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
1009e1f26343SJason M. Bills         return 1;
1010e1f26343SJason M. Bills     }
1011e1f26343SJason M. Bills 
1012e1f26343SJason M. Bills     // Get the Created time from the timestamp
101316428a1aSJason M. Bills     std::string entryTimeStr;
101416428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
1015e1f26343SJason M. Bills     {
101616428a1aSJason M. Bills         return 1;
1017e1f26343SJason M. Bills     }
1018e1f26343SJason M. Bills 
1019e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
1020c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
1021cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1022e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1023c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1024c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
1025e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
1026c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
102716428a1aSJason M. Bills         {"Message", msg},
1028e1f26343SJason M. Bills         {"EntryType", "Oem"},
1029e1f26343SJason M. Bills         {"Severity",
1030e1f26343SJason M. Bills          severity <= 2 ? "Critical"
1031e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
1032e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
1033e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
1034e1f26343SJason M. Bills     return 0;
1035e1f26343SJason M. Bills }
1036e1f26343SJason M. Bills 
1037c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
1038e1f26343SJason M. Bills {
1039e1f26343SJason M. Bills   public:
1040e1f26343SJason M. Bills     template <typename CrowApp>
1041c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
1042c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
1043e1f26343SJason M. Bills     {
1044e1f26343SJason M. Bills         entityPrivileges = {
1045e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1046e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1047e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1048e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1049e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1050e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1051e1f26343SJason M. Bills     }
1052e1f26343SJason M. Bills 
1053e1f26343SJason M. Bills   private:
1054e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1055e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1056e1f26343SJason M. Bills     {
1057e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1058193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
1059193ad2faSJason M. Bills         long skip = 0;
1060193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
106116428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
1062193ad2faSJason M. Bills         {
1063193ad2faSJason M. Bills             return;
1064193ad2faSJason M. Bills         }
106516428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
1066193ad2faSJason M. Bills         {
1067193ad2faSJason M. Bills             return;
1068193ad2faSJason M. Bills         }
1069e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
1070e1f26343SJason M. Bills         // it has a duplicate entry for members
1071e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1072e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
10730f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
10740f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1075e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1076c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1077e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1078c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1079e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1080e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
1081e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
10820f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
10830f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
1084e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1085e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
1086e1f26343SJason M. Bills 
1087e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
1088e1f26343SJason M. Bills         // for each entry
1089e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1090e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1091e1f26343SJason M. Bills         if (ret < 0)
1092e1f26343SJason M. Bills         {
1093e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1094f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1095e1f26343SJason M. Bills             return;
1096e1f26343SJason M. Bills         }
1097e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1098e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1099e1f26343SJason M. Bills         journalTmp = nullptr;
1100b01bf299SEd Tanous         uint64_t entryCount = 0;
1101e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
1102e1f26343SJason M. Bills         {
1103193ad2faSJason M. Bills             entryCount++;
1104193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
1105193ad2faSJason M. Bills             // start) and top (number of entries to display)
1106193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
1107193ad2faSJason M. Bills             {
1108193ad2faSJason M. Bills                 continue;
1109193ad2faSJason M. Bills             }
1110193ad2faSJason M. Bills 
111116428a1aSJason M. Bills             std::string idStr;
111216428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
1113e1f26343SJason M. Bills             {
1114e1f26343SJason M. Bills                 continue;
1115e1f26343SJason M. Bills             }
1116e1f26343SJason M. Bills 
1117e1f26343SJason M. Bills             logEntryArray.push_back({});
1118c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1119c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1120c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
1121e1f26343SJason M. Bills             {
1122f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
1123e1f26343SJason M. Bills                 return;
1124e1f26343SJason M. Bills             }
1125e1f26343SJason M. Bills         }
1126193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1127193ad2faSJason M. Bills         if (skip + top < entryCount)
1128193ad2faSJason M. Bills         {
1129193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
1130c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
1131193ad2faSJason M. Bills                 std::to_string(skip + top);
1132193ad2faSJason M. Bills         }
1133e1f26343SJason M. Bills     }
1134e1f26343SJason M. Bills };
1135e1f26343SJason M. Bills 
1136c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
1137e1f26343SJason M. Bills {
1138e1f26343SJason M. Bills   public:
1139c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
1140c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
1141e1f26343SJason M. Bills              std::string())
1142e1f26343SJason M. Bills     {
1143e1f26343SJason M. Bills         entityPrivileges = {
1144e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1145e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1146e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1147e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1148e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1149e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1150e1f26343SJason M. Bills     }
1151e1f26343SJason M. Bills 
1152e1f26343SJason M. Bills   private:
1153e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1154e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1155e1f26343SJason M. Bills     {
1156e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1157e1f26343SJason M. Bills         if (params.size() != 1)
1158e1f26343SJason M. Bills         {
1159f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1160e1f26343SJason M. Bills             return;
1161e1f26343SJason M. Bills         }
116216428a1aSJason M. Bills         const std::string &entryID = params[0];
1163e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
1164e1f26343SJason M. Bills         uint64_t ts = 0;
1165e1f26343SJason M. Bills         uint16_t index = 0;
116616428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
1167e1f26343SJason M. Bills         {
116816428a1aSJason M. Bills             return;
1169e1f26343SJason M. Bills         }
1170e1f26343SJason M. Bills 
1171e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1172e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1173e1f26343SJason M. Bills         if (ret < 0)
1174e1f26343SJason M. Bills         {
1175e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1176f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1177e1f26343SJason M. Bills             return;
1178e1f26343SJason M. Bills         }
1179e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1180e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1181e1f26343SJason M. Bills         journalTmp = nullptr;
1182e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
1183e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1184e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
1185e1f26343SJason M. Bills         {
1186e1f26343SJason M. Bills             sd_journal_next(journal.get());
1187e1f26343SJason M. Bills         }
1188c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
1189c4bf6374SJason M. Bills         std::string idStr;
1190c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
1191c4bf6374SJason M. Bills         {
1192c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
1193c4bf6374SJason M. Bills             return;
1194c4bf6374SJason M. Bills         }
1195c4bf6374SJason M. Bills 
1196c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1197e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
1198e1f26343SJason M. Bills         {
1199f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1200e1f26343SJason M. Bills             return;
1201e1f26343SJason M. Bills         }
1202e1f26343SJason M. Bills     }
1203e1f26343SJason M. Bills };
1204e1f26343SJason M. Bills 
1205424c4176SJason M. Bills class CrashdumpService : public Node
1206e1f26343SJason M. Bills {
1207e1f26343SJason M. Bills   public:
1208e1f26343SJason M. Bills     template <typename CrowApp>
1209424c4176SJason M. Bills     CrashdumpService(CrowApp &app) :
1210424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
12111da66f75SEd Tanous     {
12121da66f75SEd Tanous         entityPrivileges = {
1213e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1214e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1215e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1216e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1217e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1218e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
12191da66f75SEd Tanous     }
12201da66f75SEd Tanous 
12211da66f75SEd Tanous   private:
12221da66f75SEd Tanous     /**
12231da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
12241da66f75SEd Tanous      */
12251da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
12261da66f75SEd Tanous                const std::vector<std::string> &params) override
12271da66f75SEd Tanous     {
1228e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
12291da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
12300f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
1231424c4176SJason M. Bills             "/redfish/v1/Systems/system/LogServices/Crashdump";
1232e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1233e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
1234e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1235c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1236424c4176SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1237424c4176SJason M. Bills         asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1238424c4176SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Crashdump";
1239e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1240e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
1241cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1242cd50aa42SJason M. Bills             {"@odata.id",
1243424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
1244e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
12451da66f75SEd Tanous             {"Oem",
1246424c4176SJason M. Bills              {{"#Crashdump.OnDemand",
1247424c4176SJason M. Bills                {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1248424c4176SJason M. Bills                            "Actions/Oem/Crashdump.OnDemand"}}}}}};
12491da66f75SEd Tanous 
12501da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
1251e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
1252424c4176SJason M. Bills             {"#Crashdump.SendRawPeci",
1253cb92c03bSAndrew Geissler              { { "target",
1254424c4176SJason M. Bills                  "/redfish/v1/Systems/system/LogServices/Crashdump/"
1255424c4176SJason M. Bills                  "Actions/Oem/Crashdump.SendRawPeci" } }});
12561da66f75SEd Tanous #endif
12571da66f75SEd Tanous     }
12581da66f75SEd Tanous };
12591da66f75SEd Tanous 
1260424c4176SJason M. Bills class CrashdumpEntryCollection : public Node
12611da66f75SEd Tanous {
12621da66f75SEd Tanous   public:
12631da66f75SEd Tanous     template <typename CrowApp>
1264424c4176SJason M. Bills     CrashdumpEntryCollection(CrowApp &app) :
1265424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
12661da66f75SEd Tanous     {
12671da66f75SEd Tanous         entityPrivileges = {
1268e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1269e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1270e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1271e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1272e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1273e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
12741da66f75SEd Tanous     }
12751da66f75SEd Tanous 
12761da66f75SEd Tanous   private:
12771da66f75SEd Tanous     /**
12781da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
12791da66f75SEd Tanous      */
12801da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
12811da66f75SEd Tanous                const std::vector<std::string> &params) override
12821da66f75SEd Tanous     {
1283e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
12841da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
12851da66f75SEd Tanous         // it has a duplicate entry for members
1286e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
1287e1f26343SJason M. Bills                                          const boost::system::error_code ec,
12881da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
12891da66f75SEd Tanous             if (ec)
12901da66f75SEd Tanous             {
12911da66f75SEd Tanous                 if (ec.value() !=
12921da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
12931da66f75SEd Tanous                 {
12941da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
12951da66f75SEd Tanous                                      << ec.message();
1296f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
12971da66f75SEd Tanous                     return;
12981da66f75SEd Tanous                 }
12991da66f75SEd Tanous             }
1300e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
13011da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
13020f74e643SEd Tanous             asyncResp->res.jsonValue["@odata.id"] =
1303424c4176SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
1304e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1305d53dd41fSJason M. Bills                 "/redfish/v1/"
1306d53dd41fSJason M. Bills                 "$metadata#LogEntryCollection.LogEntryCollection";
1307424c4176SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
1308e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1309424c4176SJason M. Bills                 "Collection of Crashdump Entries";
1310e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1311e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
13121da66f75SEd Tanous             for (const std::string &objpath : resp)
13131da66f75SEd Tanous             {
131448e4639eSJason M. Bills                 // Don't list the on-demand log
1315424c4176SJason M. Bills                 if (objpath.compare(CrashdumpOnDemandPath) == 0)
13161da66f75SEd Tanous                 {
13171da66f75SEd Tanous                     continue;
13181da66f75SEd Tanous                 }
13194ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
13204ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
13211da66f75SEd Tanous                 {
1322e1f26343SJason M. Bills                     logEntryArray.push_back(
1323d53dd41fSJason M. Bills                         {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1324424c4176SJason M. Bills                                        "Crashdump/Entries/" +
13254ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
13261da66f75SEd Tanous                 }
13271da66f75SEd Tanous             }
1328e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1329e1f26343SJason M. Bills                 logEntryArray.size();
13301da66f75SEd Tanous         };
13311da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
13321da66f75SEd Tanous             std::move(getLogEntriesCallback),
13331da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
13341da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
13351da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
1336424c4176SJason M. Bills             std::array<const char *, 1>{CrashdumpInterface});
13371da66f75SEd Tanous     }
13381da66f75SEd Tanous };
13391da66f75SEd Tanous 
1340424c4176SJason M. Bills std::string getLogCreatedTime(const nlohmann::json &Crashdump)
13411da66f75SEd Tanous {
1342424c4176SJason M. Bills     nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1343424c4176SJason M. Bills     if (cdIt != Crashdump.end())
13441da66f75SEd Tanous     {
1345c4d00437SJason M. Bills         nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1346c4d00437SJason M. Bills         if (siIt != cdIt->end())
13471da66f75SEd Tanous         {
1348c4d00437SJason M. Bills             nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1349c4d00437SJason M. Bills             if (tsIt != siIt->end())
1350c4d00437SJason M. Bills             {
1351c4d00437SJason M. Bills                 const std::string *logTime =
1352c4d00437SJason M. Bills                     tsIt->get_ptr<const std::string *>();
13531da66f75SEd Tanous                 if (logTime != nullptr)
13541da66f75SEd Tanous                 {
13551da66f75SEd Tanous                     return *logTime;
13561da66f75SEd Tanous                 }
13571da66f75SEd Tanous             }
13581da66f75SEd Tanous         }
1359c4d00437SJason M. Bills     }
13601da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
13611da66f75SEd Tanous 
13621da66f75SEd Tanous     return std::string();
13631da66f75SEd Tanous }
13641da66f75SEd Tanous 
1365424c4176SJason M. Bills class CrashdumpEntry : public Node
13661da66f75SEd Tanous {
13671da66f75SEd Tanous   public:
1368424c4176SJason M. Bills     CrashdumpEntry(CrowApp &app) :
1369d53dd41fSJason M. Bills         Node(app,
1370424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
13711da66f75SEd Tanous              std::string())
13721da66f75SEd Tanous     {
13731da66f75SEd Tanous         entityPrivileges = {
1374e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1375e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1376e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1377e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1378e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1379e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
13801da66f75SEd Tanous     }
13811da66f75SEd Tanous 
13821da66f75SEd Tanous   private:
13831da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
13841da66f75SEd Tanous                const std::vector<std::string> &params) override
13851da66f75SEd Tanous     {
1386e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
13871da66f75SEd Tanous         if (params.size() != 1)
13881da66f75SEd Tanous         {
1389f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
13901da66f75SEd Tanous             return;
13911da66f75SEd Tanous         }
1392b01bf299SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1393abf2add6SEd Tanous         auto getStoredLogCallback = [asyncResp, logId](
1394abf2add6SEd Tanous                                         const boost::system::error_code ec,
1395abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
13961da66f75SEd Tanous             if (ec)
13971da66f75SEd Tanous             {
1398abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1399f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14001da66f75SEd Tanous                 return;
14011da66f75SEd Tanous             }
1402abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
14031da66f75SEd Tanous             if (log == nullptr)
14041da66f75SEd Tanous             {
1405f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14061da66f75SEd Tanous                 return;
14071da66f75SEd Tanous             }
14081da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
14091da66f75SEd Tanous             if (j.is_discarded())
14101da66f75SEd Tanous             {
1411f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14121da66f75SEd Tanous                 return;
14131da66f75SEd Tanous             }
14141da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1415e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1416cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1417abf2add6SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
14181da66f75SEd Tanous                 {"@odata.id",
1419424c4176SJason M. Bills                  "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
14204ed77cd5SEd Tanous                      std::to_string(logId)},
1421424c4176SJason M. Bills                 {"Name", "CPU Crashdump"},
14224ed77cd5SEd Tanous                 {"Id", logId},
14231da66f75SEd Tanous                 {"EntryType", "Oem"},
1424424c4176SJason M. Bills                 {"OemRecordFormat", "Intel Crashdump"},
14251da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
14261da66f75SEd Tanous                 {"Created", std::move(t)}};
14271da66f75SEd Tanous         };
14281da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1429424c4176SJason M. Bills             std::move(getStoredLogCallback), CrashdumpObject,
1430424c4176SJason M. Bills             CrashdumpPath + std::string("/") + std::to_string(logId),
1431424c4176SJason M. Bills             "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1432424c4176SJason M. Bills             "Log");
14331da66f75SEd Tanous     }
14341da66f75SEd Tanous };
14351da66f75SEd Tanous 
1436424c4176SJason M. Bills class OnDemandCrashdump : public Node
14371da66f75SEd Tanous {
14381da66f75SEd Tanous   public:
1439424c4176SJason M. Bills     OnDemandCrashdump(CrowApp &app) :
1440424c4176SJason M. Bills         Node(app,
1441424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1442424c4176SJason M. Bills              "Crashdump.OnDemand/")
14431da66f75SEd Tanous     {
14441da66f75SEd Tanous         entityPrivileges = {
1445e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1446e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1447e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1448e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1449e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1450e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
14511da66f75SEd Tanous     }
14521da66f75SEd Tanous 
14531da66f75SEd Tanous   private:
14541da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
14551da66f75SEd Tanous                 const std::vector<std::string> &params) override
14561da66f75SEd Tanous     {
1457e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
145848e4639eSJason M. Bills         static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
14591da66f75SEd Tanous 
146048e4639eSJason M. Bills         // Only allow one OnDemand Log request at a time
146148e4639eSJason M. Bills         if (onDemandLogMatcher != nullptr)
14621da66f75SEd Tanous         {
1463e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1464f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
14651da66f75SEd Tanous             return;
14661da66f75SEd Tanous         }
14671da66f75SEd Tanous         // Make this static so it survives outside this method
14681da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
14691da66f75SEd Tanous 
14701da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1471e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
147248e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
14731da66f75SEd Tanous             if (ec)
14741da66f75SEd Tanous             {
14751da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
14761da66f75SEd Tanous                 // completion.
14771da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
14781da66f75SEd Tanous                 {
14791da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
14801da66f75SEd Tanous                 }
14811da66f75SEd Tanous                 return;
14821da66f75SEd Tanous             }
148348e4639eSJason M. Bills             BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
14841da66f75SEd Tanous 
1485f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
14861da66f75SEd Tanous         });
14871da66f75SEd Tanous 
148848e4639eSJason M. Bills         auto onDemandLogMatcherCallback = [asyncResp](
14891da66f75SEd Tanous                                               sdbusplus::message::message &m) {
149048e4639eSJason M. Bills             BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
14911da66f75SEd Tanous             boost::system::error_code ec;
14921da66f75SEd Tanous             timeout.cancel(ec);
14931da66f75SEd Tanous             if (ec)
14941da66f75SEd Tanous             {
14951da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
14961da66f75SEd Tanous             }
14974ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
14981da66f75SEd Tanous             boost::container::flat_map<
1499abf2add6SEd Tanous                 std::string, boost::container::flat_map<
1500abf2add6SEd Tanous                                  std::string, std::variant<std::string>>>
15014ed77cd5SEd Tanous                 interfacesAdded;
15024ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1503abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(
1504424c4176SJason M. Bills                 &interfacesAdded[CrashdumpInterface]["Log"]);
15051da66f75SEd Tanous             if (log == nullptr)
15061da66f75SEd Tanous             {
1507f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
150848e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
15091da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
15101da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
15111da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
15121da66f75SEd Tanous                 // be the last thing done.
151348e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
15141da66f75SEd Tanous                 return;
15151da66f75SEd Tanous             }
15161da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
15171da66f75SEd Tanous             if (j.is_discarded())
15181da66f75SEd Tanous             {
1519f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
152048e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
15211da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
15221da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
15231da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
15241da66f75SEd Tanous                 // be the last thing done.
152548e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
15261da66f75SEd Tanous                 return;
15271da66f75SEd Tanous             }
15281da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1529e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1530cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
15311da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1532424c4176SJason M. Bills                 {"Name", "CPU Crashdump"},
15331da66f75SEd Tanous                 {"EntryType", "Oem"},
1534424c4176SJason M. Bills                 {"OemRecordFormat", "Intel Crashdump"},
15351da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
15361da66f75SEd Tanous                 {"Created", std::move(t)}};
153748e4639eSJason M. Bills             // Careful with onDemandLogMatcher.  It is a unique_ptr to the
15381da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
15391da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
15401da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
15411da66f75SEd Tanous             // thing done.
154248e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
15431da66f75SEd Tanous         };
154448e4639eSJason M. Bills         onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
15451da66f75SEd Tanous             *crow::connections::systemBus,
15461da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
1547424c4176SJason M. Bills                 sdbusplus::bus::match::rules::argNpath(0,
1548424c4176SJason M. Bills                                                        CrashdumpOnDemandPath),
154948e4639eSJason M. Bills             std::move(onDemandLogMatcherCallback));
15501da66f75SEd Tanous 
155148e4639eSJason M. Bills         auto generateonDemandLogCallback =
1552e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
15531da66f75SEd Tanous                         const std::string &resp) {
15541da66f75SEd Tanous                 if (ec)
15551da66f75SEd Tanous                 {
15561da66f75SEd Tanous                     if (ec.value() ==
15571da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
15581da66f75SEd Tanous                     {
1559f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
15601da66f75SEd Tanous                     }
15611da66f75SEd Tanous                     else
15621da66f75SEd Tanous                     {
1563f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
15641da66f75SEd Tanous                     }
15651da66f75SEd Tanous                     boost::system::error_code timeoutec;
15661da66f75SEd Tanous                     timeout.cancel(timeoutec);
15671da66f75SEd Tanous                     if (timeoutec)
15681da66f75SEd Tanous                     {
15691da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
15701da66f75SEd Tanous                                          << timeoutec;
15711da66f75SEd Tanous                     }
157248e4639eSJason M. Bills                     onDemandLogMatcher = nullptr;
15731da66f75SEd Tanous                     return;
15741da66f75SEd Tanous                 }
15751da66f75SEd Tanous             };
15761da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1577424c4176SJason M. Bills             std::move(generateonDemandLogCallback), CrashdumpObject,
1578424c4176SJason M. Bills             CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
15791da66f75SEd Tanous     }
15801da66f75SEd Tanous };
15811da66f75SEd Tanous 
1582e1f26343SJason M. Bills class SendRawPECI : public Node
15831da66f75SEd Tanous {
15841da66f75SEd Tanous   public:
1585e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
1586424c4176SJason M. Bills         Node(app,
1587424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1588424c4176SJason M. Bills              "Crashdump.SendRawPeci/")
15891da66f75SEd Tanous     {
15901da66f75SEd Tanous         entityPrivileges = {
15911da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
15921da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
15931da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
15941da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
15951da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
15961da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
15971da66f75SEd Tanous     }
15981da66f75SEd Tanous 
15991da66f75SEd Tanous   private:
16001da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
16011da66f75SEd Tanous                 const std::vector<std::string> &params) override
16021da66f75SEd Tanous     {
1603e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1604b1556427SEd Tanous         uint8_t clientAddress = 0;
1605b1556427SEd Tanous         uint8_t readLength = 0;
16061da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
1607b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1608b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
1609b1556427SEd Tanous                                  peciCommand))
16101da66f75SEd Tanous         {
16111da66f75SEd Tanous             return;
16121da66f75SEd Tanous         }
1613b1556427SEd Tanous 
16141da66f75SEd Tanous         // Callback to return the Raw PECI response
1615e1f26343SJason M. Bills         auto sendRawPECICallback =
1616e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
16171da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
16181da66f75SEd Tanous                 if (ec)
16191da66f75SEd Tanous                 {
16201da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
16211da66f75SEd Tanous                                      << ec.message();
1622f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
16231da66f75SEd Tanous                     return;
16241da66f75SEd Tanous                 }
1625e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
16261da66f75SEd Tanous                                             {"PECIResponse", resp}};
16271da66f75SEd Tanous             };
16281da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
16291da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1630424c4176SJason M. Bills             std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1631424c4176SJason M. Bills             CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
16324ed77cd5SEd Tanous             peciCommand);
16331da66f75SEd Tanous     }
16341da66f75SEd Tanous };
16351da66f75SEd Tanous 
1636cb92c03bSAndrew Geissler /**
1637cb92c03bSAndrew Geissler  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1638cb92c03bSAndrew Geissler  */
1639cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node
1640cb92c03bSAndrew Geissler {
1641cb92c03bSAndrew Geissler   public:
1642cb92c03bSAndrew Geissler     DBusLogServiceActionsClear(CrowApp &app) :
1643cb92c03bSAndrew Geissler         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1644cb92c03bSAndrew Geissler                   "LogService.Reset")
1645cb92c03bSAndrew Geissler     {
1646cb92c03bSAndrew Geissler         entityPrivileges = {
1647cb92c03bSAndrew Geissler             {boost::beast::http::verb::get, {{"Login"}}},
1648cb92c03bSAndrew Geissler             {boost::beast::http::verb::head, {{"Login"}}},
1649cb92c03bSAndrew Geissler             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1650cb92c03bSAndrew Geissler             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1651cb92c03bSAndrew Geissler             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1652cb92c03bSAndrew Geissler             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1653cb92c03bSAndrew Geissler     }
1654cb92c03bSAndrew Geissler 
1655cb92c03bSAndrew Geissler   private:
1656cb92c03bSAndrew Geissler     /**
1657cb92c03bSAndrew Geissler      * Function handles POST method request.
1658cb92c03bSAndrew Geissler      * The Clear Log actions does not require any parameter.The action deletes
1659cb92c03bSAndrew Geissler      * all entries found in the Entries collection for this Log Service.
1660cb92c03bSAndrew Geissler      */
1661cb92c03bSAndrew Geissler     void doPost(crow::Response &res, const crow::Request &req,
1662cb92c03bSAndrew Geissler                 const std::vector<std::string> &params) override
1663cb92c03bSAndrew Geissler     {
1664cb92c03bSAndrew Geissler         BMCWEB_LOG_DEBUG << "Do delete all entries.";
1665cb92c03bSAndrew Geissler 
1666cb92c03bSAndrew Geissler         auto asyncResp = std::make_shared<AsyncResp>(res);
1667cb92c03bSAndrew Geissler         // Process response from Logging service.
1668cb92c03bSAndrew Geissler         auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1669cb92c03bSAndrew Geissler             BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1670cb92c03bSAndrew Geissler             if (ec)
1671cb92c03bSAndrew Geissler             {
1672cb92c03bSAndrew Geissler                 // TODO Handle for specific error code
1673cb92c03bSAndrew Geissler                 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1674cb92c03bSAndrew Geissler                 asyncResp->res.result(
1675cb92c03bSAndrew Geissler                     boost::beast::http::status::internal_server_error);
1676cb92c03bSAndrew Geissler                 return;
1677cb92c03bSAndrew Geissler             }
1678cb92c03bSAndrew Geissler 
1679cb92c03bSAndrew Geissler             asyncResp->res.result(boost::beast::http::status::no_content);
1680cb92c03bSAndrew Geissler         };
1681cb92c03bSAndrew Geissler 
1682cb92c03bSAndrew Geissler         // Make call to Logging service to request Clear Log
1683cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
1684cb92c03bSAndrew Geissler             resp_handler, "xyz.openbmc_project.Logging",
1685cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging",
1686cb92c03bSAndrew Geissler             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1687cb92c03bSAndrew Geissler     }
1688cb92c03bSAndrew Geissler };
16891da66f75SEd Tanous } // namespace redfish
1690