xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 4418c7f0ddcc5d3518831729a380a0b67e5d4093)
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"
191da66f75SEd Tanous 
20e1f26343SJason M. Bills #include <systemd/sd-journal.h>
21e1f26343SJason M. Bills 
221da66f75SEd Tanous #include <boost/container/flat_map.hpp>
23cb92c03bSAndrew Geissler #include <error_messages.hpp>
24*4418c7f0SJames Feist #include <filesystem>
25abf2add6SEd Tanous #include <variant>
261da66f75SEd Tanous 
271da66f75SEd Tanous namespace redfish
281da66f75SEd Tanous {
291da66f75SEd Tanous 
304ed77cd5SEd Tanous constexpr char const *cpuLogObject = "com.intel.CpuDebugLog";
314ed77cd5SEd Tanous constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog";
3248e4639eSJason M. Bills constexpr char const *cpuLogOnDemandPath = "/com/intel/CpuDebugLog/OnDemand";
334ed77cd5SEd Tanous constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
3448e4639eSJason M. Bills constexpr char const *cpuLogOnDemandInterface =
3548e4639eSJason M. Bills     "com.intel.CpuDebugLog.OnDemand";
36e1f26343SJason M. Bills constexpr char const *cpuLogRawPECIInterface =
371da66f75SEd Tanous     "com.intel.CpuDebugLog.SendRawPeci";
381da66f75SEd Tanous 
39f6150403SJames Feist namespace fs = std::filesystem;
401da66f75SEd Tanous 
41cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map<
42cb92c03bSAndrew Geissler     std::string,
43cb92c03bSAndrew Geissler     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
44cb92c03bSAndrew Geissler                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
45cb92c03bSAndrew Geissler 
46cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map<
47cb92c03bSAndrew Geissler     sdbusplus::message::object_path,
48cb92c03bSAndrew Geissler     boost::container::flat_map<std::string, GetManagedPropertyType>>;
49cb92c03bSAndrew Geissler 
50cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s)
51cb92c03bSAndrew Geissler {
52cb92c03bSAndrew Geissler     if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
53cb92c03bSAndrew Geissler     {
54cb92c03bSAndrew Geissler         return "Critical";
55cb92c03bSAndrew Geissler     }
56cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
57cb92c03bSAndrew Geissler     {
58cb92c03bSAndrew Geissler         return "Critical";
59cb92c03bSAndrew Geissler     }
60cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
61cb92c03bSAndrew Geissler     {
62cb92c03bSAndrew Geissler         return "OK";
63cb92c03bSAndrew Geissler     }
64cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
65cb92c03bSAndrew Geissler     {
66cb92c03bSAndrew Geissler         return "Critical";
67cb92c03bSAndrew Geissler     }
68cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
69cb92c03bSAndrew Geissler     {
70cb92c03bSAndrew Geissler         return "Critical";
71cb92c03bSAndrew Geissler     }
72cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
73cb92c03bSAndrew Geissler     {
74cb92c03bSAndrew Geissler         return "OK";
75cb92c03bSAndrew Geissler     }
76cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
77cb92c03bSAndrew Geissler     {
78cb92c03bSAndrew Geissler         return "OK";
79cb92c03bSAndrew Geissler     }
80cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
81cb92c03bSAndrew Geissler     {
82cb92c03bSAndrew Geissler         return "Warning";
83cb92c03bSAndrew Geissler     }
84cb92c03bSAndrew Geissler     return "";
85cb92c03bSAndrew Geissler }
86cb92c03bSAndrew Geissler 
8716428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
8839e77504SEd Tanous                               const std::string_view &field,
8939e77504SEd Tanous                               std::string_view &contents)
9016428a1aSJason M. Bills {
9116428a1aSJason M. Bills     const char *data = nullptr;
9216428a1aSJason M. Bills     size_t length = 0;
9316428a1aSJason M. Bills     int ret = 0;
9416428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
95b01bf299SEd Tanous     ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
96b01bf299SEd Tanous                               &length);
9716428a1aSJason M. Bills     if (ret < 0)
9816428a1aSJason M. Bills     {
9916428a1aSJason M. Bills         return ret;
10016428a1aSJason M. Bills     }
10139e77504SEd Tanous     contents = std::string_view(data, length);
10216428a1aSJason M. Bills     // Only use the content after the "=" character.
10316428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
10416428a1aSJason M. Bills     return ret;
10516428a1aSJason M. Bills }
10616428a1aSJason M. Bills 
10716428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
10839e77504SEd Tanous                               const std::string_view &field, const int &base,
10916428a1aSJason M. Bills                               int &contents)
11016428a1aSJason M. Bills {
11116428a1aSJason M. Bills     int ret = 0;
11239e77504SEd Tanous     std::string_view metadata;
11316428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
11416428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
11516428a1aSJason M. Bills     if (ret < 0)
11616428a1aSJason M. Bills     {
11716428a1aSJason M. Bills         return ret;
11816428a1aSJason M. Bills     }
119b01bf299SEd Tanous     contents = strtol(metadata.data(), nullptr, base);
12016428a1aSJason M. Bills     return ret;
12116428a1aSJason M. Bills }
12216428a1aSJason M. Bills 
12316428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
12416428a1aSJason M. Bills {
12516428a1aSJason M. Bills     int ret = 0;
12616428a1aSJason M. Bills     uint64_t timestamp = 0;
12716428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
12816428a1aSJason M. Bills     if (ret < 0)
12916428a1aSJason M. Bills     {
13016428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
13116428a1aSJason M. Bills                          << strerror(-ret);
13216428a1aSJason M. Bills         return false;
13316428a1aSJason M. Bills     }
13416428a1aSJason M. Bills     time_t t =
13516428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
13616428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
13716428a1aSJason M. Bills     char entryTime[64] = {};
13816428a1aSJason M. Bills     if (NULL != loctime)
13916428a1aSJason M. Bills     {
14016428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
14116428a1aSJason M. Bills     }
14216428a1aSJason M. Bills     // Insert the ':' into the timezone
14339e77504SEd Tanous     std::string_view t1(entryTime);
14439e77504SEd Tanous     std::string_view t2(entryTime);
14516428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
14616428a1aSJason M. Bills     {
14716428a1aSJason M. Bills         t1.remove_suffix(2);
14816428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
14916428a1aSJason M. Bills     }
15039e77504SEd Tanous     entryTimestamp = std::string(t1) + ":" + std::string(t2);
15116428a1aSJason M. Bills     return true;
15216428a1aSJason M. Bills }
15316428a1aSJason M. Bills 
15416428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
15516428a1aSJason M. Bills                          long &skip)
15616428a1aSJason M. Bills {
15716428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
15816428a1aSJason M. Bills     if (skipParam != nullptr)
15916428a1aSJason M. Bills     {
16016428a1aSJason M. Bills         char *ptr = nullptr;
16116428a1aSJason M. Bills         skip = std::strtol(skipParam, &ptr, 10);
16216428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
16316428a1aSJason M. Bills         {
16416428a1aSJason M. Bills 
16516428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
16616428a1aSJason M. Bills                                                    "$skip");
16716428a1aSJason M. Bills             return false;
16816428a1aSJason M. Bills         }
16916428a1aSJason M. Bills         if (skip < 0)
17016428a1aSJason M. Bills         {
17116428a1aSJason M. Bills 
17216428a1aSJason M. Bills             messages::queryParameterOutOfRange(res, std::to_string(skip),
17316428a1aSJason M. Bills                                                "$skip", "greater than 0");
17416428a1aSJason M. Bills             return false;
17516428a1aSJason M. Bills         }
17616428a1aSJason M. Bills     }
17716428a1aSJason M. Bills     return true;
17816428a1aSJason M. Bills }
17916428a1aSJason M. Bills 
18016428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000;
18116428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
18216428a1aSJason M. Bills                         long &top)
18316428a1aSJason M. Bills {
18416428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
18516428a1aSJason M. Bills     if (topParam != nullptr)
18616428a1aSJason M. Bills     {
18716428a1aSJason M. Bills         char *ptr = nullptr;
18816428a1aSJason M. Bills         top = std::strtol(topParam, &ptr, 10);
18916428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
19016428a1aSJason M. Bills         {
19116428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
19216428a1aSJason M. Bills                                                    "$top");
19316428a1aSJason M. Bills             return false;
19416428a1aSJason M. Bills         }
19516428a1aSJason M. Bills         if (top < 1 || top > maxEntriesPerPage)
19616428a1aSJason M. Bills         {
19716428a1aSJason M. Bills 
19816428a1aSJason M. Bills             messages::queryParameterOutOfRange(
19916428a1aSJason M. Bills                 res, std::to_string(top), "$top",
20016428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
20116428a1aSJason M. Bills             return false;
20216428a1aSJason M. Bills         }
20316428a1aSJason M. Bills     }
20416428a1aSJason M. Bills     return true;
20516428a1aSJason M. Bills }
20616428a1aSJason M. Bills 
20716428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
20816428a1aSJason M. Bills {
20916428a1aSJason M. Bills     int ret = 0;
21016428a1aSJason M. Bills     static uint64_t prevTs = 0;
21116428a1aSJason M. Bills     static int index = 0;
21216428a1aSJason M. Bills     // Get the entry timestamp
21316428a1aSJason M. Bills     uint64_t curTs = 0;
21416428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &curTs);
21516428a1aSJason M. Bills     if (ret < 0)
21616428a1aSJason M. Bills     {
21716428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
21816428a1aSJason M. Bills                          << strerror(-ret);
21916428a1aSJason M. Bills         return false;
22016428a1aSJason M. Bills     }
22116428a1aSJason M. Bills     // If the timestamp isn't unique, increment the index
22216428a1aSJason M. Bills     if (curTs == prevTs)
22316428a1aSJason M. Bills     {
22416428a1aSJason M. Bills         index++;
22516428a1aSJason M. Bills     }
22616428a1aSJason M. Bills     else
22716428a1aSJason M. Bills     {
22816428a1aSJason M. Bills         // Otherwise, reset it
22916428a1aSJason M. Bills         index = 0;
23016428a1aSJason M. Bills     }
23116428a1aSJason M. Bills     // Save the timestamp
23216428a1aSJason M. Bills     prevTs = curTs;
23316428a1aSJason M. Bills 
23416428a1aSJason M. Bills     entryID = std::to_string(curTs);
23516428a1aSJason M. Bills     if (index > 0)
23616428a1aSJason M. Bills     {
23716428a1aSJason M. Bills         entryID += "_" + std::to_string(index);
23816428a1aSJason M. Bills     }
23916428a1aSJason M. Bills     return true;
24016428a1aSJason M. Bills }
24116428a1aSJason M. Bills 
24216428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
24316428a1aSJason M. Bills                                uint64_t &timestamp, uint16_t &index)
24416428a1aSJason M. Bills {
24516428a1aSJason M. Bills     if (entryID.empty())
24616428a1aSJason M. Bills     {
24716428a1aSJason M. Bills         return false;
24816428a1aSJason M. Bills     }
24916428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
25039e77504SEd Tanous     std::string_view tsStr(entryID);
25116428a1aSJason M. Bills 
25216428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
25316428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
25416428a1aSJason M. Bills     {
25516428a1aSJason M. Bills         // Timestamp has an index
25616428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
25739e77504SEd Tanous         std::string_view indexStr(entryID);
25816428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
25916428a1aSJason M. Bills         std::size_t pos;
26016428a1aSJason M. Bills         try
26116428a1aSJason M. Bills         {
26239e77504SEd Tanous             index = std::stoul(std::string(indexStr), &pos);
26316428a1aSJason M. Bills         }
264b01bf299SEd Tanous         catch (std::invalid_argument)
26516428a1aSJason M. Bills         {
26616428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
26716428a1aSJason M. Bills             return false;
26816428a1aSJason M. Bills         }
269b01bf299SEd Tanous         catch (std::out_of_range)
27016428a1aSJason M. Bills         {
27116428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
27216428a1aSJason M. Bills             return false;
27316428a1aSJason M. Bills         }
27416428a1aSJason M. Bills         if (pos != indexStr.size())
27516428a1aSJason M. Bills         {
27616428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
27716428a1aSJason M. Bills             return false;
27816428a1aSJason M. Bills         }
27916428a1aSJason M. Bills     }
28016428a1aSJason M. Bills     // Timestamp has no index
28116428a1aSJason M. Bills     std::size_t pos;
28216428a1aSJason M. Bills     try
28316428a1aSJason M. Bills     {
28439e77504SEd Tanous         timestamp = std::stoull(std::string(tsStr), &pos);
28516428a1aSJason M. Bills     }
286b01bf299SEd Tanous     catch (std::invalid_argument)
28716428a1aSJason M. Bills     {
28816428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
28916428a1aSJason M. Bills         return false;
29016428a1aSJason M. Bills     }
291b01bf299SEd Tanous     catch (std::out_of_range)
29216428a1aSJason M. Bills     {
29316428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
29416428a1aSJason M. Bills         return false;
29516428a1aSJason M. Bills     }
29616428a1aSJason M. Bills     if (pos != tsStr.size())
29716428a1aSJason M. Bills     {
29816428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
29916428a1aSJason M. Bills         return false;
30016428a1aSJason M. Bills     }
30116428a1aSJason M. Bills     return true;
30216428a1aSJason M. Bills }
30316428a1aSJason M. Bills 
304c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
3051da66f75SEd Tanous {
3061da66f75SEd Tanous   public:
3071da66f75SEd Tanous     template <typename CrowApp>
308c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
309029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/")
310c4bf6374SJason M. Bills     {
311c4bf6374SJason M. Bills         entityPrivileges = {
312c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
313c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
314c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
315c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
316c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
317c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
318c4bf6374SJason M. Bills     }
319c4bf6374SJason M. Bills 
320c4bf6374SJason M. Bills   private:
321c4bf6374SJason M. Bills     /**
322c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
323c4bf6374SJason M. Bills      */
324c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
325c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
326c4bf6374SJason M. Bills     {
327c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
328c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
329c4bf6374SJason M. Bills         // it has a duplicate entry for members
330c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
331c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
332c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
333c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
334c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
335029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices";
336c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
337c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
338c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
339c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
340c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
341029573d4SEd Tanous         logServiceArray.push_back(
342029573d4SEd Tanous             {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
343d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
344d53dd41fSJason M. Bills         logServiceArray.push_back(
345cb92c03bSAndrew Geissler             {{ "@odata.id",
346cb92c03bSAndrew Geissler                "/redfish/v1/Systems/system/LogServices/CpuLog" }});
347d53dd41fSJason M. Bills #endif
348c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
349c4bf6374SJason M. Bills             logServiceArray.size();
350c4bf6374SJason M. Bills     }
351c4bf6374SJason M. Bills };
352c4bf6374SJason M. Bills 
353c4bf6374SJason M. Bills class EventLogService : public Node
354c4bf6374SJason M. Bills {
355c4bf6374SJason M. Bills   public:
356c4bf6374SJason M. Bills     template <typename CrowApp>
357c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
358029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
359c4bf6374SJason M. Bills     {
360c4bf6374SJason M. Bills         entityPrivileges = {
361c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
362c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
363c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
364c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
365c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
366c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
367c4bf6374SJason M. Bills     }
368c4bf6374SJason M. Bills 
369c4bf6374SJason M. Bills   private:
370c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
371c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
372c4bf6374SJason M. Bills     {
373c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
374c4bf6374SJason M. Bills 
375c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
376029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog";
377c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
378c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
379c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
380c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
381c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
382c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
383c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
384c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
385c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
386c4bf6374SJason M. Bills             {"@odata.id",
387029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
388c4bf6374SJason M. Bills     }
389c4bf6374SJason M. Bills };
390c4bf6374SJason M. Bills 
391029573d4SEd Tanous static int fillEventLogEntryJson(const std::string &bmcLogEntryID,
39239e77504SEd Tanous                                  const std::string_view &messageID,
393c4bf6374SJason M. Bills                                  sd_journal *journal,
394c4bf6374SJason M. Bills                                  nlohmann::json &bmcLogEntryJson)
395c4bf6374SJason M. Bills {
396c4bf6374SJason M. Bills     // Get the Log Entry contents
397c4bf6374SJason M. Bills     int ret = 0;
398c4bf6374SJason M. Bills 
39939e77504SEd Tanous     std::string_view msg;
400c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
401c4bf6374SJason M. Bills     if (ret < 0)
402c4bf6374SJason M. Bills     {
403c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
404c4bf6374SJason M. Bills         return 1;
405c4bf6374SJason M. Bills     }
406c4bf6374SJason M. Bills 
407c4bf6374SJason M. Bills     // Get the severity from the PRIORITY field
408c4bf6374SJason M. Bills     int severity = 8; // Default to an invalid priority
409c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
410c4bf6374SJason M. Bills     if (ret < 0)
411c4bf6374SJason M. Bills     {
412c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
413c4bf6374SJason M. Bills         return 1;
414c4bf6374SJason M. Bills     }
415c4bf6374SJason M. Bills 
416c4bf6374SJason M. Bills     // Get the MessageArgs from the journal entry by finding all of the
417c4bf6374SJason M. Bills     // REDFISH_MESSAGE_ARG_x fields
418c4bf6374SJason M. Bills     const void *data;
419c4bf6374SJason M. Bills     size_t length;
420c4bf6374SJason M. Bills     std::vector<std::string> messageArgs;
421c4bf6374SJason M. Bills     SD_JOURNAL_FOREACH_DATA(journal, data, length)
422c4bf6374SJason M. Bills     {
42339e77504SEd Tanous         std::string_view field(static_cast<const char *>(data), length);
42439e77504SEd Tanous         if (boost::starts_with(field, "REDFISH_MESSAGE_ARG_"))
425c4bf6374SJason M. Bills         {
426c4bf6374SJason M. Bills             // Get the Arg number from the field name
427c4bf6374SJason M. Bills             field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
428c4bf6374SJason M. Bills             if (field.empty())
429c4bf6374SJason M. Bills             {
430c4bf6374SJason M. Bills                 continue;
431c4bf6374SJason M. Bills             }
432b01bf299SEd Tanous             int argNum = std::strtoul(field.data(), nullptr, 10);
433b01bf299SEd Tanous             if (argNum == 0)
434c4bf6374SJason M. Bills             {
435c4bf6374SJason M. Bills                 continue;
436c4bf6374SJason M. Bills             }
437c4bf6374SJason M. Bills             // Get the Arg value after the "=" character.
438c4bf6374SJason M. Bills             field.remove_prefix(std::min(field.find("=") + 1, field.size()));
439c4bf6374SJason M. Bills             // Make sure we have enough space in messageArgs
440c4bf6374SJason M. Bills             if (argNum > messageArgs.size())
441c4bf6374SJason M. Bills             {
442b01bf299SEd Tanous                 messageArgs.resize(argNum);
443c4bf6374SJason M. Bills             }
44439e77504SEd Tanous             messageArgs[argNum - 1] = std::string(field);
445c4bf6374SJason M. Bills         }
446c4bf6374SJason M. Bills     }
447c4bf6374SJason M. Bills 
448c4bf6374SJason M. Bills     // Get the Created time from the timestamp
449c4bf6374SJason M. Bills     std::string entryTimeStr;
450c4bf6374SJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
451c4bf6374SJason M. Bills     {
452c4bf6374SJason M. Bills         return 1;
453c4bf6374SJason M. Bills     }
454c4bf6374SJason M. Bills 
455c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
456c4bf6374SJason M. Bills     bmcLogEntryJson = {
457cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
458c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
459029573d4SEd Tanous         {"@odata.id",
460029573d4SEd Tanous          "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
461029573d4SEd Tanous              bmcLogEntryID},
462c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
463c4bf6374SJason M. Bills         {"Id", bmcLogEntryID},
464c4bf6374SJason M. Bills         {"Message", msg},
465c4bf6374SJason M. Bills         {"MessageId", messageID},
466c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
467c4bf6374SJason M. Bills         {"EntryType", "Event"},
468c4bf6374SJason M. Bills         {"Severity",
469c4bf6374SJason M. Bills          severity <= 2 ? "Critical"
470c4bf6374SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
471c4bf6374SJason M. Bills         {"Created", std::move(entryTimeStr)}};
472c4bf6374SJason M. Bills     return 0;
473c4bf6374SJason M. Bills }
474c4bf6374SJason M. Bills 
475c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
476c4bf6374SJason M. Bills {
477c4bf6374SJason M. Bills   public:
478c4bf6374SJason M. Bills     template <typename CrowApp>
479c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
480029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
481c4bf6374SJason M. Bills     {
482c4bf6374SJason M. Bills         entityPrivileges = {
483c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
484c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
485c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
486c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
487c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
488c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
489c4bf6374SJason M. Bills     }
490c4bf6374SJason M. Bills 
491c4bf6374SJason M. Bills   private:
492c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
493c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
494c4bf6374SJason M. Bills     {
495c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
496c4bf6374SJason M. Bills         long skip = 0;
497c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
498c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
499c4bf6374SJason M. Bills         {
500c4bf6374SJason M. Bills             return;
501c4bf6374SJason M. Bills         }
502c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
503c4bf6374SJason M. Bills         {
504c4bf6374SJason M. Bills             return;
505c4bf6374SJason M. Bills         }
506c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
507c4bf6374SJason M. Bills         // it has a duplicate entry for members
508c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
509c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
510c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
511c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
512c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
513029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
514c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
515c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
516c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
517cb92c03bSAndrew Geissler 
518cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
519c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
520c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
521c4bf6374SJason M. Bills         // Go through the journal and create a unique ID for each entry
522c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
523c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
524c4bf6374SJason M. Bills         if (ret < 0)
525c4bf6374SJason M. Bills         {
526c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
527c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
528c4bf6374SJason M. Bills             return;
529c4bf6374SJason M. Bills         }
530c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
531c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
532c4bf6374SJason M. Bills         journalTmp = nullptr;
533b01bf299SEd Tanous         uint64_t entryCount = 0;
534c4bf6374SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
535c4bf6374SJason M. Bills         {
536c4bf6374SJason M. Bills             // Look for only journal entries that contain a REDFISH_MESSAGE_ID
537c4bf6374SJason M. Bills             // field
53839e77504SEd Tanous             std::string_view messageID;
539c4bf6374SJason M. Bills             ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID",
540c4bf6374SJason M. Bills                                      messageID);
541c4bf6374SJason M. Bills             if (ret < 0)
542c4bf6374SJason M. Bills             {
543c4bf6374SJason M. Bills                 continue;
544c4bf6374SJason M. Bills             }
545c4bf6374SJason M. Bills 
546c4bf6374SJason M. Bills             entryCount++;
547c4bf6374SJason M. Bills             // Handle paging using skip (number of entries to skip from the
548c4bf6374SJason M. Bills             // start) and top (number of entries to display)
549c4bf6374SJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
550c4bf6374SJason M. Bills             {
551c4bf6374SJason M. Bills                 continue;
552c4bf6374SJason M. Bills             }
553c4bf6374SJason M. Bills 
554c4bf6374SJason M. Bills             std::string idStr;
555c4bf6374SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
556c4bf6374SJason M. Bills             {
557c4bf6374SJason M. Bills                 continue;
558c4bf6374SJason M. Bills             }
559c4bf6374SJason M. Bills 
560c4bf6374SJason M. Bills             logEntryArray.push_back({});
561c4bf6374SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
562029573d4SEd Tanous             if (fillEventLogEntryJson(idStr, messageID, journal.get(),
563c4bf6374SJason M. Bills                                       bmcLogEntry) != 0)
564c4bf6374SJason M. Bills             {
565c4bf6374SJason M. Bills                 messages::internalError(asyncResp->res);
566c4bf6374SJason M. Bills                 return;
567c4bf6374SJason M. Bills             }
568c4bf6374SJason M. Bills         }
569c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
570c4bf6374SJason M. Bills         if (skip + top < entryCount)
571c4bf6374SJason M. Bills         {
572c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
573c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
574c4bf6374SJason M. Bills                 std::to_string(skip + top);
575c4bf6374SJason M. Bills         }
576cb92c03bSAndrew Geissler #else
577cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
578cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
579cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
580cb92c03bSAndrew Geissler             [asyncResp](const boost::system::error_code ec,
581cb92c03bSAndrew Geissler                         GetManagedObjectsType &resp) {
582cb92c03bSAndrew Geissler                 if (ec)
583cb92c03bSAndrew Geissler                 {
584cb92c03bSAndrew Geissler                     // TODO Handle for specific error code
585cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
586cb92c03bSAndrew Geissler                         << "getLogEntriesIfaceData resp_handler got error "
587cb92c03bSAndrew Geissler                         << ec;
588cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
589cb92c03bSAndrew Geissler                     return;
590cb92c03bSAndrew Geissler                 }
591cb92c03bSAndrew Geissler                 nlohmann::json &entriesArray =
592cb92c03bSAndrew Geissler                     asyncResp->res.jsonValue["Members"];
593cb92c03bSAndrew Geissler                 entriesArray = nlohmann::json::array();
594cb92c03bSAndrew Geissler                 for (auto &objectPath : resp)
595cb92c03bSAndrew Geissler                 {
596cb92c03bSAndrew Geissler                     for (auto &interfaceMap : objectPath.second)
597cb92c03bSAndrew Geissler                     {
598cb92c03bSAndrew Geissler                         if (interfaceMap.first !=
599cb92c03bSAndrew Geissler                             "xyz.openbmc_project.Logging.Entry")
600cb92c03bSAndrew Geissler                         {
601cb92c03bSAndrew Geissler                             BMCWEB_LOG_DEBUG << "Bailing early on "
602cb92c03bSAndrew Geissler                                              << interfaceMap.first;
603cb92c03bSAndrew Geissler                             continue;
604cb92c03bSAndrew Geissler                         }
605cb92c03bSAndrew Geissler                         entriesArray.push_back({});
606cb92c03bSAndrew Geissler                         nlohmann::json &thisEntry = entriesArray.back();
607cb92c03bSAndrew Geissler                         uint32_t *id;
608cb92c03bSAndrew Geissler                         std::time_t timestamp;
609cb92c03bSAndrew Geissler                         std::string *severity, *message;
610cb92c03bSAndrew Geissler                         bool *resolved;
611cb92c03bSAndrew Geissler                         for (auto &propertyMap : interfaceMap.second)
612cb92c03bSAndrew Geissler                         {
613cb92c03bSAndrew Geissler                             if (propertyMap.first == "Id")
614cb92c03bSAndrew Geissler                             {
615cb92c03bSAndrew Geissler                                 id = sdbusplus::message::variant_ns::get_if<
616cb92c03bSAndrew Geissler                                     uint32_t>(&propertyMap.second);
617cb92c03bSAndrew Geissler                                 if (id == nullptr)
618cb92c03bSAndrew Geissler                                 {
619cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
620cb92c03bSAndrew Geissler                                                               "Id");
621cb92c03bSAndrew Geissler                                 }
622cb92c03bSAndrew Geissler                             }
623cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Timestamp")
624cb92c03bSAndrew Geissler                             {
625cb92c03bSAndrew Geissler                                 const uint64_t *millisTimeStamp =
626cb92c03bSAndrew Geissler                                     std::get_if<uint64_t>(&propertyMap.second);
627cb92c03bSAndrew Geissler                                 if (millisTimeStamp == nullptr)
628cb92c03bSAndrew Geissler                                 {
629cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
630cb92c03bSAndrew Geissler                                                               "Timestamp");
631cb92c03bSAndrew Geissler                                 }
632cb92c03bSAndrew Geissler                                 // Retrieve Created property with format:
633cb92c03bSAndrew Geissler                                 // yyyy-mm-ddThh:mm:ss
634cb92c03bSAndrew Geissler                                 std::chrono::milliseconds chronoTimeStamp(
635cb92c03bSAndrew Geissler                                     *millisTimeStamp);
636cb92c03bSAndrew Geissler                                 timestamp =
637cb92c03bSAndrew Geissler                                     std::chrono::duration_cast<
638cb92c03bSAndrew Geissler                                         std::chrono::seconds>(chronoTimeStamp)
639cb92c03bSAndrew Geissler                                         .count();
640cb92c03bSAndrew Geissler                             }
641cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Severity")
642cb92c03bSAndrew Geissler                             {
643cb92c03bSAndrew Geissler                                 severity = std::get_if<std::string>(
644cb92c03bSAndrew Geissler                                     &propertyMap.second);
645cb92c03bSAndrew Geissler                                 if (severity == nullptr)
646cb92c03bSAndrew Geissler                                 {
647cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
648cb92c03bSAndrew Geissler                                                               "Severity");
649cb92c03bSAndrew Geissler                                 }
650cb92c03bSAndrew Geissler                             }
651cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Message")
652cb92c03bSAndrew Geissler                             {
653cb92c03bSAndrew Geissler                                 message = std::get_if<std::string>(
654cb92c03bSAndrew Geissler                                     &propertyMap.second);
655cb92c03bSAndrew Geissler                                 if (message == nullptr)
656cb92c03bSAndrew Geissler                                 {
657cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
658cb92c03bSAndrew Geissler                                                               "Message");
659cb92c03bSAndrew Geissler                                 }
660cb92c03bSAndrew Geissler                             }
661cb92c03bSAndrew Geissler                         }
662cb92c03bSAndrew Geissler                         thisEntry = {
663cb92c03bSAndrew Geissler                             {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
664cb92c03bSAndrew Geissler                             {"@odata.context", "/redfish/v1/"
665cb92c03bSAndrew Geissler                                                "$metadata#LogEntry.LogEntry"},
666cb92c03bSAndrew Geissler                             {"@odata.id",
667cb92c03bSAndrew Geissler                              "/redfish/v1/Systems/system/LogServices/EventLog/"
668cb92c03bSAndrew Geissler                              "Entries/" +
669cb92c03bSAndrew Geissler                                  std::to_string(*id)},
670cb92c03bSAndrew Geissler                             {"Name", "System DBus Event Log Entry"},
671cb92c03bSAndrew Geissler                             {"Id", std::to_string(*id)},
672cb92c03bSAndrew Geissler                             {"Message", *message},
673cb92c03bSAndrew Geissler                             {"EntryType", "Event"},
674cb92c03bSAndrew Geissler                             {"Severity",
675cb92c03bSAndrew Geissler                              translateSeverityDbusToRedfish(*severity)},
676cb92c03bSAndrew Geissler                             {"Created", crow::utility::getDateTime(timestamp)}};
677cb92c03bSAndrew Geissler                     }
678cb92c03bSAndrew Geissler                 }
679cb92c03bSAndrew Geissler                 std::sort(entriesArray.begin(), entriesArray.end(),
680cb92c03bSAndrew Geissler                           [](const nlohmann::json &left,
681cb92c03bSAndrew Geissler                              const nlohmann::json &right) {
682cb92c03bSAndrew Geissler                               return (left["Id"] <= right["Id"]);
683cb92c03bSAndrew Geissler                           });
684cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue["Members@odata.count"] =
685cb92c03bSAndrew Geissler                     entriesArray.size();
686cb92c03bSAndrew Geissler             },
687cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
688cb92c03bSAndrew Geissler             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
689cb92c03bSAndrew Geissler #endif
690c4bf6374SJason M. Bills     }
691c4bf6374SJason M. Bills };
692c4bf6374SJason M. Bills 
693c4bf6374SJason M. Bills class EventLogEntry : public Node
694c4bf6374SJason M. Bills {
695c4bf6374SJason M. Bills   public:
696c4bf6374SJason M. Bills     EventLogEntry(CrowApp &app) :
697c4bf6374SJason M. Bills         Node(app,
698029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
699029573d4SEd Tanous              std::string())
700c4bf6374SJason M. Bills     {
701c4bf6374SJason M. Bills         entityPrivileges = {
702c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
703c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
704c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
705c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
706c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
707c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
708c4bf6374SJason M. Bills     }
709c4bf6374SJason M. Bills 
710c4bf6374SJason M. Bills   private:
711c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
712c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
713c4bf6374SJason M. Bills     {
714c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
715029573d4SEd Tanous         if (params.size() != 1)
716c4bf6374SJason M. Bills         {
717c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
718c4bf6374SJason M. Bills             return;
719c4bf6374SJason M. Bills         }
720029573d4SEd Tanous         const std::string &entryID = params[0];
721cb92c03bSAndrew Geissler 
722cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
723c4bf6374SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
724c4bf6374SJason M. Bills         uint64_t ts = 0;
725c4bf6374SJason M. Bills         uint16_t index = 0;
726c4bf6374SJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
727c4bf6374SJason M. Bills         {
728c4bf6374SJason M. Bills             return;
729c4bf6374SJason M. Bills         }
730c4bf6374SJason M. Bills 
731c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
732c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
733c4bf6374SJason M. Bills         if (ret < 0)
734c4bf6374SJason M. Bills         {
735c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
736c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
737c4bf6374SJason M. Bills             return;
738c4bf6374SJason M. Bills         }
739c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
740c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
741c4bf6374SJason M. Bills         journalTmp = nullptr;
742c4bf6374SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
743c4bf6374SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
744c4bf6374SJason M. Bills         for (int i = 0; i <= index; i++)
745c4bf6374SJason M. Bills         {
746c4bf6374SJason M. Bills             sd_journal_next(journal.get());
747c4bf6374SJason M. Bills         }
748c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
749c4bf6374SJason M. Bills         std::string idStr;
750c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
751c4bf6374SJason M. Bills         {
752c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
753c4bf6374SJason M. Bills             return;
754c4bf6374SJason M. Bills         }
755c4bf6374SJason M. Bills 
756cd50aa42SJason M. Bills         // only use journal entries that contain a REDFISH_MESSAGE_ID field
75739e77504SEd Tanous         std::string_view messageID;
758c4bf6374SJason M. Bills         ret =
759c4bf6374SJason M. Bills             getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
760c4bf6374SJason M. Bills         if (ret < 0)
761c4bf6374SJason M. Bills         {
762029573d4SEd Tanous             messages::resourceNotFound(asyncResp->res, "LogEntry", "system");
763c4bf6374SJason M. Bills             return;
764c4bf6374SJason M. Bills         }
765c4bf6374SJason M. Bills 
766029573d4SEd Tanous         if (fillEventLogEntryJson(entryID, messageID, journal.get(),
767c4bf6374SJason M. Bills                                   asyncResp->res.jsonValue) != 0)
768c4bf6374SJason M. Bills         {
769c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
770c4bf6374SJason M. Bills             return;
771c4bf6374SJason M. Bills         }
772cb92c03bSAndrew Geissler #else
773cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
774cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
775cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
776cb92c03bSAndrew Geissler             [asyncResp, entryID](const boost::system::error_code ec,
777cb92c03bSAndrew Geissler                                  GetManagedPropertyType &resp) {
778cb92c03bSAndrew Geissler                 if (ec)
779cb92c03bSAndrew Geissler                 {
780cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
781cb92c03bSAndrew Geissler                         << "EventLogEntry (DBus) resp_handler got error " << ec;
782cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
783cb92c03bSAndrew Geissler                     return;
784cb92c03bSAndrew Geissler                 }
785cb92c03bSAndrew Geissler                 uint32_t *id;
786cb92c03bSAndrew Geissler                 std::time_t timestamp;
787cb92c03bSAndrew Geissler                 std::string *severity, *message;
788cb92c03bSAndrew Geissler                 bool *resolved;
789cb92c03bSAndrew Geissler                 for (auto &propertyMap : resp)
790cb92c03bSAndrew Geissler                 {
791cb92c03bSAndrew Geissler                     if (propertyMap.first == "Id")
792cb92c03bSAndrew Geissler                     {
793cb92c03bSAndrew Geissler                         id = std::get_if<uint32_t>(&propertyMap.second);
794cb92c03bSAndrew Geissler                         if (id == nullptr)
795cb92c03bSAndrew Geissler                         {
796cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res, "Id");
797cb92c03bSAndrew Geissler                         }
798cb92c03bSAndrew Geissler                     }
799cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Timestamp")
800cb92c03bSAndrew Geissler                     {
801cb92c03bSAndrew Geissler                         const uint64_t *millisTimeStamp =
802cb92c03bSAndrew Geissler                             std::get_if<uint64_t>(&propertyMap.second);
803cb92c03bSAndrew Geissler                         if (millisTimeStamp == nullptr)
804cb92c03bSAndrew Geissler                         {
805cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
806cb92c03bSAndrew Geissler                                                       "Timestamp");
807cb92c03bSAndrew Geissler                         }
808cb92c03bSAndrew Geissler                         // Retrieve Created property with format:
809cb92c03bSAndrew Geissler                         // yyyy-mm-ddThh:mm:ss
810cb92c03bSAndrew Geissler                         std::chrono::milliseconds chronoTimeStamp(
811cb92c03bSAndrew Geissler                             *millisTimeStamp);
812cb92c03bSAndrew Geissler                         timestamp =
813cb92c03bSAndrew Geissler                             std::chrono::duration_cast<std::chrono::seconds>(
814cb92c03bSAndrew Geissler                                 chronoTimeStamp)
815cb92c03bSAndrew Geissler                                 .count();
816cb92c03bSAndrew Geissler                     }
817cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Severity")
818cb92c03bSAndrew Geissler                     {
819cb92c03bSAndrew Geissler                         severity =
820cb92c03bSAndrew Geissler                             std::get_if<std::string>(&propertyMap.second);
821cb92c03bSAndrew Geissler                         if (severity == nullptr)
822cb92c03bSAndrew Geissler                         {
823cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
824cb92c03bSAndrew Geissler                                                       "Severity");
825cb92c03bSAndrew Geissler                         }
826cb92c03bSAndrew Geissler                     }
827cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Message")
828cb92c03bSAndrew Geissler                     {
829cb92c03bSAndrew Geissler                         message = std::get_if<std::string>(&propertyMap.second);
830cb92c03bSAndrew Geissler                         if (message == nullptr)
831cb92c03bSAndrew Geissler                         {
832cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
833cb92c03bSAndrew Geissler                                                       "Message");
834cb92c03bSAndrew Geissler                         }
835cb92c03bSAndrew Geissler                     }
836cb92c03bSAndrew Geissler                 }
837cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue = {
838cb92c03bSAndrew Geissler                     {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
839cb92c03bSAndrew Geissler                     {"@odata.context", "/redfish/v1/"
840cb92c03bSAndrew Geissler                                        "$metadata#LogEntry.LogEntry"},
841cb92c03bSAndrew Geissler                     {"@odata.id",
842cb92c03bSAndrew Geissler                      "/redfish/v1/Systems/system/LogServices/EventLog/"
843cb92c03bSAndrew Geissler                      "Entries/" +
844cb92c03bSAndrew Geissler                          std::to_string(*id)},
845cb92c03bSAndrew Geissler                     {"Name", "System DBus Event Log Entry"},
846cb92c03bSAndrew Geissler                     {"Id", std::to_string(*id)},
847cb92c03bSAndrew Geissler                     {"Message", *message},
848cb92c03bSAndrew Geissler                     {"EntryType", "Event"},
849cb92c03bSAndrew Geissler                     {"Severity", translateSeverityDbusToRedfish(*severity)},
850cb92c03bSAndrew Geissler                     {"Created", crow::utility::getDateTime(timestamp)}};
851cb92c03bSAndrew Geissler             },
852cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging",
853cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging/entry/" + entryID,
854cb92c03bSAndrew Geissler             "org.freedesktop.DBus.Properties", "GetAll",
855cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging.Entry");
856cb92c03bSAndrew Geissler #endif
857c4bf6374SJason M. Bills     }
858c4bf6374SJason M. Bills };
859c4bf6374SJason M. Bills 
860c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
861c4bf6374SJason M. Bills {
862c4bf6374SJason M. Bills   public:
863c4bf6374SJason M. Bills     template <typename CrowApp>
864c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
8654ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
8661da66f75SEd Tanous     {
8671da66f75SEd Tanous         entityPrivileges = {
868e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
869e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
870e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
871e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
872e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
873e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
8741da66f75SEd Tanous     }
8751da66f75SEd Tanous 
8761da66f75SEd Tanous   private:
8771da66f75SEd Tanous     /**
8781da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
8791da66f75SEd Tanous      */
8801da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
8811da66f75SEd Tanous                const std::vector<std::string> &params) override
8821da66f75SEd Tanous     {
883e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
8841da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
8851da66f75SEd Tanous         // it has a duplicate entry for members
886e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
8871da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
888e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
889c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
890e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
891e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
892e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
893e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
8941da66f75SEd Tanous             "Collection of LogServices for this Manager";
895c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
896c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
897c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
898c4bf6374SJason M. Bills         logServiceArray.push_back(
899cb92c03bSAndrew Geissler             {{ "@odata.id",
900cb92c03bSAndrew Geissler                "/redfish/v1/Managers/bmc/LogServices/Journal" }});
901c4bf6374SJason M. Bills #endif
902e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
903c4bf6374SJason M. Bills             logServiceArray.size();
9041da66f75SEd Tanous     }
9051da66f75SEd Tanous };
9061da66f75SEd Tanous 
907c4bf6374SJason M. Bills class BMCJournalLogService : public Node
9081da66f75SEd Tanous {
9091da66f75SEd Tanous   public:
9101da66f75SEd Tanous     template <typename CrowApp>
911c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
912c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
913e1f26343SJason M. Bills     {
914e1f26343SJason M. Bills         entityPrivileges = {
915e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
916e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
917e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
918e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
919e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
920e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
921e1f26343SJason M. Bills     }
922e1f26343SJason M. Bills 
923e1f26343SJason M. Bills   private:
924e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
925e1f26343SJason M. Bills                const std::vector<std::string> &params) override
926e1f26343SJason M. Bills     {
927e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
928e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
929e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
9300f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
9310f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
932e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
933e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
934c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
935c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
936c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
937e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
938cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
939cd50aa42SJason M. Bills             {"@odata.id",
940cd50aa42SJason M. Bills              "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}};
941e1f26343SJason M. Bills     }
942e1f26343SJason M. Bills };
943e1f26343SJason M. Bills 
944c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
945e1f26343SJason M. Bills                                       sd_journal *journal,
946c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
947e1f26343SJason M. Bills {
948e1f26343SJason M. Bills     // Get the Log Entry contents
949e1f26343SJason M. Bills     int ret = 0;
950e1f26343SJason M. Bills 
95139e77504SEd Tanous     std::string_view msg;
95216428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
953e1f26343SJason M. Bills     if (ret < 0)
954e1f26343SJason M. Bills     {
955e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
956e1f26343SJason M. Bills         return 1;
957e1f26343SJason M. Bills     }
958e1f26343SJason M. Bills 
959e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
960e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
96116428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
962e1f26343SJason M. Bills     if (ret < 0)
963e1f26343SJason M. Bills     {
964e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
965e1f26343SJason M. Bills         return 1;
966e1f26343SJason M. Bills     }
967e1f26343SJason M. Bills 
968e1f26343SJason M. Bills     // Get the Created time from the timestamp
96916428a1aSJason M. Bills     std::string entryTimeStr;
97016428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
971e1f26343SJason M. Bills     {
97216428a1aSJason M. Bills         return 1;
973e1f26343SJason M. Bills     }
974e1f26343SJason M. Bills 
975e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
976c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
977cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
978e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
979c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
980c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
981e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
982c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
98316428a1aSJason M. Bills         {"Message", msg},
984e1f26343SJason M. Bills         {"EntryType", "Oem"},
985e1f26343SJason M. Bills         {"Severity",
986e1f26343SJason M. Bills          severity <= 2 ? "Critical"
987e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
988e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
989e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
990e1f26343SJason M. Bills     return 0;
991e1f26343SJason M. Bills }
992e1f26343SJason M. Bills 
993c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
994e1f26343SJason M. Bills {
995e1f26343SJason M. Bills   public:
996e1f26343SJason M. Bills     template <typename CrowApp>
997c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
998c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
999e1f26343SJason M. Bills     {
1000e1f26343SJason M. Bills         entityPrivileges = {
1001e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1002e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1003e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1004e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1005e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1006e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1007e1f26343SJason M. Bills     }
1008e1f26343SJason M. Bills 
1009e1f26343SJason M. Bills   private:
1010e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1011e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1012e1f26343SJason M. Bills     {
1013e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1014193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
1015193ad2faSJason M. Bills         long skip = 0;
1016193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
101716428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
1018193ad2faSJason M. Bills         {
1019193ad2faSJason M. Bills             return;
1020193ad2faSJason M. Bills         }
102116428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
1022193ad2faSJason M. Bills         {
1023193ad2faSJason M. Bills             return;
1024193ad2faSJason M. Bills         }
1025e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
1026e1f26343SJason M. Bills         // it has a duplicate entry for members
1027e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1028e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
10290f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
10300f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1031e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1032c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1033e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1034c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1035e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1036e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
1037e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
10380f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
10390f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
1040e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1041e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
1042e1f26343SJason M. Bills 
1043e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
1044e1f26343SJason M. Bills         // for each entry
1045e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1046e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1047e1f26343SJason M. Bills         if (ret < 0)
1048e1f26343SJason M. Bills         {
1049e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1050f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1051e1f26343SJason M. Bills             return;
1052e1f26343SJason M. Bills         }
1053e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1054e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1055e1f26343SJason M. Bills         journalTmp = nullptr;
1056b01bf299SEd Tanous         uint64_t entryCount = 0;
1057e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
1058e1f26343SJason M. Bills         {
1059193ad2faSJason M. Bills             entryCount++;
1060193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
1061193ad2faSJason M. Bills             // start) and top (number of entries to display)
1062193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
1063193ad2faSJason M. Bills             {
1064193ad2faSJason M. Bills                 continue;
1065193ad2faSJason M. Bills             }
1066193ad2faSJason M. Bills 
106716428a1aSJason M. Bills             std::string idStr;
106816428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
1069e1f26343SJason M. Bills             {
1070e1f26343SJason M. Bills                 continue;
1071e1f26343SJason M. Bills             }
1072e1f26343SJason M. Bills 
1073e1f26343SJason M. Bills             logEntryArray.push_back({});
1074c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1075c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1076c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
1077e1f26343SJason M. Bills             {
1078f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
1079e1f26343SJason M. Bills                 return;
1080e1f26343SJason M. Bills             }
1081e1f26343SJason M. Bills         }
1082193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1083193ad2faSJason M. Bills         if (skip + top < entryCount)
1084193ad2faSJason M. Bills         {
1085193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
1086c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
1087193ad2faSJason M. Bills                 std::to_string(skip + top);
1088193ad2faSJason M. Bills         }
1089e1f26343SJason M. Bills     }
1090e1f26343SJason M. Bills };
1091e1f26343SJason M. Bills 
1092c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
1093e1f26343SJason M. Bills {
1094e1f26343SJason M. Bills   public:
1095c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
1096c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
1097e1f26343SJason M. Bills              std::string())
1098e1f26343SJason M. Bills     {
1099e1f26343SJason M. Bills         entityPrivileges = {
1100e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1101e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1102e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1103e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1104e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1105e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1106e1f26343SJason M. Bills     }
1107e1f26343SJason M. Bills 
1108e1f26343SJason M. Bills   private:
1109e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1110e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1111e1f26343SJason M. Bills     {
1112e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1113e1f26343SJason M. Bills         if (params.size() != 1)
1114e1f26343SJason M. Bills         {
1115f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1116e1f26343SJason M. Bills             return;
1117e1f26343SJason M. Bills         }
111816428a1aSJason M. Bills         const std::string &entryID = params[0];
1119e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
1120e1f26343SJason M. Bills         uint64_t ts = 0;
1121e1f26343SJason M. Bills         uint16_t index = 0;
112216428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
1123e1f26343SJason M. Bills         {
112416428a1aSJason M. Bills             return;
1125e1f26343SJason M. Bills         }
1126e1f26343SJason M. Bills 
1127e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1128e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1129e1f26343SJason M. Bills         if (ret < 0)
1130e1f26343SJason M. Bills         {
1131e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1132f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1133e1f26343SJason M. Bills             return;
1134e1f26343SJason M. Bills         }
1135e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1136e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1137e1f26343SJason M. Bills         journalTmp = nullptr;
1138e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
1139e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1140e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
1141e1f26343SJason M. Bills         {
1142e1f26343SJason M. Bills             sd_journal_next(journal.get());
1143e1f26343SJason M. Bills         }
1144c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
1145c4bf6374SJason M. Bills         std::string idStr;
1146c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
1147c4bf6374SJason M. Bills         {
1148c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
1149c4bf6374SJason M. Bills             return;
1150c4bf6374SJason M. Bills         }
1151c4bf6374SJason M. Bills 
1152c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1153e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
1154e1f26343SJason M. Bills         {
1155f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1156e1f26343SJason M. Bills             return;
1157e1f26343SJason M. Bills         }
1158e1f26343SJason M. Bills     }
1159e1f26343SJason M. Bills };
1160e1f26343SJason M. Bills 
1161e1f26343SJason M. Bills class CPULogService : public Node
1162e1f26343SJason M. Bills {
1163e1f26343SJason M. Bills   public:
1164e1f26343SJason M. Bills     template <typename CrowApp>
1165e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
1166d53dd41fSJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/")
11671da66f75SEd Tanous     {
11681da66f75SEd Tanous         entityPrivileges = {
1169e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1170e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1171e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1172e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1173e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1174e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
11751da66f75SEd Tanous     }
11761da66f75SEd Tanous 
11771da66f75SEd Tanous   private:
11781da66f75SEd Tanous     /**
11791da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
11801da66f75SEd Tanous      */
11811da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
11821da66f75SEd Tanous                const std::vector<std::string> &params) override
11831da66f75SEd Tanous     {
1184e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
11851da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
11860f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
1187d53dd41fSJason M. Bills             "/redfish/v1/Systems/system/LogServices/CpuLog";
1188e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1189e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
1190e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1191c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1192e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
1193e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
1194e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
1195e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1196e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
1197cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1198cd50aa42SJason M. Bills             {"@odata.id",
1199f9fc2dffSJason M. Bills              "/redfish/v1/Systems/system/LogServices/CpuLog/Entries"}};
1200e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
12011da66f75SEd Tanous             {"Oem",
120248e4639eSJason M. Bills              {{"#CpuLog.OnDemand",
1203d53dd41fSJason M. Bills                {{"target", "/redfish/v1/Systems/system/LogServices/CpuLog/"
120448e4639eSJason M. Bills                            "Actions/Oem/CpuLog.OnDemand"}}}}}};
12051da66f75SEd Tanous 
12061da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
1207e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
12081da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
1209cb92c03bSAndrew Geissler              { { "target",
1210cb92c03bSAndrew Geissler                  "/redfish/v1/Systems/system/LogServices/CpuLog/"
1211d53dd41fSJason M. Bills                  "Actions/Oem/CpuLog.SendRawPeci" } }});
12121da66f75SEd Tanous #endif
12131da66f75SEd Tanous     }
12141da66f75SEd Tanous };
12151da66f75SEd Tanous 
1216e1f26343SJason M. Bills class CPULogEntryCollection : public Node
12171da66f75SEd Tanous {
12181da66f75SEd Tanous   public:
12191da66f75SEd Tanous     template <typename CrowApp>
1220e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
1221d53dd41fSJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/")
12221da66f75SEd Tanous     {
12231da66f75SEd Tanous         entityPrivileges = {
1224e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1225e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1226e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1227e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1228e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1229e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
12301da66f75SEd Tanous     }
12311da66f75SEd Tanous 
12321da66f75SEd Tanous   private:
12331da66f75SEd Tanous     /**
12341da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
12351da66f75SEd Tanous      */
12361da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
12371da66f75SEd Tanous                const std::vector<std::string> &params) override
12381da66f75SEd Tanous     {
1239e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
12401da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
12411da66f75SEd Tanous         // it has a duplicate entry for members
1242e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
1243e1f26343SJason M. Bills                                          const boost::system::error_code ec,
12441da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
12451da66f75SEd Tanous             if (ec)
12461da66f75SEd Tanous             {
12471da66f75SEd Tanous                 if (ec.value() !=
12481da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
12491da66f75SEd Tanous                 {
12501da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
12511da66f75SEd Tanous                                      << ec.message();
1252f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
12531da66f75SEd Tanous                     return;
12541da66f75SEd Tanous                 }
12551da66f75SEd Tanous             }
1256e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
12571da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
12580f74e643SEd Tanous             asyncResp->res.jsonValue["@odata.id"] =
1259d53dd41fSJason M. Bills                 "/redfish/v1/Systems/system/LogServices/CpuLog/Entries";
1260e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1261d53dd41fSJason M. Bills                 "/redfish/v1/"
1262d53dd41fSJason M. Bills                 "$metadata#LogEntryCollection.LogEntryCollection";
1263e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
1264e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1265e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
1266e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1267e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
12681da66f75SEd Tanous             for (const std::string &objpath : resp)
12691da66f75SEd Tanous             {
127048e4639eSJason M. Bills                 // Don't list the on-demand log
127148e4639eSJason M. Bills                 if (objpath.compare(cpuLogOnDemandPath) == 0)
12721da66f75SEd Tanous                 {
12731da66f75SEd Tanous                     continue;
12741da66f75SEd Tanous                 }
12754ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
12764ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
12771da66f75SEd Tanous                 {
1278e1f26343SJason M. Bills                     logEntryArray.push_back(
1279d53dd41fSJason M. Bills                         {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1280e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
12814ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
12821da66f75SEd Tanous                 }
12831da66f75SEd Tanous             }
1284e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1285e1f26343SJason M. Bills                 logEntryArray.size();
12861da66f75SEd Tanous         };
12871da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
12881da66f75SEd Tanous             std::move(getLogEntriesCallback),
12891da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
12901da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
12911da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
12924ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
12931da66f75SEd Tanous     }
12941da66f75SEd Tanous };
12951da66f75SEd Tanous 
12961da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
12971da66f75SEd Tanous {
1298c4d00437SJason M. Bills     nlohmann::json::const_iterator cdIt = cpuLog.find("crashlog_data");
1299c4d00437SJason M. Bills     if (cdIt != cpuLog.end())
13001da66f75SEd Tanous     {
1301c4d00437SJason M. Bills         nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1302c4d00437SJason M. Bills         if (siIt != cdIt->end())
13031da66f75SEd Tanous         {
1304c4d00437SJason M. Bills             nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1305c4d00437SJason M. Bills             if (tsIt != siIt->end())
1306c4d00437SJason M. Bills             {
1307c4d00437SJason M. Bills                 const std::string *logTime =
1308c4d00437SJason M. Bills                     tsIt->get_ptr<const std::string *>();
13091da66f75SEd Tanous                 if (logTime != nullptr)
13101da66f75SEd Tanous                 {
13111da66f75SEd Tanous                     return *logTime;
13121da66f75SEd Tanous                 }
13131da66f75SEd Tanous             }
13141da66f75SEd Tanous         }
1315c4d00437SJason M. Bills     }
13161da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
13171da66f75SEd Tanous 
13181da66f75SEd Tanous     return std::string();
13191da66f75SEd Tanous }
13201da66f75SEd Tanous 
1321e1f26343SJason M. Bills class CPULogEntry : public Node
13221da66f75SEd Tanous {
13231da66f75SEd Tanous   public:
1324e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
1325d53dd41fSJason M. Bills         Node(app,
1326d53dd41fSJason M. Bills              "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/<str>/",
13271da66f75SEd Tanous              std::string())
13281da66f75SEd Tanous     {
13291da66f75SEd Tanous         entityPrivileges = {
1330e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1331e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1332e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1333e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1334e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1335e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
13361da66f75SEd Tanous     }
13371da66f75SEd Tanous 
13381da66f75SEd Tanous   private:
13391da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
13401da66f75SEd Tanous                const std::vector<std::string> &params) override
13411da66f75SEd Tanous     {
1342e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
13431da66f75SEd Tanous         if (params.size() != 1)
13441da66f75SEd Tanous         {
1345f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
13461da66f75SEd Tanous             return;
13471da66f75SEd Tanous         }
1348b01bf299SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1349abf2add6SEd Tanous         auto getStoredLogCallback = [asyncResp, logId](
1350abf2add6SEd Tanous                                         const boost::system::error_code ec,
1351abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
13521da66f75SEd Tanous             if (ec)
13531da66f75SEd Tanous             {
1354abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1355f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
13561da66f75SEd Tanous                 return;
13571da66f75SEd Tanous             }
1358abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
13591da66f75SEd Tanous             if (log == nullptr)
13601da66f75SEd Tanous             {
1361f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
13621da66f75SEd Tanous                 return;
13631da66f75SEd Tanous             }
13641da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
13651da66f75SEd Tanous             if (j.is_discarded())
13661da66f75SEd Tanous             {
1367f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
13681da66f75SEd Tanous                 return;
13691da66f75SEd Tanous             }
13701da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1371e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1372cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1373abf2add6SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
13741da66f75SEd Tanous                 {"@odata.id",
1375d53dd41fSJason M. Bills                  "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/" +
13764ed77cd5SEd Tanous                      std::to_string(logId)},
13771da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
13784ed77cd5SEd Tanous                 {"Id", logId},
13791da66f75SEd Tanous                 {"EntryType", "Oem"},
13801da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
13811da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
13821da66f75SEd Tanous                 {"Created", std::move(t)}};
13831da66f75SEd Tanous         };
13841da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
13854ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
13864ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
13874ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
13881da66f75SEd Tanous     }
13891da66f75SEd Tanous };
13901da66f75SEd Tanous 
139148e4639eSJason M. Bills class OnDemandCPULog : public Node
13921da66f75SEd Tanous {
13931da66f75SEd Tanous   public:
139448e4639eSJason M. Bills     OnDemandCPULog(CrowApp &app) :
1395d53dd41fSJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Actions/Oem/"
139648e4639eSJason M. Bills                   "CpuLog.OnDemand/")
13971da66f75SEd Tanous     {
13981da66f75SEd Tanous         entityPrivileges = {
1399e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1400e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1401e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1402e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1403e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1404e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
14051da66f75SEd Tanous     }
14061da66f75SEd Tanous 
14071da66f75SEd Tanous   private:
14081da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
14091da66f75SEd Tanous                 const std::vector<std::string> &params) override
14101da66f75SEd Tanous     {
1411e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
141248e4639eSJason M. Bills         static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
14131da66f75SEd Tanous 
141448e4639eSJason M. Bills         // Only allow one OnDemand Log request at a time
141548e4639eSJason M. Bills         if (onDemandLogMatcher != nullptr)
14161da66f75SEd Tanous         {
1417e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1418f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
14191da66f75SEd Tanous             return;
14201da66f75SEd Tanous         }
14211da66f75SEd Tanous         // Make this static so it survives outside this method
14221da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
14231da66f75SEd Tanous 
14241da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1425e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
142648e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
14271da66f75SEd Tanous             if (ec)
14281da66f75SEd Tanous             {
14291da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
14301da66f75SEd Tanous                 // completion.
14311da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
14321da66f75SEd Tanous                 {
14331da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
14341da66f75SEd Tanous                 }
14351da66f75SEd Tanous                 return;
14361da66f75SEd Tanous             }
143748e4639eSJason M. Bills             BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
14381da66f75SEd Tanous 
1439f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
14401da66f75SEd Tanous         });
14411da66f75SEd Tanous 
144248e4639eSJason M. Bills         auto onDemandLogMatcherCallback = [asyncResp](
14431da66f75SEd Tanous                                               sdbusplus::message::message &m) {
144448e4639eSJason M. Bills             BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
14451da66f75SEd Tanous             boost::system::error_code ec;
14461da66f75SEd Tanous             timeout.cancel(ec);
14471da66f75SEd Tanous             if (ec)
14481da66f75SEd Tanous             {
14491da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
14501da66f75SEd Tanous             }
14514ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
14521da66f75SEd Tanous             boost::container::flat_map<
1453abf2add6SEd Tanous                 std::string, boost::container::flat_map<
1454abf2add6SEd Tanous                                  std::string, std::variant<std::string>>>
14554ed77cd5SEd Tanous                 interfacesAdded;
14564ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1457abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(
14581b6b96c5SEd Tanous                 &interfacesAdded[cpuLogInterface]["Log"]);
14591da66f75SEd Tanous             if (log == nullptr)
14601da66f75SEd Tanous             {
1461f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
146248e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
14631da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
14641da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
14651da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
14661da66f75SEd Tanous                 // be the last thing done.
146748e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
14681da66f75SEd Tanous                 return;
14691da66f75SEd Tanous             }
14701da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
14711da66f75SEd Tanous             if (j.is_discarded())
14721da66f75SEd Tanous             {
1473f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
147448e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
14751da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
14761da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
14771da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
14781da66f75SEd Tanous                 // be the last thing done.
147948e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
14801da66f75SEd Tanous                 return;
14811da66f75SEd Tanous             }
14821da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1483e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1484cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
14851da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
14861da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
14871da66f75SEd Tanous                 {"EntryType", "Oem"},
14881da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
14891da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
14901da66f75SEd Tanous                 {"Created", std::move(t)}};
149148e4639eSJason M. Bills             // Careful with onDemandLogMatcher.  It is a unique_ptr to the
14921da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
14931da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
14941da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
14951da66f75SEd Tanous             // thing done.
149648e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
14971da66f75SEd Tanous         };
149848e4639eSJason M. Bills         onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
14991da66f75SEd Tanous             *crow::connections::systemBus,
15001da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
150148e4639eSJason M. Bills                 sdbusplus::bus::match::rules::argNpath(0, cpuLogOnDemandPath),
150248e4639eSJason M. Bills             std::move(onDemandLogMatcherCallback));
15031da66f75SEd Tanous 
150448e4639eSJason M. Bills         auto generateonDemandLogCallback =
1505e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
15061da66f75SEd Tanous                         const std::string &resp) {
15071da66f75SEd Tanous                 if (ec)
15081da66f75SEd Tanous                 {
15091da66f75SEd Tanous                     if (ec.value() ==
15101da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
15111da66f75SEd Tanous                     {
1512f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
15131da66f75SEd Tanous                     }
15141da66f75SEd Tanous                     else
15151da66f75SEd Tanous                     {
1516f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
15171da66f75SEd Tanous                     }
15181da66f75SEd Tanous                     boost::system::error_code timeoutec;
15191da66f75SEd Tanous                     timeout.cancel(timeoutec);
15201da66f75SEd Tanous                     if (timeoutec)
15211da66f75SEd Tanous                     {
15221da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
15231da66f75SEd Tanous                                          << timeoutec;
15241da66f75SEd Tanous                     }
152548e4639eSJason M. Bills                     onDemandLogMatcher = nullptr;
15261da66f75SEd Tanous                     return;
15271da66f75SEd Tanous                 }
15281da66f75SEd Tanous             };
15291da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
153048e4639eSJason M. Bills             std::move(generateonDemandLogCallback), cpuLogObject, cpuLogPath,
153148e4639eSJason M. Bills             cpuLogOnDemandInterface, "GenerateOnDemandLog");
15321da66f75SEd Tanous     }
15331da66f75SEd Tanous };
15341da66f75SEd Tanous 
1535e1f26343SJason M. Bills class SendRawPECI : public Node
15361da66f75SEd Tanous {
15371da66f75SEd Tanous   public:
1538e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
1539d53dd41fSJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Actions/Oem/"
1540e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
15411da66f75SEd Tanous     {
15421da66f75SEd Tanous         entityPrivileges = {
15431da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
15441da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
15451da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
15461da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
15471da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
15481da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
15491da66f75SEd Tanous     }
15501da66f75SEd Tanous 
15511da66f75SEd Tanous   private:
15521da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
15531da66f75SEd Tanous                 const std::vector<std::string> &params) override
15541da66f75SEd Tanous     {
1555e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1556b1556427SEd Tanous         uint8_t clientAddress = 0;
1557b1556427SEd Tanous         uint8_t readLength = 0;
15581da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
1559b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1560b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
1561b1556427SEd Tanous                                  peciCommand))
15621da66f75SEd Tanous         {
15631da66f75SEd Tanous             return;
15641da66f75SEd Tanous         }
1565b1556427SEd Tanous 
15661da66f75SEd Tanous         // Callback to return the Raw PECI response
1567e1f26343SJason M. Bills         auto sendRawPECICallback =
1568e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
15691da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
15701da66f75SEd Tanous                 if (ec)
15711da66f75SEd Tanous                 {
15721da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
15731da66f75SEd Tanous                                      << ec.message();
1574f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
15751da66f75SEd Tanous                     return;
15761da66f75SEd Tanous                 }
1577e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
15781da66f75SEd Tanous                                             {"PECIResponse", resp}};
15791da66f75SEd Tanous             };
15801da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
15811da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1582e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
1583e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
15844ed77cd5SEd Tanous             peciCommand);
15851da66f75SEd Tanous     }
15861da66f75SEd Tanous };
15871da66f75SEd Tanous 
1588cb92c03bSAndrew Geissler /**
1589cb92c03bSAndrew Geissler  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1590cb92c03bSAndrew Geissler  */
1591cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node
1592cb92c03bSAndrew Geissler {
1593cb92c03bSAndrew Geissler   public:
1594cb92c03bSAndrew Geissler     DBusLogServiceActionsClear(CrowApp &app) :
1595cb92c03bSAndrew Geissler         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1596cb92c03bSAndrew Geissler                   "LogService.Reset")
1597cb92c03bSAndrew Geissler     {
1598cb92c03bSAndrew Geissler         entityPrivileges = {
1599cb92c03bSAndrew Geissler             {boost::beast::http::verb::get, {{"Login"}}},
1600cb92c03bSAndrew Geissler             {boost::beast::http::verb::head, {{"Login"}}},
1601cb92c03bSAndrew Geissler             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1602cb92c03bSAndrew Geissler             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1603cb92c03bSAndrew Geissler             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1604cb92c03bSAndrew Geissler             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1605cb92c03bSAndrew Geissler     }
1606cb92c03bSAndrew Geissler 
1607cb92c03bSAndrew Geissler   private:
1608cb92c03bSAndrew Geissler     /**
1609cb92c03bSAndrew Geissler      * Function handles POST method request.
1610cb92c03bSAndrew Geissler      * The Clear Log actions does not require any parameter.The action deletes
1611cb92c03bSAndrew Geissler      * all entries found in the Entries collection for this Log Service.
1612cb92c03bSAndrew Geissler      */
1613cb92c03bSAndrew Geissler     void doPost(crow::Response &res, const crow::Request &req,
1614cb92c03bSAndrew Geissler                 const std::vector<std::string> &params) override
1615cb92c03bSAndrew Geissler     {
1616cb92c03bSAndrew Geissler         BMCWEB_LOG_DEBUG << "Do delete all entries.";
1617cb92c03bSAndrew Geissler 
1618cb92c03bSAndrew Geissler         auto asyncResp = std::make_shared<AsyncResp>(res);
1619cb92c03bSAndrew Geissler         // Process response from Logging service.
1620cb92c03bSAndrew Geissler         auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1621cb92c03bSAndrew Geissler             BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1622cb92c03bSAndrew Geissler             if (ec)
1623cb92c03bSAndrew Geissler             {
1624cb92c03bSAndrew Geissler                 // TODO Handle for specific error code
1625cb92c03bSAndrew Geissler                 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1626cb92c03bSAndrew Geissler                 asyncResp->res.result(
1627cb92c03bSAndrew Geissler                     boost::beast::http::status::internal_server_error);
1628cb92c03bSAndrew Geissler                 return;
1629cb92c03bSAndrew Geissler             }
1630cb92c03bSAndrew Geissler 
1631cb92c03bSAndrew Geissler             asyncResp->res.result(boost::beast::http::status::no_content);
1632cb92c03bSAndrew Geissler         };
1633cb92c03bSAndrew Geissler 
1634cb92c03bSAndrew Geissler         // Make call to Logging service to request Clear Log
1635cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
1636cb92c03bSAndrew Geissler             resp_handler, "xyz.openbmc_project.Logging",
1637cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging",
1638cb92c03bSAndrew Geissler             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1639cb92c03bSAndrew Geissler     }
1640cb92c03bSAndrew Geissler };
16411da66f75SEd Tanous } // namespace redfish
1642