xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 4851d45d5eff4fdd1af4478db2ad788b4a3d59ed)
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"
19*4851d45dSJason M. Bills #include "registries.hpp"
20*4851d45dSJason M. Bills #include "registries/base_message_registry.hpp"
21*4851d45dSJason M. Bills #include "registries/openbmc_message_registry.hpp"
221da66f75SEd Tanous 
23e1f26343SJason M. Bills #include <systemd/sd-journal.h>
24e1f26343SJason M. Bills 
25*4851d45dSJason M. Bills #include <boost/algorithm/string/split.hpp>
26*4851d45dSJason 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 
44*4851d45dSJason M. Bills namespace message_registries
45*4851d45dSJason M. Bills {
46*4851d45dSJason M. Bills static const Message *getMessageFromRegistry(
47*4851d45dSJason M. Bills     const std::string &messageKey,
48*4851d45dSJason M. Bills     const boost::beast::span<const MessageEntry> registry)
49*4851d45dSJason M. Bills {
50*4851d45dSJason M. Bills     boost::beast::span<const MessageEntry>::const_iterator messageIt =
51*4851d45dSJason M. Bills         std::find_if(registry.cbegin(), registry.cend(),
52*4851d45dSJason M. Bills                      [&messageKey](const MessageEntry &messageEntry) {
53*4851d45dSJason M. Bills                          return !std::strcmp(messageEntry.first,
54*4851d45dSJason M. Bills                                              messageKey.c_str());
55*4851d45dSJason M. Bills                      });
56*4851d45dSJason M. Bills     if (messageIt != registry.cend())
57*4851d45dSJason M. Bills     {
58*4851d45dSJason M. Bills         return &messageIt->second;
59*4851d45dSJason M. Bills     }
60*4851d45dSJason M. Bills 
61*4851d45dSJason M. Bills     return nullptr;
62*4851d45dSJason M. Bills }
63*4851d45dSJason M. Bills 
64*4851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID)
65*4851d45dSJason M. Bills {
66*4851d45dSJason M. Bills     // Redfish MessageIds are in the form
67*4851d45dSJason M. Bills     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
68*4851d45dSJason M. Bills     // the right Message
69*4851d45dSJason M. Bills     std::vector<std::string> fields;
70*4851d45dSJason M. Bills     fields.reserve(4);
71*4851d45dSJason M. Bills     boost::split(fields, messageID, boost::is_any_of("."));
72*4851d45dSJason M. Bills     std::string &registryName = fields[0];
73*4851d45dSJason M. Bills     std::string &messageKey = fields[3];
74*4851d45dSJason M. Bills 
75*4851d45dSJason M. Bills     // Find the right registry and check it for the MessageKey
76*4851d45dSJason M. Bills     if (std::string(base::header.registryPrefix) == registryName)
77*4851d45dSJason M. Bills     {
78*4851d45dSJason M. Bills         return getMessageFromRegistry(
79*4851d45dSJason M. Bills             messageKey, boost::beast::span<const MessageEntry>(base::registry));
80*4851d45dSJason M. Bills     }
81*4851d45dSJason M. Bills     if (std::string(openbmc::header.registryPrefix) == registryName)
82*4851d45dSJason M. Bills     {
83*4851d45dSJason M. Bills         return getMessageFromRegistry(
84*4851d45dSJason M. Bills             messageKey,
85*4851d45dSJason M. Bills             boost::beast::span<const MessageEntry>(openbmc::registry));
86*4851d45dSJason M. Bills     }
87*4851d45dSJason M. Bills     return nullptr;
88*4851d45dSJason M. Bills }
89*4851d45dSJason M. Bills } // namespace message_registries
90*4851d45dSJason 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 
29416428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
29516428a1aSJason M. Bills                                uint64_t &timestamp, uint16_t &index)
29616428a1aSJason M. Bills {
29716428a1aSJason M. Bills     if (entryID.empty())
29816428a1aSJason M. Bills     {
29916428a1aSJason M. Bills         return false;
30016428a1aSJason M. Bills     }
30116428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
30239e77504SEd Tanous     std::string_view tsStr(entryID);
30316428a1aSJason M. Bills 
30416428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
30516428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
30616428a1aSJason M. Bills     {
30716428a1aSJason M. Bills         // Timestamp has an index
30816428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
30939e77504SEd Tanous         std::string_view indexStr(entryID);
31016428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
31116428a1aSJason M. Bills         std::size_t pos;
31216428a1aSJason M. Bills         try
31316428a1aSJason M. Bills         {
31439e77504SEd Tanous             index = std::stoul(std::string(indexStr), &pos);
31516428a1aSJason M. Bills         }
316b01bf299SEd Tanous         catch (std::invalid_argument)
31716428a1aSJason M. Bills         {
31816428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
31916428a1aSJason M. Bills             return false;
32016428a1aSJason M. Bills         }
321b01bf299SEd Tanous         catch (std::out_of_range)
32216428a1aSJason M. Bills         {
32316428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
32416428a1aSJason M. Bills             return false;
32516428a1aSJason M. Bills         }
32616428a1aSJason M. Bills         if (pos != indexStr.size())
32716428a1aSJason M. Bills         {
32816428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
32916428a1aSJason M. Bills             return false;
33016428a1aSJason M. Bills         }
33116428a1aSJason M. Bills     }
33216428a1aSJason M. Bills     // Timestamp has no index
33316428a1aSJason M. Bills     std::size_t pos;
33416428a1aSJason M. Bills     try
33516428a1aSJason M. Bills     {
33639e77504SEd Tanous         timestamp = std::stoull(std::string(tsStr), &pos);
33716428a1aSJason M. Bills     }
338b01bf299SEd Tanous     catch (std::invalid_argument)
33916428a1aSJason M. Bills     {
34016428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
34116428a1aSJason M. Bills         return false;
34216428a1aSJason M. Bills     }
343b01bf299SEd Tanous     catch (std::out_of_range)
34416428a1aSJason M. Bills     {
34516428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
34616428a1aSJason M. Bills         return false;
34716428a1aSJason M. Bills     }
34816428a1aSJason M. Bills     if (pos != tsStr.size())
34916428a1aSJason M. Bills     {
35016428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
35116428a1aSJason M. Bills         return false;
35216428a1aSJason M. Bills     }
35316428a1aSJason M. Bills     return true;
35416428a1aSJason M. Bills }
35516428a1aSJason M. Bills 
356c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
3571da66f75SEd Tanous {
3581da66f75SEd Tanous   public:
3591da66f75SEd Tanous     template <typename CrowApp>
360c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
361029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/")
362c4bf6374SJason M. Bills     {
363c4bf6374SJason M. Bills         entityPrivileges = {
364c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
365c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
366c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
367c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
368c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
369c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
370c4bf6374SJason M. Bills     }
371c4bf6374SJason M. Bills 
372c4bf6374SJason M. Bills   private:
373c4bf6374SJason M. Bills     /**
374c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
375c4bf6374SJason M. Bills      */
376c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
377c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
378c4bf6374SJason M. Bills     {
379c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
380c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
381c4bf6374SJason M. Bills         // it has a duplicate entry for members
382c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
383c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
384c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
385c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
386c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
387029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices";
388c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
389c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
390c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
391c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
392c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
393029573d4SEd Tanous         logServiceArray.push_back(
394029573d4SEd Tanous             {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
395d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
396d53dd41fSJason M. Bills         logServiceArray.push_back(
397cb92c03bSAndrew Geissler             {{ "@odata.id",
398424c4176SJason M. Bills                "/redfish/v1/Systems/system/LogServices/Crashdump" }});
399d53dd41fSJason M. Bills #endif
400c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
401c4bf6374SJason M. Bills             logServiceArray.size();
402c4bf6374SJason M. Bills     }
403c4bf6374SJason M. Bills };
404c4bf6374SJason M. Bills 
405c4bf6374SJason M. Bills class EventLogService : public Node
406c4bf6374SJason M. Bills {
407c4bf6374SJason M. Bills   public:
408c4bf6374SJason M. Bills     template <typename CrowApp>
409c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
410029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
411c4bf6374SJason M. Bills     {
412c4bf6374SJason M. Bills         entityPrivileges = {
413c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
414c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
415c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
416c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
417c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
418c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
419c4bf6374SJason M. Bills     }
420c4bf6374SJason M. Bills 
421c4bf6374SJason M. Bills   private:
422c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
423c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
424c4bf6374SJason M. Bills     {
425c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
426c4bf6374SJason M. Bills 
427c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
428029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog";
429c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
430c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
431c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
432c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
433c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
434c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
435c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
436c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
437c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
438c4bf6374SJason M. Bills             {"@odata.id",
439029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
440c4bf6374SJason M. Bills     }
441c4bf6374SJason M. Bills };
442c4bf6374SJason M. Bills 
443029573d4SEd Tanous static int fillEventLogEntryJson(const std::string &bmcLogEntryID,
44439e77504SEd Tanous                                  const std::string_view &messageID,
445c4bf6374SJason M. Bills                                  sd_journal *journal,
446c4bf6374SJason M. Bills                                  nlohmann::json &bmcLogEntryJson)
447c4bf6374SJason M. Bills {
448*4851d45dSJason M. Bills     // Get the Message from the MessageRegistry
449*4851d45dSJason M. Bills     const message_registries::Message *message =
450*4851d45dSJason M. Bills         message_registries::getMessage(messageID);
451c4bf6374SJason M. Bills 
452*4851d45dSJason M. Bills     std::string msg;
453*4851d45dSJason M. Bills     std::string severity;
454*4851d45dSJason M. Bills     if (message != nullptr)
455c4bf6374SJason M. Bills     {
456*4851d45dSJason M. Bills         msg = message->message;
457*4851d45dSJason M. Bills         severity = message->severity;
458c4bf6374SJason M. Bills     }
459c4bf6374SJason M. Bills 
460c4bf6374SJason M. Bills     // Get the MessageArgs from the journal entry by finding all of the
461c4bf6374SJason M. Bills     // REDFISH_MESSAGE_ARG_x fields
462c4bf6374SJason M. Bills     const void *data;
463c4bf6374SJason M. Bills     size_t length;
464c4bf6374SJason M. Bills     std::vector<std::string> messageArgs;
465c4bf6374SJason M. Bills     SD_JOURNAL_FOREACH_DATA(journal, data, length)
466c4bf6374SJason M. Bills     {
46739e77504SEd Tanous         std::string_view field(static_cast<const char *>(data), length);
46839e77504SEd Tanous         if (boost::starts_with(field, "REDFISH_MESSAGE_ARG_"))
469c4bf6374SJason M. Bills         {
470c4bf6374SJason M. Bills             // Get the Arg number from the field name
471c4bf6374SJason M. Bills             field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
472c4bf6374SJason M. Bills             if (field.empty())
473c4bf6374SJason M. Bills             {
474c4bf6374SJason M. Bills                 continue;
475c4bf6374SJason M. Bills             }
476b01bf299SEd Tanous             int argNum = std::strtoul(field.data(), nullptr, 10);
477b01bf299SEd Tanous             if (argNum == 0)
478c4bf6374SJason M. Bills             {
479c4bf6374SJason M. Bills                 continue;
480c4bf6374SJason M. Bills             }
481c4bf6374SJason M. Bills             // Get the Arg value after the "=" character.
482c4bf6374SJason M. Bills             field.remove_prefix(std::min(field.find("=") + 1, field.size()));
483c4bf6374SJason M. Bills             // Make sure we have enough space in messageArgs
484c4bf6374SJason M. Bills             if (argNum > messageArgs.size())
485c4bf6374SJason M. Bills             {
486b01bf299SEd Tanous                 messageArgs.resize(argNum);
487c4bf6374SJason M. Bills             }
48839e77504SEd Tanous             messageArgs[argNum - 1] = std::string(field);
489c4bf6374SJason M. Bills         }
490c4bf6374SJason M. Bills     }
491c4bf6374SJason M. Bills 
492*4851d45dSJason M. Bills     // Fill the MessageArgs into the Message
493*4851d45dSJason M. Bills     for (size_t i = 0; i < messageArgs.size(); i++)
494*4851d45dSJason M. Bills     {
495*4851d45dSJason M. Bills         std::string argStr = "%" + std::to_string(i + 1);
496*4851d45dSJason M. Bills         size_t argPos = msg.find(argStr);
497*4851d45dSJason M. Bills         if (argPos != std::string::npos)
498*4851d45dSJason M. Bills         {
499*4851d45dSJason M. Bills             msg.replace(argPos, argStr.length(), messageArgs[i]);
500*4851d45dSJason M. Bills         }
501*4851d45dSJason M. Bills     }
502*4851d45dSJason M. Bills 
503c4bf6374SJason M. Bills     // Get the Created time from the timestamp
504c4bf6374SJason M. Bills     std::string entryTimeStr;
505c4bf6374SJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
506c4bf6374SJason M. Bills     {
507c4bf6374SJason M. Bills         return 1;
508c4bf6374SJason M. Bills     }
509c4bf6374SJason M. Bills 
510c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
511c4bf6374SJason M. Bills     bmcLogEntryJson = {
512cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
513c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
514029573d4SEd Tanous         {"@odata.id",
515029573d4SEd Tanous          "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
516029573d4SEd Tanous              bmcLogEntryID},
517c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
518c4bf6374SJason M. Bills         {"Id", bmcLogEntryID},
519c4bf6374SJason M. Bills         {"Message", msg},
520c4bf6374SJason M. Bills         {"MessageId", messageID},
521c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
522c4bf6374SJason M. Bills         {"EntryType", "Event"},
523*4851d45dSJason M. Bills         {"Severity", severity},
524c4bf6374SJason M. Bills         {"Created", std::move(entryTimeStr)}};
525c4bf6374SJason M. Bills     return 0;
526c4bf6374SJason M. Bills }
527c4bf6374SJason M. Bills 
528c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
529c4bf6374SJason M. Bills {
530c4bf6374SJason M. Bills   public:
531c4bf6374SJason M. Bills     template <typename CrowApp>
532c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
533029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
534c4bf6374SJason M. Bills     {
535c4bf6374SJason M. Bills         entityPrivileges = {
536c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
537c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
538c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
539c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
540c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
541c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
542c4bf6374SJason M. Bills     }
543c4bf6374SJason M. Bills 
544c4bf6374SJason M. Bills   private:
545c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
546c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
547c4bf6374SJason M. Bills     {
548c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
549c4bf6374SJason M. Bills         long skip = 0;
550c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
551c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
552c4bf6374SJason M. Bills         {
553c4bf6374SJason M. Bills             return;
554c4bf6374SJason M. Bills         }
555c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
556c4bf6374SJason M. Bills         {
557c4bf6374SJason M. Bills             return;
558c4bf6374SJason M. Bills         }
559c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
560c4bf6374SJason M. Bills         // it has a duplicate entry for members
561c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
562c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
563c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
564c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
565c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
566029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
567c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
568c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
569c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
570cb92c03bSAndrew Geissler 
571cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
572c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
573c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
574c4bf6374SJason M. Bills         // Go through the journal and create a unique ID for each entry
575c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
576c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
577c4bf6374SJason M. Bills         if (ret < 0)
578c4bf6374SJason M. Bills         {
579c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
580c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
581c4bf6374SJason M. Bills             return;
582c4bf6374SJason M. Bills         }
583c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
584c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
585c4bf6374SJason M. Bills         journalTmp = nullptr;
586b01bf299SEd Tanous         uint64_t entryCount = 0;
587c4bf6374SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
588c4bf6374SJason M. Bills         {
589c4bf6374SJason M. Bills             // Look for only journal entries that contain a REDFISH_MESSAGE_ID
590c4bf6374SJason M. Bills             // field
59139e77504SEd Tanous             std::string_view messageID;
592c4bf6374SJason M. Bills             ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID",
593c4bf6374SJason M. Bills                                      messageID);
594c4bf6374SJason M. Bills             if (ret < 0)
595c4bf6374SJason M. Bills             {
596c4bf6374SJason M. Bills                 continue;
597c4bf6374SJason M. Bills             }
598c4bf6374SJason M. Bills 
599c4bf6374SJason M. Bills             entryCount++;
600c4bf6374SJason M. Bills             // Handle paging using skip (number of entries to skip from the
601c4bf6374SJason M. Bills             // start) and top (number of entries to display)
602c4bf6374SJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
603c4bf6374SJason M. Bills             {
604c4bf6374SJason M. Bills                 continue;
605c4bf6374SJason M. Bills             }
606c4bf6374SJason M. Bills 
607c4bf6374SJason M. Bills             std::string idStr;
608c4bf6374SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
609c4bf6374SJason M. Bills             {
610c4bf6374SJason M. Bills                 continue;
611c4bf6374SJason M. Bills             }
612c4bf6374SJason M. Bills 
613c4bf6374SJason M. Bills             logEntryArray.push_back({});
614c4bf6374SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
615029573d4SEd Tanous             if (fillEventLogEntryJson(idStr, messageID, journal.get(),
616c4bf6374SJason M. Bills                                       bmcLogEntry) != 0)
617c4bf6374SJason M. Bills             {
618c4bf6374SJason M. Bills                 messages::internalError(asyncResp->res);
619c4bf6374SJason M. Bills                 return;
620c4bf6374SJason M. Bills             }
621c4bf6374SJason M. Bills         }
622c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
623c4bf6374SJason M. Bills         if (skip + top < entryCount)
624c4bf6374SJason M. Bills         {
625c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
626c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
627c4bf6374SJason M. Bills                 std::to_string(skip + top);
628c4bf6374SJason M. Bills         }
629cb92c03bSAndrew Geissler #else
630cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
631cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
632cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
633cb92c03bSAndrew Geissler             [asyncResp](const boost::system::error_code ec,
634cb92c03bSAndrew Geissler                         GetManagedObjectsType &resp) {
635cb92c03bSAndrew Geissler                 if (ec)
636cb92c03bSAndrew Geissler                 {
637cb92c03bSAndrew Geissler                     // TODO Handle for specific error code
638cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
639cb92c03bSAndrew Geissler                         << "getLogEntriesIfaceData resp_handler got error "
640cb92c03bSAndrew Geissler                         << ec;
641cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
642cb92c03bSAndrew Geissler                     return;
643cb92c03bSAndrew Geissler                 }
644cb92c03bSAndrew Geissler                 nlohmann::json &entriesArray =
645cb92c03bSAndrew Geissler                     asyncResp->res.jsonValue["Members"];
646cb92c03bSAndrew Geissler                 entriesArray = nlohmann::json::array();
647cb92c03bSAndrew Geissler                 for (auto &objectPath : resp)
648cb92c03bSAndrew Geissler                 {
649cb92c03bSAndrew Geissler                     for (auto &interfaceMap : objectPath.second)
650cb92c03bSAndrew Geissler                     {
651cb92c03bSAndrew Geissler                         if (interfaceMap.first !=
652cb92c03bSAndrew Geissler                             "xyz.openbmc_project.Logging.Entry")
653cb92c03bSAndrew Geissler                         {
654cb92c03bSAndrew Geissler                             BMCWEB_LOG_DEBUG << "Bailing early on "
655cb92c03bSAndrew Geissler                                              << interfaceMap.first;
656cb92c03bSAndrew Geissler                             continue;
657cb92c03bSAndrew Geissler                         }
658cb92c03bSAndrew Geissler                         entriesArray.push_back({});
659cb92c03bSAndrew Geissler                         nlohmann::json &thisEntry = entriesArray.back();
660cb92c03bSAndrew Geissler                         uint32_t *id;
661cb92c03bSAndrew Geissler                         std::time_t timestamp;
662cb92c03bSAndrew Geissler                         std::string *severity, *message;
663cb92c03bSAndrew Geissler                         bool *resolved;
664cb92c03bSAndrew Geissler                         for (auto &propertyMap : interfaceMap.second)
665cb92c03bSAndrew Geissler                         {
666cb92c03bSAndrew Geissler                             if (propertyMap.first == "Id")
667cb92c03bSAndrew Geissler                             {
668cb92c03bSAndrew Geissler                                 id = sdbusplus::message::variant_ns::get_if<
669cb92c03bSAndrew Geissler                                     uint32_t>(&propertyMap.second);
670cb92c03bSAndrew Geissler                                 if (id == nullptr)
671cb92c03bSAndrew Geissler                                 {
672cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
673cb92c03bSAndrew Geissler                                                               "Id");
674cb92c03bSAndrew Geissler                                 }
675cb92c03bSAndrew Geissler                             }
676cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Timestamp")
677cb92c03bSAndrew Geissler                             {
678cb92c03bSAndrew Geissler                                 const uint64_t *millisTimeStamp =
679cb92c03bSAndrew Geissler                                     std::get_if<uint64_t>(&propertyMap.second);
680cb92c03bSAndrew Geissler                                 if (millisTimeStamp == nullptr)
681cb92c03bSAndrew Geissler                                 {
682cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
683cb92c03bSAndrew Geissler                                                               "Timestamp");
684cb92c03bSAndrew Geissler                                 }
685cb92c03bSAndrew Geissler                                 // Retrieve Created property with format:
686cb92c03bSAndrew Geissler                                 // yyyy-mm-ddThh:mm:ss
687cb92c03bSAndrew Geissler                                 std::chrono::milliseconds chronoTimeStamp(
688cb92c03bSAndrew Geissler                                     *millisTimeStamp);
689cb92c03bSAndrew Geissler                                 timestamp =
690cb92c03bSAndrew Geissler                                     std::chrono::duration_cast<
691cb92c03bSAndrew Geissler                                         std::chrono::seconds>(chronoTimeStamp)
692cb92c03bSAndrew Geissler                                         .count();
693cb92c03bSAndrew Geissler                             }
694cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Severity")
695cb92c03bSAndrew Geissler                             {
696cb92c03bSAndrew Geissler                                 severity = std::get_if<std::string>(
697cb92c03bSAndrew Geissler                                     &propertyMap.second);
698cb92c03bSAndrew Geissler                                 if (severity == nullptr)
699cb92c03bSAndrew Geissler                                 {
700cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
701cb92c03bSAndrew Geissler                                                               "Severity");
702cb92c03bSAndrew Geissler                                 }
703cb92c03bSAndrew Geissler                             }
704cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Message")
705cb92c03bSAndrew Geissler                             {
706cb92c03bSAndrew Geissler                                 message = std::get_if<std::string>(
707cb92c03bSAndrew Geissler                                     &propertyMap.second);
708cb92c03bSAndrew Geissler                                 if (message == nullptr)
709cb92c03bSAndrew Geissler                                 {
710cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
711cb92c03bSAndrew Geissler                                                               "Message");
712cb92c03bSAndrew Geissler                                 }
713cb92c03bSAndrew Geissler                             }
714cb92c03bSAndrew Geissler                         }
715cb92c03bSAndrew Geissler                         thisEntry = {
716cb92c03bSAndrew Geissler                             {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
717cb92c03bSAndrew Geissler                             {"@odata.context", "/redfish/v1/"
718cb92c03bSAndrew Geissler                                                "$metadata#LogEntry.LogEntry"},
719cb92c03bSAndrew Geissler                             {"@odata.id",
720cb92c03bSAndrew Geissler                              "/redfish/v1/Systems/system/LogServices/EventLog/"
721cb92c03bSAndrew Geissler                              "Entries/" +
722cb92c03bSAndrew Geissler                                  std::to_string(*id)},
723cb92c03bSAndrew Geissler                             {"Name", "System DBus Event Log Entry"},
724cb92c03bSAndrew Geissler                             {"Id", std::to_string(*id)},
725cb92c03bSAndrew Geissler                             {"Message", *message},
726cb92c03bSAndrew Geissler                             {"EntryType", "Event"},
727cb92c03bSAndrew Geissler                             {"Severity",
728cb92c03bSAndrew Geissler                              translateSeverityDbusToRedfish(*severity)},
729cb92c03bSAndrew Geissler                             {"Created", crow::utility::getDateTime(timestamp)}};
730cb92c03bSAndrew Geissler                     }
731cb92c03bSAndrew Geissler                 }
732cb92c03bSAndrew Geissler                 std::sort(entriesArray.begin(), entriesArray.end(),
733cb92c03bSAndrew Geissler                           [](const nlohmann::json &left,
734cb92c03bSAndrew Geissler                              const nlohmann::json &right) {
735cb92c03bSAndrew Geissler                               return (left["Id"] <= right["Id"]);
736cb92c03bSAndrew Geissler                           });
737cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue["Members@odata.count"] =
738cb92c03bSAndrew Geissler                     entriesArray.size();
739cb92c03bSAndrew Geissler             },
740cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
741cb92c03bSAndrew Geissler             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
742cb92c03bSAndrew Geissler #endif
743c4bf6374SJason M. Bills     }
744c4bf6374SJason M. Bills };
745c4bf6374SJason M. Bills 
746c4bf6374SJason M. Bills class EventLogEntry : public Node
747c4bf6374SJason M. Bills {
748c4bf6374SJason M. Bills   public:
749c4bf6374SJason M. Bills     EventLogEntry(CrowApp &app) :
750c4bf6374SJason M. Bills         Node(app,
751029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
752029573d4SEd Tanous              std::string())
753c4bf6374SJason M. Bills     {
754c4bf6374SJason M. Bills         entityPrivileges = {
755c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
756c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
757c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
758c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
759c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
760c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
761c4bf6374SJason M. Bills     }
762c4bf6374SJason M. Bills 
763c4bf6374SJason M. Bills   private:
764c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
765c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
766c4bf6374SJason M. Bills     {
767c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
768029573d4SEd Tanous         if (params.size() != 1)
769c4bf6374SJason M. Bills         {
770c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
771c4bf6374SJason M. Bills             return;
772c4bf6374SJason M. Bills         }
773029573d4SEd Tanous         const std::string &entryID = params[0];
774cb92c03bSAndrew Geissler 
775cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
776c4bf6374SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
777c4bf6374SJason M. Bills         uint64_t ts = 0;
778c4bf6374SJason M. Bills         uint16_t index = 0;
779c4bf6374SJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
780c4bf6374SJason M. Bills         {
781c4bf6374SJason M. Bills             return;
782c4bf6374SJason M. Bills         }
783c4bf6374SJason M. Bills 
784c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
785c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
786c4bf6374SJason M. Bills         if (ret < 0)
787c4bf6374SJason M. Bills         {
788c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
789c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
790c4bf6374SJason M. Bills             return;
791c4bf6374SJason M. Bills         }
792c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
793c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
794c4bf6374SJason M. Bills         journalTmp = nullptr;
795c4bf6374SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
796c4bf6374SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
797c4bf6374SJason M. Bills         for (int i = 0; i <= index; i++)
798c4bf6374SJason M. Bills         {
799c4bf6374SJason M. Bills             sd_journal_next(journal.get());
800c4bf6374SJason M. Bills         }
801c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
802c4bf6374SJason M. Bills         std::string idStr;
803c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
804c4bf6374SJason M. Bills         {
805c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
806c4bf6374SJason M. Bills             return;
807c4bf6374SJason M. Bills         }
808c4bf6374SJason M. Bills 
809cd50aa42SJason M. Bills         // only use journal entries that contain a REDFISH_MESSAGE_ID field
81039e77504SEd Tanous         std::string_view messageID;
811c4bf6374SJason M. Bills         ret =
812c4bf6374SJason M. Bills             getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
813c4bf6374SJason M. Bills         if (ret < 0)
814c4bf6374SJason M. Bills         {
815029573d4SEd Tanous             messages::resourceNotFound(asyncResp->res, "LogEntry", "system");
816c4bf6374SJason M. Bills             return;
817c4bf6374SJason M. Bills         }
818c4bf6374SJason M. Bills 
819029573d4SEd Tanous         if (fillEventLogEntryJson(entryID, messageID, journal.get(),
820c4bf6374SJason M. Bills                                   asyncResp->res.jsonValue) != 0)
821c4bf6374SJason M. Bills         {
822c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
823c4bf6374SJason M. Bills             return;
824c4bf6374SJason M. Bills         }
825cb92c03bSAndrew Geissler #else
826cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
827cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
828cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
829cb92c03bSAndrew Geissler             [asyncResp, entryID](const boost::system::error_code ec,
830cb92c03bSAndrew Geissler                                  GetManagedPropertyType &resp) {
831cb92c03bSAndrew Geissler                 if (ec)
832cb92c03bSAndrew Geissler                 {
833cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
834cb92c03bSAndrew Geissler                         << "EventLogEntry (DBus) resp_handler got error " << ec;
835cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
836cb92c03bSAndrew Geissler                     return;
837cb92c03bSAndrew Geissler                 }
838cb92c03bSAndrew Geissler                 uint32_t *id;
839cb92c03bSAndrew Geissler                 std::time_t timestamp;
840cb92c03bSAndrew Geissler                 std::string *severity, *message;
841cb92c03bSAndrew Geissler                 bool *resolved;
842cb92c03bSAndrew Geissler                 for (auto &propertyMap : resp)
843cb92c03bSAndrew Geissler                 {
844cb92c03bSAndrew Geissler                     if (propertyMap.first == "Id")
845cb92c03bSAndrew Geissler                     {
846cb92c03bSAndrew Geissler                         id = std::get_if<uint32_t>(&propertyMap.second);
847cb92c03bSAndrew Geissler                         if (id == nullptr)
848cb92c03bSAndrew Geissler                         {
849cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res, "Id");
850cb92c03bSAndrew Geissler                         }
851cb92c03bSAndrew Geissler                     }
852cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Timestamp")
853cb92c03bSAndrew Geissler                     {
854cb92c03bSAndrew Geissler                         const uint64_t *millisTimeStamp =
855cb92c03bSAndrew Geissler                             std::get_if<uint64_t>(&propertyMap.second);
856cb92c03bSAndrew Geissler                         if (millisTimeStamp == nullptr)
857cb92c03bSAndrew Geissler                         {
858cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
859cb92c03bSAndrew Geissler                                                       "Timestamp");
860cb92c03bSAndrew Geissler                         }
861cb92c03bSAndrew Geissler                         // Retrieve Created property with format:
862cb92c03bSAndrew Geissler                         // yyyy-mm-ddThh:mm:ss
863cb92c03bSAndrew Geissler                         std::chrono::milliseconds chronoTimeStamp(
864cb92c03bSAndrew Geissler                             *millisTimeStamp);
865cb92c03bSAndrew Geissler                         timestamp =
866cb92c03bSAndrew Geissler                             std::chrono::duration_cast<std::chrono::seconds>(
867cb92c03bSAndrew Geissler                                 chronoTimeStamp)
868cb92c03bSAndrew Geissler                                 .count();
869cb92c03bSAndrew Geissler                     }
870cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Severity")
871cb92c03bSAndrew Geissler                     {
872cb92c03bSAndrew Geissler                         severity =
873cb92c03bSAndrew Geissler                             std::get_if<std::string>(&propertyMap.second);
874cb92c03bSAndrew Geissler                         if (severity == nullptr)
875cb92c03bSAndrew Geissler                         {
876cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
877cb92c03bSAndrew Geissler                                                       "Severity");
878cb92c03bSAndrew Geissler                         }
879cb92c03bSAndrew Geissler                     }
880cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Message")
881cb92c03bSAndrew Geissler                     {
882cb92c03bSAndrew Geissler                         message = std::get_if<std::string>(&propertyMap.second);
883cb92c03bSAndrew Geissler                         if (message == nullptr)
884cb92c03bSAndrew Geissler                         {
885cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
886cb92c03bSAndrew Geissler                                                       "Message");
887cb92c03bSAndrew Geissler                         }
888cb92c03bSAndrew Geissler                     }
889cb92c03bSAndrew Geissler                 }
890cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue = {
891cb92c03bSAndrew Geissler                     {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
892cb92c03bSAndrew Geissler                     {"@odata.context", "/redfish/v1/"
893cb92c03bSAndrew Geissler                                        "$metadata#LogEntry.LogEntry"},
894cb92c03bSAndrew Geissler                     {"@odata.id",
895cb92c03bSAndrew Geissler                      "/redfish/v1/Systems/system/LogServices/EventLog/"
896cb92c03bSAndrew Geissler                      "Entries/" +
897cb92c03bSAndrew Geissler                          std::to_string(*id)},
898cb92c03bSAndrew Geissler                     {"Name", "System DBus Event Log Entry"},
899cb92c03bSAndrew Geissler                     {"Id", std::to_string(*id)},
900cb92c03bSAndrew Geissler                     {"Message", *message},
901cb92c03bSAndrew Geissler                     {"EntryType", "Event"},
902cb92c03bSAndrew Geissler                     {"Severity", translateSeverityDbusToRedfish(*severity)},
903cb92c03bSAndrew Geissler                     {"Created", crow::utility::getDateTime(timestamp)}};
904cb92c03bSAndrew Geissler             },
905cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging",
906cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging/entry/" + entryID,
907cb92c03bSAndrew Geissler             "org.freedesktop.DBus.Properties", "GetAll",
908cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging.Entry");
909cb92c03bSAndrew Geissler #endif
910c4bf6374SJason M. Bills     }
911c4bf6374SJason M. Bills };
912c4bf6374SJason M. Bills 
913c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
914c4bf6374SJason M. Bills {
915c4bf6374SJason M. Bills   public:
916c4bf6374SJason M. Bills     template <typename CrowApp>
917c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
9184ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
9191da66f75SEd Tanous     {
9201da66f75SEd Tanous         entityPrivileges = {
921e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
922e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
923e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
924e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
925e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
926e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9271da66f75SEd Tanous     }
9281da66f75SEd Tanous 
9291da66f75SEd Tanous   private:
9301da66f75SEd Tanous     /**
9311da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9321da66f75SEd Tanous      */
9331da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9341da66f75SEd Tanous                const std::vector<std::string> &params) override
9351da66f75SEd Tanous     {
936e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9371da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
9381da66f75SEd Tanous         // it has a duplicate entry for members
939e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
9401da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
941e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
942c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
943e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
944e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
945e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
946e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
9471da66f75SEd Tanous             "Collection of LogServices for this Manager";
948c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
949c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
950c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
951c4bf6374SJason M. Bills         logServiceArray.push_back(
952cb92c03bSAndrew Geissler             {{ "@odata.id",
953cb92c03bSAndrew Geissler                "/redfish/v1/Managers/bmc/LogServices/Journal" }});
954c4bf6374SJason M. Bills #endif
955e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
956c4bf6374SJason M. Bills             logServiceArray.size();
9571da66f75SEd Tanous     }
9581da66f75SEd Tanous };
9591da66f75SEd Tanous 
960c4bf6374SJason M. Bills class BMCJournalLogService : public Node
9611da66f75SEd Tanous {
9621da66f75SEd Tanous   public:
9631da66f75SEd Tanous     template <typename CrowApp>
964c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
965c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
966e1f26343SJason M. Bills     {
967e1f26343SJason M. Bills         entityPrivileges = {
968e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
969e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
970e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
971e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
972e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
973e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
974e1f26343SJason M. Bills     }
975e1f26343SJason M. Bills 
976e1f26343SJason M. Bills   private:
977e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
978e1f26343SJason M. Bills                const std::vector<std::string> &params) override
979e1f26343SJason M. Bills     {
980e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
981e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
982e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
9830f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
9840f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
985e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
986e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
987c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
988c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
989c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
990e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
991cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
992cd50aa42SJason M. Bills             {"@odata.id",
993cd50aa42SJason M. Bills              "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}};
994e1f26343SJason M. Bills     }
995e1f26343SJason M. Bills };
996e1f26343SJason M. Bills 
997c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
998e1f26343SJason M. Bills                                       sd_journal *journal,
999c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
1000e1f26343SJason M. Bills {
1001e1f26343SJason M. Bills     // Get the Log Entry contents
1002e1f26343SJason M. Bills     int ret = 0;
1003e1f26343SJason M. Bills 
100439e77504SEd Tanous     std::string_view msg;
100516428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
1006e1f26343SJason M. Bills     if (ret < 0)
1007e1f26343SJason M. Bills     {
1008e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1009e1f26343SJason M. Bills         return 1;
1010e1f26343SJason M. Bills     }
1011e1f26343SJason M. Bills 
1012e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
1013e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
101416428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
1015e1f26343SJason M. Bills     if (ret < 0)
1016e1f26343SJason M. Bills     {
1017e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
1018e1f26343SJason M. Bills         return 1;
1019e1f26343SJason M. Bills     }
1020e1f26343SJason M. Bills 
1021e1f26343SJason M. Bills     // Get the Created time from the timestamp
102216428a1aSJason M. Bills     std::string entryTimeStr;
102316428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
1024e1f26343SJason M. Bills     {
102516428a1aSJason M. Bills         return 1;
1026e1f26343SJason M. Bills     }
1027e1f26343SJason M. Bills 
1028e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
1029c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
1030cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1031e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1032c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1033c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
1034e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
1035c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
103616428a1aSJason M. Bills         {"Message", msg},
1037e1f26343SJason M. Bills         {"EntryType", "Oem"},
1038e1f26343SJason M. Bills         {"Severity",
1039e1f26343SJason M. Bills          severity <= 2 ? "Critical"
1040e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
1041e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
1042e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
1043e1f26343SJason M. Bills     return 0;
1044e1f26343SJason M. Bills }
1045e1f26343SJason M. Bills 
1046c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
1047e1f26343SJason M. Bills {
1048e1f26343SJason M. Bills   public:
1049e1f26343SJason M. Bills     template <typename CrowApp>
1050c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
1051c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
1052e1f26343SJason M. Bills     {
1053e1f26343SJason M. Bills         entityPrivileges = {
1054e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1055e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1056e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1057e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1058e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1059e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1060e1f26343SJason M. Bills     }
1061e1f26343SJason M. Bills 
1062e1f26343SJason M. Bills   private:
1063e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1064e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1065e1f26343SJason M. Bills     {
1066e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1067193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
1068193ad2faSJason M. Bills         long skip = 0;
1069193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
107016428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
1071193ad2faSJason M. Bills         {
1072193ad2faSJason M. Bills             return;
1073193ad2faSJason M. Bills         }
107416428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
1075193ad2faSJason M. Bills         {
1076193ad2faSJason M. Bills             return;
1077193ad2faSJason M. Bills         }
1078e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
1079e1f26343SJason M. Bills         // it has a duplicate entry for members
1080e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1081e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
10820f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
10830f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1084e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1085c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1086e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1087c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1088e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1089e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
1090e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
10910f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
10920f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
1093e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1094e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
1095e1f26343SJason M. Bills 
1096e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
1097e1f26343SJason M. Bills         // for each entry
1098e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1099e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1100e1f26343SJason M. Bills         if (ret < 0)
1101e1f26343SJason M. Bills         {
1102e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1103f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1104e1f26343SJason M. Bills             return;
1105e1f26343SJason M. Bills         }
1106e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1107e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1108e1f26343SJason M. Bills         journalTmp = nullptr;
1109b01bf299SEd Tanous         uint64_t entryCount = 0;
1110e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
1111e1f26343SJason M. Bills         {
1112193ad2faSJason M. Bills             entryCount++;
1113193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
1114193ad2faSJason M. Bills             // start) and top (number of entries to display)
1115193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
1116193ad2faSJason M. Bills             {
1117193ad2faSJason M. Bills                 continue;
1118193ad2faSJason M. Bills             }
1119193ad2faSJason M. Bills 
112016428a1aSJason M. Bills             std::string idStr;
112116428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
1122e1f26343SJason M. Bills             {
1123e1f26343SJason M. Bills                 continue;
1124e1f26343SJason M. Bills             }
1125e1f26343SJason M. Bills 
1126e1f26343SJason M. Bills             logEntryArray.push_back({});
1127c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1128c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1129c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
1130e1f26343SJason M. Bills             {
1131f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
1132e1f26343SJason M. Bills                 return;
1133e1f26343SJason M. Bills             }
1134e1f26343SJason M. Bills         }
1135193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1136193ad2faSJason M. Bills         if (skip + top < entryCount)
1137193ad2faSJason M. Bills         {
1138193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
1139c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
1140193ad2faSJason M. Bills                 std::to_string(skip + top);
1141193ad2faSJason M. Bills         }
1142e1f26343SJason M. Bills     }
1143e1f26343SJason M. Bills };
1144e1f26343SJason M. Bills 
1145c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
1146e1f26343SJason M. Bills {
1147e1f26343SJason M. Bills   public:
1148c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
1149c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
1150e1f26343SJason M. Bills              std::string())
1151e1f26343SJason M. Bills     {
1152e1f26343SJason M. Bills         entityPrivileges = {
1153e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1154e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1155e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1156e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1157e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1158e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1159e1f26343SJason M. Bills     }
1160e1f26343SJason M. Bills 
1161e1f26343SJason M. Bills   private:
1162e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1163e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1164e1f26343SJason M. Bills     {
1165e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1166e1f26343SJason M. Bills         if (params.size() != 1)
1167e1f26343SJason M. Bills         {
1168f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1169e1f26343SJason M. Bills             return;
1170e1f26343SJason M. Bills         }
117116428a1aSJason M. Bills         const std::string &entryID = params[0];
1172e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
1173e1f26343SJason M. Bills         uint64_t ts = 0;
1174e1f26343SJason M. Bills         uint16_t index = 0;
117516428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
1176e1f26343SJason M. Bills         {
117716428a1aSJason M. Bills             return;
1178e1f26343SJason M. Bills         }
1179e1f26343SJason M. Bills 
1180e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1181e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1182e1f26343SJason M. Bills         if (ret < 0)
1183e1f26343SJason M. Bills         {
1184e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1185f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1186e1f26343SJason M. Bills             return;
1187e1f26343SJason M. Bills         }
1188e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1189e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1190e1f26343SJason M. Bills         journalTmp = nullptr;
1191e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
1192e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1193e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
1194e1f26343SJason M. Bills         {
1195e1f26343SJason M. Bills             sd_journal_next(journal.get());
1196e1f26343SJason M. Bills         }
1197c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
1198c4bf6374SJason M. Bills         std::string idStr;
1199c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
1200c4bf6374SJason M. Bills         {
1201c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
1202c4bf6374SJason M. Bills             return;
1203c4bf6374SJason M. Bills         }
1204c4bf6374SJason M. Bills 
1205c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1206e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
1207e1f26343SJason M. Bills         {
1208f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1209e1f26343SJason M. Bills             return;
1210e1f26343SJason M. Bills         }
1211e1f26343SJason M. Bills     }
1212e1f26343SJason M. Bills };
1213e1f26343SJason M. Bills 
1214424c4176SJason M. Bills class CrashdumpService : public Node
1215e1f26343SJason M. Bills {
1216e1f26343SJason M. Bills   public:
1217e1f26343SJason M. Bills     template <typename CrowApp>
1218424c4176SJason M. Bills     CrashdumpService(CrowApp &app) :
1219424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
12201da66f75SEd Tanous     {
12211da66f75SEd Tanous         entityPrivileges = {
1222e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1223e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1224e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1225e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1226e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1227e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
12281da66f75SEd Tanous     }
12291da66f75SEd Tanous 
12301da66f75SEd Tanous   private:
12311da66f75SEd Tanous     /**
12321da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
12331da66f75SEd Tanous      */
12341da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
12351da66f75SEd Tanous                const std::vector<std::string> &params) override
12361da66f75SEd Tanous     {
1237e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
12381da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
12390f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
1240424c4176SJason M. Bills             "/redfish/v1/Systems/system/LogServices/Crashdump";
1241e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1242e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
1243e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1244c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1245424c4176SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1246424c4176SJason M. Bills         asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1247424c4176SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Crashdump";
1248e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1249e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
1250cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1251cd50aa42SJason M. Bills             {"@odata.id",
1252424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
1253e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
12541da66f75SEd Tanous             {"Oem",
1255424c4176SJason M. Bills              {{"#Crashdump.OnDemand",
1256424c4176SJason M. Bills                {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1257424c4176SJason M. Bills                            "Actions/Oem/Crashdump.OnDemand"}}}}}};
12581da66f75SEd Tanous 
12591da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
1260e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
1261424c4176SJason M. Bills             {"#Crashdump.SendRawPeci",
1262cb92c03bSAndrew Geissler              { { "target",
1263424c4176SJason M. Bills                  "/redfish/v1/Systems/system/LogServices/Crashdump/"
1264424c4176SJason M. Bills                  "Actions/Oem/Crashdump.SendRawPeci" } }});
12651da66f75SEd Tanous #endif
12661da66f75SEd Tanous     }
12671da66f75SEd Tanous };
12681da66f75SEd Tanous 
1269424c4176SJason M. Bills class CrashdumpEntryCollection : public Node
12701da66f75SEd Tanous {
12711da66f75SEd Tanous   public:
12721da66f75SEd Tanous     template <typename CrowApp>
1273424c4176SJason M. Bills     CrashdumpEntryCollection(CrowApp &app) :
1274424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
12751da66f75SEd Tanous     {
12761da66f75SEd Tanous         entityPrivileges = {
1277e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1278e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1279e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1280e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1281e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1282e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
12831da66f75SEd Tanous     }
12841da66f75SEd Tanous 
12851da66f75SEd Tanous   private:
12861da66f75SEd Tanous     /**
12871da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
12881da66f75SEd Tanous      */
12891da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
12901da66f75SEd Tanous                const std::vector<std::string> &params) override
12911da66f75SEd Tanous     {
1292e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
12931da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
12941da66f75SEd Tanous         // it has a duplicate entry for members
1295e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
1296e1f26343SJason M. Bills                                          const boost::system::error_code ec,
12971da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
12981da66f75SEd Tanous             if (ec)
12991da66f75SEd Tanous             {
13001da66f75SEd Tanous                 if (ec.value() !=
13011da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
13021da66f75SEd Tanous                 {
13031da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
13041da66f75SEd Tanous                                      << ec.message();
1305f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
13061da66f75SEd Tanous                     return;
13071da66f75SEd Tanous                 }
13081da66f75SEd Tanous             }
1309e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
13101da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
13110f74e643SEd Tanous             asyncResp->res.jsonValue["@odata.id"] =
1312424c4176SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
1313e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1314d53dd41fSJason M. Bills                 "/redfish/v1/"
1315d53dd41fSJason M. Bills                 "$metadata#LogEntryCollection.LogEntryCollection";
1316424c4176SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
1317e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1318424c4176SJason M. Bills                 "Collection of Crashdump Entries";
1319e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1320e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
13211da66f75SEd Tanous             for (const std::string &objpath : resp)
13221da66f75SEd Tanous             {
132348e4639eSJason M. Bills                 // Don't list the on-demand log
1324424c4176SJason M. Bills                 if (objpath.compare(CrashdumpOnDemandPath) == 0)
13251da66f75SEd Tanous                 {
13261da66f75SEd Tanous                     continue;
13271da66f75SEd Tanous                 }
13284ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
13294ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
13301da66f75SEd Tanous                 {
1331e1f26343SJason M. Bills                     logEntryArray.push_back(
1332d53dd41fSJason M. Bills                         {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1333424c4176SJason M. Bills                                        "Crashdump/Entries/" +
13344ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
13351da66f75SEd Tanous                 }
13361da66f75SEd Tanous             }
1337e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1338e1f26343SJason M. Bills                 logEntryArray.size();
13391da66f75SEd Tanous         };
13401da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
13411da66f75SEd Tanous             std::move(getLogEntriesCallback),
13421da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
13431da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
13441da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
1345424c4176SJason M. Bills             std::array<const char *, 1>{CrashdumpInterface});
13461da66f75SEd Tanous     }
13471da66f75SEd Tanous };
13481da66f75SEd Tanous 
1349424c4176SJason M. Bills std::string getLogCreatedTime(const nlohmann::json &Crashdump)
13501da66f75SEd Tanous {
1351424c4176SJason M. Bills     nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1352424c4176SJason M. Bills     if (cdIt != Crashdump.end())
13531da66f75SEd Tanous     {
1354c4d00437SJason M. Bills         nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1355c4d00437SJason M. Bills         if (siIt != cdIt->end())
13561da66f75SEd Tanous         {
1357c4d00437SJason M. Bills             nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1358c4d00437SJason M. Bills             if (tsIt != siIt->end())
1359c4d00437SJason M. Bills             {
1360c4d00437SJason M. Bills                 const std::string *logTime =
1361c4d00437SJason M. Bills                     tsIt->get_ptr<const std::string *>();
13621da66f75SEd Tanous                 if (logTime != nullptr)
13631da66f75SEd Tanous                 {
13641da66f75SEd Tanous                     return *logTime;
13651da66f75SEd Tanous                 }
13661da66f75SEd Tanous             }
13671da66f75SEd Tanous         }
1368c4d00437SJason M. Bills     }
13691da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
13701da66f75SEd Tanous 
13711da66f75SEd Tanous     return std::string();
13721da66f75SEd Tanous }
13731da66f75SEd Tanous 
1374424c4176SJason M. Bills class CrashdumpEntry : public Node
13751da66f75SEd Tanous {
13761da66f75SEd Tanous   public:
1377424c4176SJason M. Bills     CrashdumpEntry(CrowApp &app) :
1378d53dd41fSJason M. Bills         Node(app,
1379424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
13801da66f75SEd Tanous              std::string())
13811da66f75SEd Tanous     {
13821da66f75SEd Tanous         entityPrivileges = {
1383e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1384e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1385e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1386e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1387e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1388e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
13891da66f75SEd Tanous     }
13901da66f75SEd Tanous 
13911da66f75SEd Tanous   private:
13921da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
13931da66f75SEd Tanous                const std::vector<std::string> &params) override
13941da66f75SEd Tanous     {
1395e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
13961da66f75SEd Tanous         if (params.size() != 1)
13971da66f75SEd Tanous         {
1398f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
13991da66f75SEd Tanous             return;
14001da66f75SEd Tanous         }
1401b01bf299SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1402abf2add6SEd Tanous         auto getStoredLogCallback = [asyncResp, logId](
1403abf2add6SEd Tanous                                         const boost::system::error_code ec,
1404abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
14051da66f75SEd Tanous             if (ec)
14061da66f75SEd Tanous             {
1407abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1408f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14091da66f75SEd Tanous                 return;
14101da66f75SEd Tanous             }
1411abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
14121da66f75SEd Tanous             if (log == nullptr)
14131da66f75SEd Tanous             {
1414f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14151da66f75SEd Tanous                 return;
14161da66f75SEd Tanous             }
14171da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
14181da66f75SEd Tanous             if (j.is_discarded())
14191da66f75SEd Tanous             {
1420f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14211da66f75SEd Tanous                 return;
14221da66f75SEd Tanous             }
14231da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1424e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1425cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1426abf2add6SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
14271da66f75SEd Tanous                 {"@odata.id",
1428424c4176SJason M. Bills                  "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
14294ed77cd5SEd Tanous                      std::to_string(logId)},
1430424c4176SJason M. Bills                 {"Name", "CPU Crashdump"},
14314ed77cd5SEd Tanous                 {"Id", logId},
14321da66f75SEd Tanous                 {"EntryType", "Oem"},
1433424c4176SJason M. Bills                 {"OemRecordFormat", "Intel Crashdump"},
14341da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
14351da66f75SEd Tanous                 {"Created", std::move(t)}};
14361da66f75SEd Tanous         };
14371da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1438424c4176SJason M. Bills             std::move(getStoredLogCallback), CrashdumpObject,
1439424c4176SJason M. Bills             CrashdumpPath + std::string("/") + std::to_string(logId),
1440424c4176SJason M. Bills             "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1441424c4176SJason M. Bills             "Log");
14421da66f75SEd Tanous     }
14431da66f75SEd Tanous };
14441da66f75SEd Tanous 
1445424c4176SJason M. Bills class OnDemandCrashdump : public Node
14461da66f75SEd Tanous {
14471da66f75SEd Tanous   public:
1448424c4176SJason M. Bills     OnDemandCrashdump(CrowApp &app) :
1449424c4176SJason M. Bills         Node(app,
1450424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1451424c4176SJason M. Bills              "Crashdump.OnDemand/")
14521da66f75SEd Tanous     {
14531da66f75SEd Tanous         entityPrivileges = {
1454e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1455e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1456e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1457e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1458e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1459e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
14601da66f75SEd Tanous     }
14611da66f75SEd Tanous 
14621da66f75SEd Tanous   private:
14631da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
14641da66f75SEd Tanous                 const std::vector<std::string> &params) override
14651da66f75SEd Tanous     {
1466e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
146748e4639eSJason M. Bills         static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
14681da66f75SEd Tanous 
146948e4639eSJason M. Bills         // Only allow one OnDemand Log request at a time
147048e4639eSJason M. Bills         if (onDemandLogMatcher != nullptr)
14711da66f75SEd Tanous         {
1472e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1473f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
14741da66f75SEd Tanous             return;
14751da66f75SEd Tanous         }
14761da66f75SEd Tanous         // Make this static so it survives outside this method
14771da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
14781da66f75SEd Tanous 
14791da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1480e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
148148e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
14821da66f75SEd Tanous             if (ec)
14831da66f75SEd Tanous             {
14841da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
14851da66f75SEd Tanous                 // completion.
14861da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
14871da66f75SEd Tanous                 {
14881da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
14891da66f75SEd Tanous                 }
14901da66f75SEd Tanous                 return;
14911da66f75SEd Tanous             }
149248e4639eSJason M. Bills             BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
14931da66f75SEd Tanous 
1494f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
14951da66f75SEd Tanous         });
14961da66f75SEd Tanous 
149748e4639eSJason M. Bills         auto onDemandLogMatcherCallback = [asyncResp](
14981da66f75SEd Tanous                                               sdbusplus::message::message &m) {
149948e4639eSJason M. Bills             BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
15001da66f75SEd Tanous             boost::system::error_code ec;
15011da66f75SEd Tanous             timeout.cancel(ec);
15021da66f75SEd Tanous             if (ec)
15031da66f75SEd Tanous             {
15041da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
15051da66f75SEd Tanous             }
15064ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
15071da66f75SEd Tanous             boost::container::flat_map<
1508abf2add6SEd Tanous                 std::string, boost::container::flat_map<
1509abf2add6SEd Tanous                                  std::string, std::variant<std::string>>>
15104ed77cd5SEd Tanous                 interfacesAdded;
15114ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1512abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(
1513424c4176SJason M. Bills                 &interfacesAdded[CrashdumpInterface]["Log"]);
15141da66f75SEd Tanous             if (log == nullptr)
15151da66f75SEd Tanous             {
1516f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
151748e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
15181da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
15191da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
15201da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
15211da66f75SEd Tanous                 // be the last thing done.
152248e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
15231da66f75SEd Tanous                 return;
15241da66f75SEd Tanous             }
15251da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
15261da66f75SEd Tanous             if (j.is_discarded())
15271da66f75SEd Tanous             {
1528f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
152948e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
15301da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
15311da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
15321da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
15331da66f75SEd Tanous                 // be the last thing done.
153448e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
15351da66f75SEd Tanous                 return;
15361da66f75SEd Tanous             }
15371da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1538e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1539cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
15401da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1541424c4176SJason M. Bills                 {"Name", "CPU Crashdump"},
15421da66f75SEd Tanous                 {"EntryType", "Oem"},
1543424c4176SJason M. Bills                 {"OemRecordFormat", "Intel Crashdump"},
15441da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
15451da66f75SEd Tanous                 {"Created", std::move(t)}};
154648e4639eSJason M. Bills             // Careful with onDemandLogMatcher.  It is a unique_ptr to the
15471da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
15481da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
15491da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
15501da66f75SEd Tanous             // thing done.
155148e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
15521da66f75SEd Tanous         };
155348e4639eSJason M. Bills         onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
15541da66f75SEd Tanous             *crow::connections::systemBus,
15551da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
1556424c4176SJason M. Bills                 sdbusplus::bus::match::rules::argNpath(0,
1557424c4176SJason M. Bills                                                        CrashdumpOnDemandPath),
155848e4639eSJason M. Bills             std::move(onDemandLogMatcherCallback));
15591da66f75SEd Tanous 
156048e4639eSJason M. Bills         auto generateonDemandLogCallback =
1561e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
15621da66f75SEd Tanous                         const std::string &resp) {
15631da66f75SEd Tanous                 if (ec)
15641da66f75SEd Tanous                 {
15651da66f75SEd Tanous                     if (ec.value() ==
15661da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
15671da66f75SEd Tanous                     {
1568f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
15691da66f75SEd Tanous                     }
15701da66f75SEd Tanous                     else
15711da66f75SEd Tanous                     {
1572f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
15731da66f75SEd Tanous                     }
15741da66f75SEd Tanous                     boost::system::error_code timeoutec;
15751da66f75SEd Tanous                     timeout.cancel(timeoutec);
15761da66f75SEd Tanous                     if (timeoutec)
15771da66f75SEd Tanous                     {
15781da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
15791da66f75SEd Tanous                                          << timeoutec;
15801da66f75SEd Tanous                     }
158148e4639eSJason M. Bills                     onDemandLogMatcher = nullptr;
15821da66f75SEd Tanous                     return;
15831da66f75SEd Tanous                 }
15841da66f75SEd Tanous             };
15851da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1586424c4176SJason M. Bills             std::move(generateonDemandLogCallback), CrashdumpObject,
1587424c4176SJason M. Bills             CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
15881da66f75SEd Tanous     }
15891da66f75SEd Tanous };
15901da66f75SEd Tanous 
1591e1f26343SJason M. Bills class SendRawPECI : public Node
15921da66f75SEd Tanous {
15931da66f75SEd Tanous   public:
1594e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
1595424c4176SJason M. Bills         Node(app,
1596424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1597424c4176SJason M. Bills              "Crashdump.SendRawPeci/")
15981da66f75SEd Tanous     {
15991da66f75SEd Tanous         entityPrivileges = {
16001da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
16011da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
16021da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
16031da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
16041da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
16051da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
16061da66f75SEd Tanous     }
16071da66f75SEd Tanous 
16081da66f75SEd Tanous   private:
16091da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
16101da66f75SEd Tanous                 const std::vector<std::string> &params) override
16111da66f75SEd Tanous     {
1612e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1613b1556427SEd Tanous         uint8_t clientAddress = 0;
1614b1556427SEd Tanous         uint8_t readLength = 0;
16151da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
1616b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1617b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
1618b1556427SEd Tanous                                  peciCommand))
16191da66f75SEd Tanous         {
16201da66f75SEd Tanous             return;
16211da66f75SEd Tanous         }
1622b1556427SEd Tanous 
16231da66f75SEd Tanous         // Callback to return the Raw PECI response
1624e1f26343SJason M. Bills         auto sendRawPECICallback =
1625e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
16261da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
16271da66f75SEd Tanous                 if (ec)
16281da66f75SEd Tanous                 {
16291da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
16301da66f75SEd Tanous                                      << ec.message();
1631f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
16321da66f75SEd Tanous                     return;
16331da66f75SEd Tanous                 }
1634e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
16351da66f75SEd Tanous                                             {"PECIResponse", resp}};
16361da66f75SEd Tanous             };
16371da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
16381da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1639424c4176SJason M. Bills             std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1640424c4176SJason M. Bills             CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
16414ed77cd5SEd Tanous             peciCommand);
16421da66f75SEd Tanous     }
16431da66f75SEd Tanous };
16441da66f75SEd Tanous 
1645cb92c03bSAndrew Geissler /**
1646cb92c03bSAndrew Geissler  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1647cb92c03bSAndrew Geissler  */
1648cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node
1649cb92c03bSAndrew Geissler {
1650cb92c03bSAndrew Geissler   public:
1651cb92c03bSAndrew Geissler     DBusLogServiceActionsClear(CrowApp &app) :
1652cb92c03bSAndrew Geissler         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1653cb92c03bSAndrew Geissler                   "LogService.Reset")
1654cb92c03bSAndrew Geissler     {
1655cb92c03bSAndrew Geissler         entityPrivileges = {
1656cb92c03bSAndrew Geissler             {boost::beast::http::verb::get, {{"Login"}}},
1657cb92c03bSAndrew Geissler             {boost::beast::http::verb::head, {{"Login"}}},
1658cb92c03bSAndrew Geissler             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1659cb92c03bSAndrew Geissler             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1660cb92c03bSAndrew Geissler             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1661cb92c03bSAndrew Geissler             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1662cb92c03bSAndrew Geissler     }
1663cb92c03bSAndrew Geissler 
1664cb92c03bSAndrew Geissler   private:
1665cb92c03bSAndrew Geissler     /**
1666cb92c03bSAndrew Geissler      * Function handles POST method request.
1667cb92c03bSAndrew Geissler      * The Clear Log actions does not require any parameter.The action deletes
1668cb92c03bSAndrew Geissler      * all entries found in the Entries collection for this Log Service.
1669cb92c03bSAndrew Geissler      */
1670cb92c03bSAndrew Geissler     void doPost(crow::Response &res, const crow::Request &req,
1671cb92c03bSAndrew Geissler                 const std::vector<std::string> &params) override
1672cb92c03bSAndrew Geissler     {
1673cb92c03bSAndrew Geissler         BMCWEB_LOG_DEBUG << "Do delete all entries.";
1674cb92c03bSAndrew Geissler 
1675cb92c03bSAndrew Geissler         auto asyncResp = std::make_shared<AsyncResp>(res);
1676cb92c03bSAndrew Geissler         // Process response from Logging service.
1677cb92c03bSAndrew Geissler         auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1678cb92c03bSAndrew Geissler             BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1679cb92c03bSAndrew Geissler             if (ec)
1680cb92c03bSAndrew Geissler             {
1681cb92c03bSAndrew Geissler                 // TODO Handle for specific error code
1682cb92c03bSAndrew Geissler                 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1683cb92c03bSAndrew Geissler                 asyncResp->res.result(
1684cb92c03bSAndrew Geissler                     boost::beast::http::status::internal_server_error);
1685cb92c03bSAndrew Geissler                 return;
1686cb92c03bSAndrew Geissler             }
1687cb92c03bSAndrew Geissler 
1688cb92c03bSAndrew Geissler             asyncResp->res.result(boost::beast::http::status::no_content);
1689cb92c03bSAndrew Geissler         };
1690cb92c03bSAndrew Geissler 
1691cb92c03bSAndrew Geissler         // Make call to Logging service to request Clear Log
1692cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
1693cb92c03bSAndrew Geissler             resp_handler, "xyz.openbmc_project.Logging",
1694cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging",
1695cb92c03bSAndrew Geissler             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1696cb92c03bSAndrew Geissler     }
1697cb92c03bSAndrew Geissler };
16981da66f75SEd Tanous } // namespace redfish
1699