xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision abf2add6a35aaf42ba60c4ae955a4d8925b13b19)
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 
18f6150403SJames Feist #include "filesystem.hpp"
191da66f75SEd Tanous #include "node.hpp"
201da66f75SEd Tanous 
21e1f26343SJason M. Bills #include <systemd/sd-journal.h>
22e1f26343SJason M. Bills 
231da66f75SEd Tanous #include <boost/container/flat_map.hpp>
24e1f26343SJason M. Bills #include <boost/utility/string_view.hpp>
25*abf2add6SEd 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";
324ed77cd5SEd Tanous constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate";
334ed77cd5SEd Tanous constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
344ed77cd5SEd Tanous constexpr char const *cpuLogImmediateInterface =
351da66f75SEd Tanous     "com.intel.CpuDebugLog.Immediate";
36e1f26343SJason M. Bills constexpr char const *cpuLogRawPECIInterface =
371da66f75SEd Tanous     "com.intel.CpuDebugLog.SendRawPeci";
381da66f75SEd Tanous 
39f6150403SJames Feist namespace fs = std::filesystem;
401da66f75SEd Tanous 
4116428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
4216428a1aSJason M. Bills                               const boost::string_view &field,
4316428a1aSJason M. Bills                               boost::string_view &contents)
4416428a1aSJason M. Bills {
4516428a1aSJason M. Bills     const char *data = nullptr;
4616428a1aSJason M. Bills     size_t length = 0;
4716428a1aSJason M. Bills     int ret = 0;
4816428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
4916428a1aSJason M. Bills     ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
5016428a1aSJason M. Bills                               &length);
5116428a1aSJason M. Bills     if (ret < 0)
5216428a1aSJason M. Bills     {
5316428a1aSJason M. Bills         return ret;
5416428a1aSJason M. Bills     }
5516428a1aSJason M. Bills     contents = boost::string_view(data, length);
5616428a1aSJason M. Bills     // Only use the content after the "=" character.
5716428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
5816428a1aSJason M. Bills     return ret;
5916428a1aSJason M. Bills }
6016428a1aSJason M. Bills 
6116428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
6216428a1aSJason M. Bills                               const boost::string_view &field, const int &base,
6316428a1aSJason M. Bills                               int &contents)
6416428a1aSJason M. Bills {
6516428a1aSJason M. Bills     int ret = 0;
6616428a1aSJason M. Bills     boost::string_view metadata;
6716428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
6816428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
6916428a1aSJason M. Bills     if (ret < 0)
7016428a1aSJason M. Bills     {
7116428a1aSJason M. Bills         return ret;
7216428a1aSJason M. Bills     }
7316428a1aSJason M. Bills     contents = strtol(metadata.data(), nullptr, base);
7416428a1aSJason M. Bills     return ret;
7516428a1aSJason M. Bills }
7616428a1aSJason M. Bills 
7716428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
7816428a1aSJason M. Bills {
7916428a1aSJason M. Bills     int ret = 0;
8016428a1aSJason M. Bills     uint64_t timestamp = 0;
8116428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
8216428a1aSJason M. Bills     if (ret < 0)
8316428a1aSJason M. Bills     {
8416428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
8516428a1aSJason M. Bills                          << strerror(-ret);
8616428a1aSJason M. Bills         return false;
8716428a1aSJason M. Bills     }
8816428a1aSJason M. Bills     time_t t =
8916428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
9016428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
9116428a1aSJason M. Bills     char entryTime[64] = {};
9216428a1aSJason M. Bills     if (NULL != loctime)
9316428a1aSJason M. Bills     {
9416428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
9516428a1aSJason M. Bills     }
9616428a1aSJason M. Bills     // Insert the ':' into the timezone
9716428a1aSJason M. Bills     boost::string_view t1(entryTime);
9816428a1aSJason M. Bills     boost::string_view t2(entryTime);
9916428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
10016428a1aSJason M. Bills     {
10116428a1aSJason M. Bills         t1.remove_suffix(2);
10216428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
10316428a1aSJason M. Bills     }
10416428a1aSJason M. Bills     entryTimestamp = t1.to_string() + ":" + t2.to_string();
10516428a1aSJason M. Bills     return true;
10616428a1aSJason M. Bills }
10716428a1aSJason M. Bills 
10816428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
10916428a1aSJason M. Bills                          long &skip)
11016428a1aSJason M. Bills {
11116428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
11216428a1aSJason M. Bills     if (skipParam != nullptr)
11316428a1aSJason M. Bills     {
11416428a1aSJason M. Bills         char *ptr = nullptr;
11516428a1aSJason M. Bills         skip = std::strtol(skipParam, &ptr, 10);
11616428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
11716428a1aSJason M. Bills         {
11816428a1aSJason M. Bills 
11916428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
12016428a1aSJason M. Bills                                                    "$skip");
12116428a1aSJason M. Bills             return false;
12216428a1aSJason M. Bills         }
12316428a1aSJason M. Bills         if (skip < 0)
12416428a1aSJason M. Bills         {
12516428a1aSJason M. Bills 
12616428a1aSJason M. Bills             messages::queryParameterOutOfRange(res, std::to_string(skip),
12716428a1aSJason M. Bills                                                "$skip", "greater than 0");
12816428a1aSJason M. Bills             return false;
12916428a1aSJason M. Bills         }
13016428a1aSJason M. Bills     }
13116428a1aSJason M. Bills     return true;
13216428a1aSJason M. Bills }
13316428a1aSJason M. Bills 
13416428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000;
13516428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
13616428a1aSJason M. Bills                         long &top)
13716428a1aSJason M. Bills {
13816428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
13916428a1aSJason M. Bills     if (topParam != nullptr)
14016428a1aSJason M. Bills     {
14116428a1aSJason M. Bills         char *ptr = nullptr;
14216428a1aSJason M. Bills         top = std::strtol(topParam, &ptr, 10);
14316428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
14416428a1aSJason M. Bills         {
14516428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
14616428a1aSJason M. Bills                                                    "$top");
14716428a1aSJason M. Bills             return false;
14816428a1aSJason M. Bills         }
14916428a1aSJason M. Bills         if (top < 1 || top > maxEntriesPerPage)
15016428a1aSJason M. Bills         {
15116428a1aSJason M. Bills 
15216428a1aSJason M. Bills             messages::queryParameterOutOfRange(
15316428a1aSJason M. Bills                 res, std::to_string(top), "$top",
15416428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
15516428a1aSJason M. Bills             return false;
15616428a1aSJason M. Bills         }
15716428a1aSJason M. Bills     }
15816428a1aSJason M. Bills     return true;
15916428a1aSJason M. Bills }
16016428a1aSJason M. Bills 
16116428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
16216428a1aSJason M. Bills {
16316428a1aSJason M. Bills     int ret = 0;
16416428a1aSJason M. Bills     static uint64_t prevTs = 0;
16516428a1aSJason M. Bills     static int index = 0;
16616428a1aSJason M. Bills     // Get the entry timestamp
16716428a1aSJason M. Bills     uint64_t curTs = 0;
16816428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &curTs);
16916428a1aSJason M. Bills     if (ret < 0)
17016428a1aSJason M. Bills     {
17116428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
17216428a1aSJason M. Bills                          << strerror(-ret);
17316428a1aSJason M. Bills         return false;
17416428a1aSJason M. Bills     }
17516428a1aSJason M. Bills     // If the timestamp isn't unique, increment the index
17616428a1aSJason M. Bills     if (curTs == prevTs)
17716428a1aSJason M. Bills     {
17816428a1aSJason M. Bills         index++;
17916428a1aSJason M. Bills     }
18016428a1aSJason M. Bills     else
18116428a1aSJason M. Bills     {
18216428a1aSJason M. Bills         // Otherwise, reset it
18316428a1aSJason M. Bills         index = 0;
18416428a1aSJason M. Bills     }
18516428a1aSJason M. Bills     // Save the timestamp
18616428a1aSJason M. Bills     prevTs = curTs;
18716428a1aSJason M. Bills 
18816428a1aSJason M. Bills     entryID = std::to_string(curTs);
18916428a1aSJason M. Bills     if (index > 0)
19016428a1aSJason M. Bills     {
19116428a1aSJason M. Bills         entryID += "_" + std::to_string(index);
19216428a1aSJason M. Bills     }
19316428a1aSJason M. Bills     return true;
19416428a1aSJason M. Bills }
19516428a1aSJason M. Bills 
19616428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
19716428a1aSJason M. Bills                                uint64_t &timestamp, uint16_t &index)
19816428a1aSJason M. Bills {
19916428a1aSJason M. Bills     if (entryID.empty())
20016428a1aSJason M. Bills     {
20116428a1aSJason M. Bills         return false;
20216428a1aSJason M. Bills     }
20316428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
20416428a1aSJason M. Bills     boost::string_view tsStr(entryID);
20516428a1aSJason M. Bills 
20616428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
20716428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
20816428a1aSJason M. Bills     {
20916428a1aSJason M. Bills         // Timestamp has an index
21016428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
21116428a1aSJason M. Bills         boost::string_view indexStr(entryID);
21216428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
21316428a1aSJason M. Bills         std::size_t pos;
21416428a1aSJason M. Bills         try
21516428a1aSJason M. Bills         {
21616428a1aSJason M. Bills             index = std::stoul(indexStr.to_string(), &pos);
21716428a1aSJason M. Bills         }
21816428a1aSJason M. Bills         catch (std::invalid_argument)
21916428a1aSJason M. Bills         {
22016428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
22116428a1aSJason M. Bills             return false;
22216428a1aSJason M. Bills         }
22316428a1aSJason M. Bills         catch (std::out_of_range)
22416428a1aSJason M. Bills         {
22516428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
22616428a1aSJason M. Bills             return false;
22716428a1aSJason M. Bills         }
22816428a1aSJason M. Bills         if (pos != indexStr.size())
22916428a1aSJason M. Bills         {
23016428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
23116428a1aSJason M. Bills             return false;
23216428a1aSJason M. Bills         }
23316428a1aSJason M. Bills     }
23416428a1aSJason M. Bills     // Timestamp has no index
23516428a1aSJason M. Bills     std::size_t pos;
23616428a1aSJason M. Bills     try
23716428a1aSJason M. Bills     {
23816428a1aSJason M. Bills         timestamp = std::stoull(tsStr.to_string(), &pos);
23916428a1aSJason M. Bills     }
24016428a1aSJason M. Bills     catch (std::invalid_argument)
24116428a1aSJason M. Bills     {
24216428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
24316428a1aSJason M. Bills         return false;
24416428a1aSJason M. Bills     }
24516428a1aSJason M. Bills     catch (std::out_of_range)
24616428a1aSJason M. Bills     {
24716428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
24816428a1aSJason M. Bills         return false;
24916428a1aSJason M. Bills     }
25016428a1aSJason M. Bills     if (pos != tsStr.size())
25116428a1aSJason M. Bills     {
25216428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
25316428a1aSJason M. Bills         return false;
25416428a1aSJason M. Bills     }
25516428a1aSJason M. Bills     return true;
25616428a1aSJason M. Bills }
25716428a1aSJason M. Bills 
258c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
2591da66f75SEd Tanous {
2601da66f75SEd Tanous   public:
2611da66f75SEd Tanous     template <typename CrowApp>
262c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
263c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/", std::string())
264c4bf6374SJason M. Bills     {
265c4bf6374SJason M. Bills         entityPrivileges = {
266c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
267c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
268c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
269c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
270c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
271c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
272c4bf6374SJason M. Bills     }
273c4bf6374SJason M. Bills 
274c4bf6374SJason M. Bills   private:
275c4bf6374SJason M. Bills     /**
276c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
277c4bf6374SJason M. Bills      */
278c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
279c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
280c4bf6374SJason M. Bills     {
281c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
282c4bf6374SJason M. Bills         const std::string &name = params[0];
283c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
284c4bf6374SJason M. Bills         // it has a duplicate entry for members
285c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
286c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
287c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
288c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
289c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
290c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices";
291c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
292c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
293c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
294c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
295c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
296c4bf6374SJason M. Bills         logServiceArray.push_back({{"@odata.id", "/redfish/v1/Systems/" + name +
297c4bf6374SJason M. Bills                                                      "/LogServices/EventLog"}});
298c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
299c4bf6374SJason M. Bills             logServiceArray.size();
300c4bf6374SJason M. Bills     }
301c4bf6374SJason M. Bills };
302c4bf6374SJason M. Bills 
303c4bf6374SJason M. Bills class EventLogService : public Node
304c4bf6374SJason M. Bills {
305c4bf6374SJason M. Bills   public:
306c4bf6374SJason M. Bills     template <typename CrowApp>
307c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
308c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/",
309c4bf6374SJason M. Bills              std::string())
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     void doGet(crow::Response &res, const crow::Request &req,
322c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
323c4bf6374SJason M. Bills     {
324c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
325c4bf6374SJason M. Bills 
326c4bf6374SJason M. Bills         const std::string &name = params[0];
327c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
328c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices/EventLog";
329c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
330c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
331c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
332c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
333c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
334c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
335c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
336c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
337c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
338c4bf6374SJason M. Bills             {"@odata.id",
339c4bf6374SJason M. Bills              "/redfish/v1/Systems/" + name + "/LogServices/EventLog/Entries"}};
340c4bf6374SJason M. Bills     }
341c4bf6374SJason M. Bills };
342c4bf6374SJason M. Bills 
343c4bf6374SJason M. Bills static int fillEventLogEntryJson(const std::string &systemName,
344c4bf6374SJason M. Bills                                  const std::string &bmcLogEntryID,
345c4bf6374SJason M. Bills                                  const boost::string_view &messageID,
346c4bf6374SJason M. Bills                                  sd_journal *journal,
347c4bf6374SJason M. Bills                                  nlohmann::json &bmcLogEntryJson)
348c4bf6374SJason M. Bills {
349c4bf6374SJason M. Bills     // Get the Log Entry contents
350c4bf6374SJason M. Bills     int ret = 0;
351c4bf6374SJason M. Bills 
352c4bf6374SJason M. Bills     boost::string_view msg;
353c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
354c4bf6374SJason M. Bills     if (ret < 0)
355c4bf6374SJason M. Bills     {
356c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
357c4bf6374SJason M. Bills         return 1;
358c4bf6374SJason M. Bills     }
359c4bf6374SJason M. Bills 
360c4bf6374SJason M. Bills     // Get the severity from the PRIORITY field
361c4bf6374SJason M. Bills     int severity = 8; // Default to an invalid priority
362c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
363c4bf6374SJason M. Bills     if (ret < 0)
364c4bf6374SJason M. Bills     {
365c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
366c4bf6374SJason M. Bills         return 1;
367c4bf6374SJason M. Bills     }
368c4bf6374SJason M. Bills 
369c4bf6374SJason M. Bills     // Get the MessageArgs from the journal entry by finding all of the
370c4bf6374SJason M. Bills     // REDFISH_MESSAGE_ARG_x fields
371c4bf6374SJason M. Bills     const void *data;
372c4bf6374SJason M. Bills     size_t length;
373c4bf6374SJason M. Bills     std::vector<std::string> messageArgs;
374c4bf6374SJason M. Bills     SD_JOURNAL_FOREACH_DATA(journal, data, length)
375c4bf6374SJason M. Bills     {
376c4bf6374SJason M. Bills         boost::string_view field(static_cast<const char *>(data), length);
377c4bf6374SJason M. Bills         if (field.starts_with("REDFISH_MESSAGE_ARG_"))
378c4bf6374SJason M. Bills         {
379c4bf6374SJason M. Bills             // Get the Arg number from the field name
380c4bf6374SJason M. Bills             field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
381c4bf6374SJason M. Bills             if (field.empty())
382c4bf6374SJason M. Bills             {
383c4bf6374SJason M. Bills                 continue;
384c4bf6374SJason M. Bills             }
385c4bf6374SJason M. Bills             int argNum = std::strtoul(field.data(), nullptr, 10);
386c4bf6374SJason M. Bills             if (argNum == 0)
387c4bf6374SJason M. Bills             {
388c4bf6374SJason M. Bills                 continue;
389c4bf6374SJason M. Bills             }
390c4bf6374SJason M. Bills             // Get the Arg value after the "=" character.
391c4bf6374SJason M. Bills             field.remove_prefix(std::min(field.find("=") + 1, field.size()));
392c4bf6374SJason M. Bills             // Make sure we have enough space in messageArgs
393c4bf6374SJason M. Bills             if (argNum > messageArgs.size())
394c4bf6374SJason M. Bills             {
395c4bf6374SJason M. Bills                 messageArgs.resize(argNum);
396c4bf6374SJason M. Bills             }
397c4bf6374SJason M. Bills             messageArgs[argNum - 1] = field.to_string();
398c4bf6374SJason M. Bills         }
399c4bf6374SJason M. Bills     }
400c4bf6374SJason M. Bills 
401c4bf6374SJason M. Bills     // Get the Created time from the timestamp
402c4bf6374SJason M. Bills     std::string entryTimeStr;
403c4bf6374SJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
404c4bf6374SJason M. Bills     {
405c4bf6374SJason M. Bills         return 1;
406c4bf6374SJason M. Bills     }
407c4bf6374SJason M. Bills 
408c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
409c4bf6374SJason M. Bills     bmcLogEntryJson = {
410c4bf6374SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
411c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
412c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Systems/" + systemName +
413c4bf6374SJason M. Bills                           "/LogServices/EventLog/Entries/" + bmcLogEntryID},
414c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
415c4bf6374SJason M. Bills         {"Id", bmcLogEntryID},
416c4bf6374SJason M. Bills         {"Message", msg},
417c4bf6374SJason M. Bills         {"MessageId", messageID},
418c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
419c4bf6374SJason M. Bills         {"EntryType", "Event"},
420c4bf6374SJason M. Bills         {"Severity",
421c4bf6374SJason M. Bills          severity <= 2 ? "Critical"
422c4bf6374SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
423c4bf6374SJason M. Bills         {"Created", std::move(entryTimeStr)}};
424c4bf6374SJason M. Bills     return 0;
425c4bf6374SJason M. Bills }
426c4bf6374SJason M. Bills 
427c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
428c4bf6374SJason M. Bills {
429c4bf6374SJason M. Bills   public:
430c4bf6374SJason M. Bills     template <typename CrowApp>
431c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
432c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/",
433c4bf6374SJason M. Bills              std::string())
434c4bf6374SJason M. Bills     {
435c4bf6374SJason M. Bills         entityPrivileges = {
436c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
437c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
438c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
439c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
440c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
441c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
442c4bf6374SJason M. Bills     }
443c4bf6374SJason M. Bills 
444c4bf6374SJason M. Bills   private:
445c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
446c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
447c4bf6374SJason M. Bills     {
448c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
449c4bf6374SJason M. Bills         long skip = 0;
450c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
451c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
452c4bf6374SJason M. Bills         {
453c4bf6374SJason M. Bills             return;
454c4bf6374SJason M. Bills         }
455c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
456c4bf6374SJason M. Bills         {
457c4bf6374SJason M. Bills             return;
458c4bf6374SJason M. Bills         }
459c4bf6374SJason M. Bills         const std::string &name = params[0];
460c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
461c4bf6374SJason M. Bills         // it has a duplicate entry for members
462c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
463c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
464c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
465c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
466c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
467c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices/EventLog/Entries";
468c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
469c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
470c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
471c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
472c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
473c4bf6374SJason M. Bills 
474c4bf6374SJason M. Bills         // Go through the journal and create a unique ID for each entry
475c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
476c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
477c4bf6374SJason M. Bills         if (ret < 0)
478c4bf6374SJason M. Bills         {
479c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
480c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
481c4bf6374SJason M. Bills             return;
482c4bf6374SJason M. Bills         }
483c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
484c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
485c4bf6374SJason M. Bills         journalTmp = nullptr;
486c4bf6374SJason M. Bills         uint64_t entryCount = 0;
487c4bf6374SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
488c4bf6374SJason M. Bills         {
489c4bf6374SJason M. Bills             // Look for only journal entries that contain a REDFISH_MESSAGE_ID
490c4bf6374SJason M. Bills             // field
491c4bf6374SJason M. Bills             boost::string_view messageID;
492c4bf6374SJason M. Bills             ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID",
493c4bf6374SJason M. Bills                                      messageID);
494c4bf6374SJason M. Bills             if (ret < 0)
495c4bf6374SJason M. Bills             {
496c4bf6374SJason M. Bills                 continue;
497c4bf6374SJason M. Bills             }
498c4bf6374SJason M. Bills 
499c4bf6374SJason M. Bills             entryCount++;
500c4bf6374SJason M. Bills             // Handle paging using skip (number of entries to skip from the
501c4bf6374SJason M. Bills             // start) and top (number of entries to display)
502c4bf6374SJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
503c4bf6374SJason M. Bills             {
504c4bf6374SJason M. Bills                 continue;
505c4bf6374SJason M. Bills             }
506c4bf6374SJason M. Bills 
507c4bf6374SJason M. Bills             std::string idStr;
508c4bf6374SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
509c4bf6374SJason M. Bills             {
510c4bf6374SJason M. Bills                 continue;
511c4bf6374SJason M. Bills             }
512c4bf6374SJason M. Bills 
513c4bf6374SJason M. Bills             logEntryArray.push_back({});
514c4bf6374SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
515c4bf6374SJason M. Bills             if (fillEventLogEntryJson(name, idStr, messageID, journal.get(),
516c4bf6374SJason M. Bills                                       bmcLogEntry) != 0)
517c4bf6374SJason M. Bills             {
518c4bf6374SJason M. Bills                 messages::internalError(asyncResp->res);
519c4bf6374SJason M. Bills                 return;
520c4bf6374SJason M. Bills             }
521c4bf6374SJason M. Bills         }
522c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
523c4bf6374SJason M. Bills         if (skip + top < entryCount)
524c4bf6374SJason M. Bills         {
525c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
526c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
527c4bf6374SJason M. Bills                 std::to_string(skip + top);
528c4bf6374SJason M. Bills         }
529c4bf6374SJason M. Bills     }
530c4bf6374SJason M. Bills };
531c4bf6374SJason M. Bills 
532c4bf6374SJason M. Bills class EventLogEntry : public Node
533c4bf6374SJason M. Bills {
534c4bf6374SJason M. Bills   public:
535c4bf6374SJason M. Bills     EventLogEntry(CrowApp &app) :
536c4bf6374SJason M. Bills         Node(app,
537c4bf6374SJason M. Bills              "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/",
538c4bf6374SJason M. Bills              std::string(), std::string())
539c4bf6374SJason M. Bills     {
540c4bf6374SJason M. Bills         entityPrivileges = {
541c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
542c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
543c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
544c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
545c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
546c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
547c4bf6374SJason M. Bills     }
548c4bf6374SJason M. Bills 
549c4bf6374SJason M. Bills   private:
550c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
551c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
552c4bf6374SJason M. Bills     {
553c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
554c4bf6374SJason M. Bills         if (params.size() != 2)
555c4bf6374SJason M. Bills         {
556c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
557c4bf6374SJason M. Bills             return;
558c4bf6374SJason M. Bills         }
559c4bf6374SJason M. Bills         const std::string &name = params[0];
560c4bf6374SJason M. Bills         const std::string &entryID = params[1];
561c4bf6374SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
562c4bf6374SJason M. Bills         uint64_t ts = 0;
563c4bf6374SJason M. Bills         uint16_t index = 0;
564c4bf6374SJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
565c4bf6374SJason M. Bills         {
566c4bf6374SJason M. Bills             return;
567c4bf6374SJason M. Bills         }
568c4bf6374SJason M. Bills 
569c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
570c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
571c4bf6374SJason M. Bills         if (ret < 0)
572c4bf6374SJason M. Bills         {
573c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
574c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
575c4bf6374SJason M. Bills             return;
576c4bf6374SJason M. Bills         }
577c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
578c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
579c4bf6374SJason M. Bills         journalTmp = nullptr;
580c4bf6374SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
581c4bf6374SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
582c4bf6374SJason M. Bills         for (int i = 0; i <= index; i++)
583c4bf6374SJason M. Bills         {
584c4bf6374SJason M. Bills             sd_journal_next(journal.get());
585c4bf6374SJason M. Bills         }
586c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
587c4bf6374SJason M. Bills         std::string idStr;
588c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
589c4bf6374SJason M. Bills         {
590c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
591c4bf6374SJason M. Bills             return;
592c4bf6374SJason M. Bills         }
593c4bf6374SJason M. Bills 
594c4bf6374SJason M. Bills         // only use journal entries that contain a REDFISH_MESSAGE_ID
595c4bf6374SJason M. Bills         // field
596c4bf6374SJason M. Bills         boost::string_view messageID;
597c4bf6374SJason M. Bills         ret =
598c4bf6374SJason M. Bills             getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
599c4bf6374SJason M. Bills         if (ret < 0)
600c4bf6374SJason M. Bills         {
601c4bf6374SJason M. Bills             messages::resourceNotFound(asyncResp->res, "LogEntry", name);
602c4bf6374SJason M. Bills             return;
603c4bf6374SJason M. Bills         }
604c4bf6374SJason M. Bills 
605c4bf6374SJason M. Bills         if (fillEventLogEntryJson(name, entryID, messageID, journal.get(),
606c4bf6374SJason M. Bills                                   asyncResp->res.jsonValue) != 0)
607c4bf6374SJason M. Bills         {
608c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
609c4bf6374SJason M. Bills             return;
610c4bf6374SJason M. Bills         }
611c4bf6374SJason M. Bills     }
612c4bf6374SJason M. Bills };
613c4bf6374SJason M. Bills 
614c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
615c4bf6374SJason M. Bills {
616c4bf6374SJason M. Bills   public:
617c4bf6374SJason M. Bills     template <typename CrowApp>
618c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
6194ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
6201da66f75SEd Tanous     {
6211da66f75SEd Tanous         entityPrivileges = {
622e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
623e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
624e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
625e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
626e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
627e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6281da66f75SEd Tanous     }
6291da66f75SEd Tanous 
6301da66f75SEd Tanous   private:
6311da66f75SEd Tanous     /**
6321da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
6331da66f75SEd Tanous      */
6341da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
6351da66f75SEd Tanous                const std::vector<std::string> &params) override
6361da66f75SEd Tanous     {
637e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6381da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
6391da66f75SEd Tanous         // it has a duplicate entry for members
640e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
6411da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
642e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
643c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
644e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
645e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
646e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
647e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
6481da66f75SEd Tanous             "Collection of LogServices for this Manager";
649c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
650c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
651c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
652c4bf6374SJason M. Bills         logServiceArray.push_back(
653c4bf6374SJason M. Bills             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
654c4bf6374SJason M. Bills #endif
6551da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
656c4bf6374SJason M. Bills         logServiceArray.push_back(
6574ed77cd5SEd Tanous             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
6581da66f75SEd Tanous #endif
659e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
660c4bf6374SJason M. Bills             logServiceArray.size();
6611da66f75SEd Tanous     }
6621da66f75SEd Tanous };
6631da66f75SEd Tanous 
664c4bf6374SJason M. Bills class BMCJournalLogService : public Node
6651da66f75SEd Tanous {
6661da66f75SEd Tanous   public:
6671da66f75SEd Tanous     template <typename CrowApp>
668c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
669c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
670e1f26343SJason M. Bills     {
671e1f26343SJason M. Bills         entityPrivileges = {
672e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
673e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
674e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
675e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
676e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
677e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
678e1f26343SJason M. Bills     }
679e1f26343SJason M. Bills 
680e1f26343SJason M. Bills   private:
681e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
682e1f26343SJason M. Bills                const std::vector<std::string> &params) override
683e1f26343SJason M. Bills     {
684e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
685e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
686e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
6870f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
6880f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
689e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
690e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
691c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
692c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
693c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
694e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
695e1f26343SJason M. Bills     }
696e1f26343SJason M. Bills };
697e1f26343SJason M. Bills 
698c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
699e1f26343SJason M. Bills                                       sd_journal *journal,
700c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
701e1f26343SJason M. Bills {
702e1f26343SJason M. Bills     // Get the Log Entry contents
703e1f26343SJason M. Bills     int ret = 0;
704e1f26343SJason M. Bills 
70516428a1aSJason M. Bills     boost::string_view msg;
70616428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
707e1f26343SJason M. Bills     if (ret < 0)
708e1f26343SJason M. Bills     {
709e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
710e1f26343SJason M. Bills         return 1;
711e1f26343SJason M. Bills     }
712e1f26343SJason M. Bills 
713e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
714e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
71516428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
716e1f26343SJason M. Bills     if (ret < 0)
717e1f26343SJason M. Bills     {
718e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
719e1f26343SJason M. Bills         return 1;
720e1f26343SJason M. Bills     }
721e1f26343SJason M. Bills 
722e1f26343SJason M. Bills     // Get the Created time from the timestamp
72316428a1aSJason M. Bills     std::string entryTimeStr;
72416428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
725e1f26343SJason M. Bills     {
72616428a1aSJason M. Bills         return 1;
727e1f26343SJason M. Bills     }
728e1f26343SJason M. Bills 
729e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
730c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
731e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
732e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
733c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
734c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
735e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
736c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
73716428a1aSJason M. Bills         {"Message", msg},
738e1f26343SJason M. Bills         {"EntryType", "Oem"},
739e1f26343SJason M. Bills         {"Severity",
740e1f26343SJason M. Bills          severity <= 2 ? "Critical"
741e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
742e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
743e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
744e1f26343SJason M. Bills     return 0;
745e1f26343SJason M. Bills }
746e1f26343SJason M. Bills 
747c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
748e1f26343SJason M. Bills {
749e1f26343SJason M. Bills   public:
750e1f26343SJason M. Bills     template <typename CrowApp>
751c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
752c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
753e1f26343SJason M. Bills     {
754e1f26343SJason M. Bills         entityPrivileges = {
755e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
756e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
757e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
758e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
759e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
760e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
761e1f26343SJason M. Bills     }
762e1f26343SJason M. Bills 
763e1f26343SJason M. Bills   private:
764e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
765e1f26343SJason M. Bills                const std::vector<std::string> &params) override
766e1f26343SJason M. Bills     {
767e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
768193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
769193ad2faSJason M. Bills         long skip = 0;
770193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
77116428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
772193ad2faSJason M. Bills         {
773193ad2faSJason M. Bills             return;
774193ad2faSJason M. Bills         }
77516428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
776193ad2faSJason M. Bills         {
777193ad2faSJason M. Bills             return;
778193ad2faSJason M. Bills         }
779e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
780e1f26343SJason M. Bills         // it has a duplicate entry for members
781e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
782e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
7830f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
7840f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
785e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
786c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
787e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
788c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
789e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
790e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
791e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
7920f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
7930f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
794e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
795e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
796e1f26343SJason M. Bills 
797e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
798e1f26343SJason M. Bills         // for each entry
799e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
800e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
801e1f26343SJason M. Bills         if (ret < 0)
802e1f26343SJason M. Bills         {
803e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
804f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
805e1f26343SJason M. Bills             return;
806e1f26343SJason M. Bills         }
807e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
808e1f26343SJason M. Bills             journalTmp, sd_journal_close);
809e1f26343SJason M. Bills         journalTmp = nullptr;
810193ad2faSJason M. Bills         uint64_t entryCount = 0;
811e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
812e1f26343SJason M. Bills         {
813193ad2faSJason M. Bills             entryCount++;
814193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
815193ad2faSJason M. Bills             // start) and top (number of entries to display)
816193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
817193ad2faSJason M. Bills             {
818193ad2faSJason M. Bills                 continue;
819193ad2faSJason M. Bills             }
820193ad2faSJason M. Bills 
82116428a1aSJason M. Bills             std::string idStr;
82216428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
823e1f26343SJason M. Bills             {
824e1f26343SJason M. Bills                 continue;
825e1f26343SJason M. Bills             }
826e1f26343SJason M. Bills 
827e1f26343SJason M. Bills             logEntryArray.push_back({});
828c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
829c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
830c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
831e1f26343SJason M. Bills             {
832f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
833e1f26343SJason M. Bills                 return;
834e1f26343SJason M. Bills             }
835e1f26343SJason M. Bills         }
836193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
837193ad2faSJason M. Bills         if (skip + top < entryCount)
838193ad2faSJason M. Bills         {
839193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
840c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
841193ad2faSJason M. Bills                 std::to_string(skip + top);
842193ad2faSJason M. Bills         }
843e1f26343SJason M. Bills     }
844e1f26343SJason M. Bills };
845e1f26343SJason M. Bills 
846c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
847e1f26343SJason M. Bills {
848e1f26343SJason M. Bills   public:
849c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
850c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
851e1f26343SJason M. Bills              std::string())
852e1f26343SJason M. Bills     {
853e1f26343SJason M. Bills         entityPrivileges = {
854e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
855e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
856e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
857e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
858e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
859e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
860e1f26343SJason M. Bills     }
861e1f26343SJason M. Bills 
862e1f26343SJason M. Bills   private:
863e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
864e1f26343SJason M. Bills                const std::vector<std::string> &params) override
865e1f26343SJason M. Bills     {
866e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
867e1f26343SJason M. Bills         if (params.size() != 1)
868e1f26343SJason M. Bills         {
869f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
870e1f26343SJason M. Bills             return;
871e1f26343SJason M. Bills         }
87216428a1aSJason M. Bills         const std::string &entryID = params[0];
873e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
874e1f26343SJason M. Bills         uint64_t ts = 0;
875e1f26343SJason M. Bills         uint16_t index = 0;
87616428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
877e1f26343SJason M. Bills         {
87816428a1aSJason M. Bills             return;
879e1f26343SJason M. Bills         }
880e1f26343SJason M. Bills 
881e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
882e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
883e1f26343SJason M. Bills         if (ret < 0)
884e1f26343SJason M. Bills         {
885e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
886f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
887e1f26343SJason M. Bills             return;
888e1f26343SJason M. Bills         }
889e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
890e1f26343SJason M. Bills             journalTmp, sd_journal_close);
891e1f26343SJason M. Bills         journalTmp = nullptr;
892e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
893e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
894e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
895e1f26343SJason M. Bills         {
896e1f26343SJason M. Bills             sd_journal_next(journal.get());
897e1f26343SJason M. Bills         }
898c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
899c4bf6374SJason M. Bills         std::string idStr;
900c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
901c4bf6374SJason M. Bills         {
902c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
903c4bf6374SJason M. Bills             return;
904c4bf6374SJason M. Bills         }
905c4bf6374SJason M. Bills 
906c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
907e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
908e1f26343SJason M. Bills         {
909f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
910e1f26343SJason M. Bills             return;
911e1f26343SJason M. Bills         }
912e1f26343SJason M. Bills     }
913e1f26343SJason M. Bills };
914e1f26343SJason M. Bills 
915e1f26343SJason M. Bills class CPULogService : public Node
916e1f26343SJason M. Bills {
917e1f26343SJason M. Bills   public:
918e1f26343SJason M. Bills     template <typename CrowApp>
919e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
920e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
9211da66f75SEd Tanous     {
9221da66f75SEd Tanous         entityPrivileges = {
923e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
924e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
925e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
926e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
927e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
928e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9291da66f75SEd Tanous     }
9301da66f75SEd Tanous 
9311da66f75SEd Tanous   private:
9321da66f75SEd Tanous     /**
9331da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9341da66f75SEd Tanous      */
9351da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9361da66f75SEd Tanous                const std::vector<std::string> &params) override
9371da66f75SEd Tanous     {
938e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9391da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
9400f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
9410f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog";
942e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
943e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
944e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
945c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
946e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
947e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
948e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
949e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
950e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
951e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
9521da66f75SEd Tanous             {"Oem",
9531da66f75SEd Tanous              {{"#CpuLog.Immediate",
954c4bf6374SJason M. Bills                {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/"
955c4bf6374SJason M. Bills                            "Actions/Oem/CpuLog.Immediate"}}}}}};
9561da66f75SEd Tanous 
9571da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
958e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
9591da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
960c4bf6374SJason M. Bills              {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/"
961c4bf6374SJason M. Bills                          "Oem/CpuLog.SendRawPeci"}}});
9621da66f75SEd Tanous #endif
9631da66f75SEd Tanous     }
9641da66f75SEd Tanous };
9651da66f75SEd Tanous 
966e1f26343SJason M. Bills class CPULogEntryCollection : public Node
9671da66f75SEd Tanous {
9681da66f75SEd Tanous   public:
9691da66f75SEd Tanous     template <typename CrowApp>
970e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
971e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
9721da66f75SEd Tanous     {
9731da66f75SEd Tanous         entityPrivileges = {
974e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
975e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
976e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
977e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
978e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
979e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9801da66f75SEd Tanous     }
9811da66f75SEd Tanous 
9821da66f75SEd Tanous   private:
9831da66f75SEd Tanous     /**
9841da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9851da66f75SEd Tanous      */
9861da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9871da66f75SEd Tanous                const std::vector<std::string> &params) override
9881da66f75SEd Tanous     {
989e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9901da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
9911da66f75SEd Tanous         // it has a duplicate entry for members
992e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
993e1f26343SJason M. Bills                                          const boost::system::error_code ec,
9941da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
9951da66f75SEd Tanous             if (ec)
9961da66f75SEd Tanous             {
9971da66f75SEd Tanous                 if (ec.value() !=
9981da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
9991da66f75SEd Tanous                 {
10001da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
10011da66f75SEd Tanous                                      << ec.message();
1002f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
10031da66f75SEd Tanous                     return;
10041da66f75SEd Tanous                 }
10051da66f75SEd Tanous             }
1006e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
10071da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
10080f74e643SEd Tanous             asyncResp->res.jsonValue["@odata.id"] =
10090f74e643SEd Tanous                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
1010e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1011c4bf6374SJason M. Bills                 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1012e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.id"] =
1013e1f26343SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
1014e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
1015e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1016e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
1017e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1018e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
10191da66f75SEd Tanous             for (const std::string &objpath : resp)
10201da66f75SEd Tanous             {
10211da66f75SEd Tanous                 // Don't list the immediate log
10224ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
10231da66f75SEd Tanous                 {
10241da66f75SEd Tanous                     continue;
10251da66f75SEd Tanous                 }
10264ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
10274ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
10281da66f75SEd Tanous                 {
1029e1f26343SJason M. Bills                     logEntryArray.push_back(
1030e1f26343SJason M. Bills                         {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
1031e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
10324ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
10331da66f75SEd Tanous                 }
10341da66f75SEd Tanous             }
1035e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1036e1f26343SJason M. Bills                 logEntryArray.size();
10371da66f75SEd Tanous         };
10381da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
10391da66f75SEd Tanous             std::move(getLogEntriesCallback),
10401da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
10411da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
10421da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
10434ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
10441da66f75SEd Tanous     }
10451da66f75SEd Tanous };
10461da66f75SEd Tanous 
10471da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
10481da66f75SEd Tanous {
10491da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
10501da66f75SEd Tanous     if (metaIt != cpuLog.end())
10511da66f75SEd Tanous     {
10521da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
10531da66f75SEd Tanous         if (tsIt != metaIt->end())
10541da66f75SEd Tanous         {
10551da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
10561da66f75SEd Tanous             if (logTime != nullptr)
10571da66f75SEd Tanous             {
10581da66f75SEd Tanous                 return *logTime;
10591da66f75SEd Tanous             }
10601da66f75SEd Tanous         }
10611da66f75SEd Tanous     }
10621da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
10631da66f75SEd Tanous 
10641da66f75SEd Tanous     return std::string();
10651da66f75SEd Tanous }
10661da66f75SEd Tanous 
1067e1f26343SJason M. Bills class CPULogEntry : public Node
10681da66f75SEd Tanous {
10691da66f75SEd Tanous   public:
1070e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
10714ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
10721da66f75SEd Tanous              std::string())
10731da66f75SEd Tanous     {
10741da66f75SEd Tanous         entityPrivileges = {
1075e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1076e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1077e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1078e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1079e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1080e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
10811da66f75SEd Tanous     }
10821da66f75SEd Tanous 
10831da66f75SEd Tanous   private:
10841da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
10851da66f75SEd Tanous                const std::vector<std::string> &params) override
10861da66f75SEd Tanous     {
1087e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
10881da66f75SEd Tanous         if (params.size() != 1)
10891da66f75SEd Tanous         {
1090f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
10911da66f75SEd Tanous             return;
10921da66f75SEd Tanous         }
10934ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1094*abf2add6SEd Tanous         auto getStoredLogCallback = [asyncResp, logId](
1095*abf2add6SEd Tanous                                         const boost::system::error_code ec,
1096*abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
10971da66f75SEd Tanous             if (ec)
10981da66f75SEd Tanous             {
1099*abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1100f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11011da66f75SEd Tanous                 return;
11021da66f75SEd Tanous             }
1103*abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
11041da66f75SEd Tanous             if (log == nullptr)
11051da66f75SEd Tanous             {
1106f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11071da66f75SEd Tanous                 return;
11081da66f75SEd Tanous             }
11091da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
11101da66f75SEd Tanous             if (j.is_discarded())
11111da66f75SEd Tanous             {
1112f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11131da66f75SEd Tanous                 return;
11141da66f75SEd Tanous             }
11151da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1116e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
11171da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
1118*abf2add6SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
11191da66f75SEd Tanous                 {"@odata.id",
11204ed77cd5SEd Tanous                  "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
11214ed77cd5SEd Tanous                      std::to_string(logId)},
11221da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
11234ed77cd5SEd Tanous                 {"Id", logId},
11241da66f75SEd Tanous                 {"EntryType", "Oem"},
11251da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
11261da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
11271da66f75SEd Tanous                 {"Created", std::move(t)}};
11281da66f75SEd Tanous         };
11291da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
11304ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
11314ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
11324ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
11331da66f75SEd Tanous     }
11341da66f75SEd Tanous };
11351da66f75SEd Tanous 
1136e1f26343SJason M. Bills class ImmediateCPULog : public Node
11371da66f75SEd Tanous {
11381da66f75SEd Tanous   public:
1139e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
11404ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
1141e1f26343SJason M. Bills                   "CpuLog.Immediate/")
11421da66f75SEd Tanous     {
11431da66f75SEd Tanous         entityPrivileges = {
1144e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1145e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1146e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1147e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1148e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1149e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
11501da66f75SEd Tanous     }
11511da66f75SEd Tanous 
11521da66f75SEd Tanous   private:
11531da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
11541da66f75SEd Tanous                 const std::vector<std::string> &params) override
11551da66f75SEd Tanous     {
1156e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
11571da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
11581da66f75SEd Tanous             immediateLogMatcher;
11591da66f75SEd Tanous 
11601da66f75SEd Tanous         // Only allow one Immediate Log request at a time
11611da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
11621da66f75SEd Tanous         {
1163e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1164f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
11651da66f75SEd Tanous             return;
11661da66f75SEd Tanous         }
11671da66f75SEd Tanous         // Make this static so it survives outside this method
11681da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
11691da66f75SEd Tanous 
11701da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1171e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
11721da66f75SEd Tanous             immediateLogMatcher = nullptr;
11731da66f75SEd Tanous             if (ec)
11741da66f75SEd Tanous             {
11751da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
11761da66f75SEd Tanous                 // completion.
11771da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
11781da66f75SEd Tanous                 {
11791da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
11801da66f75SEd Tanous                 }
11811da66f75SEd Tanous                 return;
11821da66f75SEd Tanous             }
11831da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
11841da66f75SEd Tanous 
1185f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
11861da66f75SEd Tanous         });
11871da66f75SEd Tanous 
1188e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
11891da66f75SEd Tanous                                                sdbusplus::message::message &m) {
11901da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
11911da66f75SEd Tanous             boost::system::error_code ec;
11921da66f75SEd Tanous             timeout.cancel(ec);
11931da66f75SEd Tanous             if (ec)
11941da66f75SEd Tanous             {
11951da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
11961da66f75SEd Tanous             }
11974ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
11981da66f75SEd Tanous             boost::container::flat_map<
1199*abf2add6SEd Tanous                 std::string, boost::container::flat_map<
1200*abf2add6SEd Tanous                                  std::string, std::variant<std::string>>>
12014ed77cd5SEd Tanous                 interfacesAdded;
12024ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1203*abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(
12041b6b96c5SEd Tanous                 &interfacesAdded[cpuLogInterface]["Log"]);
12051da66f75SEd Tanous             if (log == nullptr)
12061da66f75SEd Tanous             {
1207f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12081da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
12091da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
12101da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
12111da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
12121da66f75SEd Tanous                 // be the last thing done.
12131da66f75SEd Tanous                 immediateLogMatcher = nullptr;
12141da66f75SEd Tanous                 return;
12151da66f75SEd Tanous             }
12161da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
12171da66f75SEd Tanous             if (j.is_discarded())
12181da66f75SEd Tanous             {
1219f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12201da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
12211da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
12221da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
12231da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
12241da66f75SEd Tanous                 // be the last thing done.
12251da66f75SEd Tanous                 immediateLogMatcher = nullptr;
12261da66f75SEd Tanous                 return;
12271da66f75SEd Tanous             }
12281da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1229e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
12301da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
12311da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
12321da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
12331da66f75SEd Tanous                 {"EntryType", "Oem"},
12341da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
12351da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
12361da66f75SEd Tanous                 {"Created", std::move(t)}};
12371da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
12381da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
12391da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
12401da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
12411da66f75SEd Tanous             // thing done.
12421da66f75SEd Tanous             immediateLogMatcher = nullptr;
12431da66f75SEd Tanous         };
12441da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
12451da66f75SEd Tanous             *crow::connections::systemBus,
12461da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
12474ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
12481da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
12491da66f75SEd Tanous 
12501da66f75SEd Tanous         auto generateImmediateLogCallback =
1251e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
12521da66f75SEd Tanous                         const std::string &resp) {
12531da66f75SEd Tanous                 if (ec)
12541da66f75SEd Tanous                 {
12551da66f75SEd Tanous                     if (ec.value() ==
12561da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
12571da66f75SEd Tanous                     {
1258f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
12591da66f75SEd Tanous                     }
12601da66f75SEd Tanous                     else
12611da66f75SEd Tanous                     {
1262f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
12631da66f75SEd Tanous                     }
12641da66f75SEd Tanous                     boost::system::error_code timeoutec;
12651da66f75SEd Tanous                     timeout.cancel(timeoutec);
12661da66f75SEd Tanous                     if (timeoutec)
12671da66f75SEd Tanous                     {
12681da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
12691da66f75SEd Tanous                                          << timeoutec;
12701da66f75SEd Tanous                     }
12711da66f75SEd Tanous                     immediateLogMatcher = nullptr;
12721da66f75SEd Tanous                     return;
12731da66f75SEd Tanous                 }
12741da66f75SEd Tanous             };
12751da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
12764ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
12774ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
12781da66f75SEd Tanous     }
12791da66f75SEd Tanous };
12801da66f75SEd Tanous 
1281e1f26343SJason M. Bills class SendRawPECI : public Node
12821da66f75SEd Tanous {
12831da66f75SEd Tanous   public:
1284e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
12854ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
1286e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
12871da66f75SEd Tanous     {
12881da66f75SEd Tanous         entityPrivileges = {
12891da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
12901da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
12911da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
12921da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
12931da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
12941da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
12951da66f75SEd Tanous     }
12961da66f75SEd Tanous 
12971da66f75SEd Tanous   private:
12981da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
12991da66f75SEd Tanous                 const std::vector<std::string> &params) override
13001da66f75SEd Tanous     {
1301e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1302b1556427SEd Tanous         uint8_t clientAddress = 0;
1303b1556427SEd Tanous         uint8_t readLength = 0;
13041da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
1305b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1306b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
1307b1556427SEd Tanous                                  peciCommand))
13081da66f75SEd Tanous         {
13091da66f75SEd Tanous             return;
13101da66f75SEd Tanous         }
1311b1556427SEd Tanous 
13121da66f75SEd Tanous         // Callback to return the Raw PECI response
1313e1f26343SJason M. Bills         auto sendRawPECICallback =
1314e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
13151da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
13161da66f75SEd Tanous                 if (ec)
13171da66f75SEd Tanous                 {
13181da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
13191da66f75SEd Tanous                                      << ec.message();
1320f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
13211da66f75SEd Tanous                     return;
13221da66f75SEd Tanous                 }
1323e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
13241da66f75SEd Tanous                                             {"PECIResponse", resp}};
13251da66f75SEd Tanous             };
13261da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
13271da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1328e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
1329e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
13304ed77cd5SEd Tanous             peciCommand);
13311da66f75SEd Tanous     }
13321da66f75SEd Tanous };
13331da66f75SEd Tanous 
13341da66f75SEd Tanous } // namespace redfish
1335