xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 1b6b96c570fdf7ca5c643f3d0776581656e78070)
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>
23e1f26343SJason M. Bills #include <boost/utility/string_view.hpp>
241da66f75SEd Tanous #include <experimental/filesystem>
251da66f75SEd Tanous 
261da66f75SEd Tanous namespace redfish
271da66f75SEd Tanous {
281da66f75SEd Tanous 
294ed77cd5SEd Tanous constexpr char const *cpuLogObject = "com.intel.CpuDebugLog";
304ed77cd5SEd Tanous constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog";
314ed77cd5SEd Tanous constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate";
324ed77cd5SEd Tanous constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
334ed77cd5SEd Tanous constexpr char const *cpuLogImmediateInterface =
341da66f75SEd Tanous     "com.intel.CpuDebugLog.Immediate";
35e1f26343SJason M. Bills constexpr char const *cpuLogRawPECIInterface =
361da66f75SEd Tanous     "com.intel.CpuDebugLog.SendRawPeci";
371da66f75SEd Tanous 
381da66f75SEd Tanous namespace fs = std::experimental::filesystem;
391da66f75SEd Tanous 
4016428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
4116428a1aSJason M. Bills                               const boost::string_view &field,
4216428a1aSJason M. Bills                               boost::string_view &contents)
4316428a1aSJason M. Bills {
4416428a1aSJason M. Bills     const char *data = nullptr;
4516428a1aSJason M. Bills     size_t length = 0;
4616428a1aSJason M. Bills     int ret = 0;
4716428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
4816428a1aSJason M. Bills     ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
4916428a1aSJason M. Bills                               &length);
5016428a1aSJason M. Bills     if (ret < 0)
5116428a1aSJason M. Bills     {
5216428a1aSJason M. Bills         return ret;
5316428a1aSJason M. Bills     }
5416428a1aSJason M. Bills     contents = boost::string_view(data, length);
5516428a1aSJason M. Bills     // Only use the content after the "=" character.
5616428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
5716428a1aSJason M. Bills     return ret;
5816428a1aSJason M. Bills }
5916428a1aSJason M. Bills 
6016428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
6116428a1aSJason M. Bills                               const boost::string_view &field, const int &base,
6216428a1aSJason M. Bills                               int &contents)
6316428a1aSJason M. Bills {
6416428a1aSJason M. Bills     int ret = 0;
6516428a1aSJason M. Bills     boost::string_view metadata;
6616428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
6716428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
6816428a1aSJason M. Bills     if (ret < 0)
6916428a1aSJason M. Bills     {
7016428a1aSJason M. Bills         return ret;
7116428a1aSJason M. Bills     }
7216428a1aSJason M. Bills     contents = strtol(metadata.data(), nullptr, base);
7316428a1aSJason M. Bills     return ret;
7416428a1aSJason M. Bills }
7516428a1aSJason M. Bills 
7616428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
7716428a1aSJason M. Bills {
7816428a1aSJason M. Bills     int ret = 0;
7916428a1aSJason M. Bills     uint64_t timestamp = 0;
8016428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
8116428a1aSJason M. Bills     if (ret < 0)
8216428a1aSJason M. Bills     {
8316428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
8416428a1aSJason M. Bills                          << strerror(-ret);
8516428a1aSJason M. Bills         return false;
8616428a1aSJason M. Bills     }
8716428a1aSJason M. Bills     time_t t =
8816428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
8916428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
9016428a1aSJason M. Bills     char entryTime[64] = {};
9116428a1aSJason M. Bills     if (NULL != loctime)
9216428a1aSJason M. Bills     {
9316428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
9416428a1aSJason M. Bills     }
9516428a1aSJason M. Bills     // Insert the ':' into the timezone
9616428a1aSJason M. Bills     boost::string_view t1(entryTime);
9716428a1aSJason M. Bills     boost::string_view t2(entryTime);
9816428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
9916428a1aSJason M. Bills     {
10016428a1aSJason M. Bills         t1.remove_suffix(2);
10116428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
10216428a1aSJason M. Bills     }
10316428a1aSJason M. Bills     entryTimestamp = t1.to_string() + ":" + t2.to_string();
10416428a1aSJason M. Bills     return true;
10516428a1aSJason M. Bills }
10616428a1aSJason M. Bills 
10716428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
10816428a1aSJason M. Bills                          long &skip)
10916428a1aSJason M. Bills {
11016428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
11116428a1aSJason M. Bills     if (skipParam != nullptr)
11216428a1aSJason M. Bills     {
11316428a1aSJason M. Bills         char *ptr = nullptr;
11416428a1aSJason M. Bills         skip = std::strtol(skipParam, &ptr, 10);
11516428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
11616428a1aSJason M. Bills         {
11716428a1aSJason M. Bills 
11816428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
11916428a1aSJason M. Bills                                                    "$skip");
12016428a1aSJason M. Bills             return false;
12116428a1aSJason M. Bills         }
12216428a1aSJason M. Bills         if (skip < 0)
12316428a1aSJason M. Bills         {
12416428a1aSJason M. Bills 
12516428a1aSJason M. Bills             messages::queryParameterOutOfRange(res, std::to_string(skip),
12616428a1aSJason M. Bills                                                "$skip", "greater than 0");
12716428a1aSJason M. Bills             return false;
12816428a1aSJason M. Bills         }
12916428a1aSJason M. Bills     }
13016428a1aSJason M. Bills     return true;
13116428a1aSJason M. Bills }
13216428a1aSJason M. Bills 
13316428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000;
13416428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
13516428a1aSJason M. Bills                         long &top)
13616428a1aSJason M. Bills {
13716428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
13816428a1aSJason M. Bills     if (topParam != nullptr)
13916428a1aSJason M. Bills     {
14016428a1aSJason M. Bills         char *ptr = nullptr;
14116428a1aSJason M. Bills         top = std::strtol(topParam, &ptr, 10);
14216428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
14316428a1aSJason M. Bills         {
14416428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
14516428a1aSJason M. Bills                                                    "$top");
14616428a1aSJason M. Bills             return false;
14716428a1aSJason M. Bills         }
14816428a1aSJason M. Bills         if (top < 1 || top > maxEntriesPerPage)
14916428a1aSJason M. Bills         {
15016428a1aSJason M. Bills 
15116428a1aSJason M. Bills             messages::queryParameterOutOfRange(
15216428a1aSJason M. Bills                 res, std::to_string(top), "$top",
15316428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
15416428a1aSJason M. Bills             return false;
15516428a1aSJason M. Bills         }
15616428a1aSJason M. Bills     }
15716428a1aSJason M. Bills     return true;
15816428a1aSJason M. Bills }
15916428a1aSJason M. Bills 
16016428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
16116428a1aSJason M. Bills {
16216428a1aSJason M. Bills     int ret = 0;
16316428a1aSJason M. Bills     static uint64_t prevTs = 0;
16416428a1aSJason M. Bills     static int index = 0;
16516428a1aSJason M. Bills     // Get the entry timestamp
16616428a1aSJason M. Bills     uint64_t curTs = 0;
16716428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &curTs);
16816428a1aSJason M. Bills     if (ret < 0)
16916428a1aSJason M. Bills     {
17016428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
17116428a1aSJason M. Bills                          << strerror(-ret);
17216428a1aSJason M. Bills         return false;
17316428a1aSJason M. Bills     }
17416428a1aSJason M. Bills     // If the timestamp isn't unique, increment the index
17516428a1aSJason M. Bills     if (curTs == prevTs)
17616428a1aSJason M. Bills     {
17716428a1aSJason M. Bills         index++;
17816428a1aSJason M. Bills     }
17916428a1aSJason M. Bills     else
18016428a1aSJason M. Bills     {
18116428a1aSJason M. Bills         // Otherwise, reset it
18216428a1aSJason M. Bills         index = 0;
18316428a1aSJason M. Bills     }
18416428a1aSJason M. Bills     // Save the timestamp
18516428a1aSJason M. Bills     prevTs = curTs;
18616428a1aSJason M. Bills 
18716428a1aSJason M. Bills     entryID = std::to_string(curTs);
18816428a1aSJason M. Bills     if (index > 0)
18916428a1aSJason M. Bills     {
19016428a1aSJason M. Bills         entryID += "_" + std::to_string(index);
19116428a1aSJason M. Bills     }
19216428a1aSJason M. Bills     return true;
19316428a1aSJason M. Bills }
19416428a1aSJason M. Bills 
19516428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
19616428a1aSJason M. Bills                                uint64_t &timestamp, uint16_t &index)
19716428a1aSJason M. Bills {
19816428a1aSJason M. Bills     if (entryID.empty())
19916428a1aSJason M. Bills     {
20016428a1aSJason M. Bills         return false;
20116428a1aSJason M. Bills     }
20216428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
20316428a1aSJason M. Bills     boost::string_view tsStr(entryID);
20416428a1aSJason M. Bills 
20516428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
20616428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
20716428a1aSJason M. Bills     {
20816428a1aSJason M. Bills         // Timestamp has an index
20916428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
21016428a1aSJason M. Bills         boost::string_view indexStr(entryID);
21116428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
21216428a1aSJason M. Bills         std::size_t pos;
21316428a1aSJason M. Bills         try
21416428a1aSJason M. Bills         {
21516428a1aSJason M. Bills             index = std::stoul(indexStr.to_string(), &pos);
21616428a1aSJason M. Bills         }
21716428a1aSJason M. Bills         catch (std::invalid_argument)
21816428a1aSJason M. Bills         {
21916428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
22016428a1aSJason M. Bills             return false;
22116428a1aSJason M. Bills         }
22216428a1aSJason M. Bills         catch (std::out_of_range)
22316428a1aSJason M. Bills         {
22416428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
22516428a1aSJason M. Bills             return false;
22616428a1aSJason M. Bills         }
22716428a1aSJason M. Bills         if (pos != indexStr.size())
22816428a1aSJason M. Bills         {
22916428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
23016428a1aSJason M. Bills             return false;
23116428a1aSJason M. Bills         }
23216428a1aSJason M. Bills     }
23316428a1aSJason M. Bills     // Timestamp has no index
23416428a1aSJason M. Bills     std::size_t pos;
23516428a1aSJason M. Bills     try
23616428a1aSJason M. Bills     {
23716428a1aSJason M. Bills         timestamp = std::stoull(tsStr.to_string(), &pos);
23816428a1aSJason M. Bills     }
23916428a1aSJason M. Bills     catch (std::invalid_argument)
24016428a1aSJason M. Bills     {
24116428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
24216428a1aSJason M. Bills         return false;
24316428a1aSJason M. Bills     }
24416428a1aSJason M. Bills     catch (std::out_of_range)
24516428a1aSJason M. Bills     {
24616428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
24716428a1aSJason M. Bills         return false;
24816428a1aSJason M. Bills     }
24916428a1aSJason M. Bills     if (pos != tsStr.size())
25016428a1aSJason M. Bills     {
25116428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
25216428a1aSJason M. Bills         return false;
25316428a1aSJason M. Bills     }
25416428a1aSJason M. Bills     return true;
25516428a1aSJason M. Bills }
25616428a1aSJason M. Bills 
257c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
2581da66f75SEd Tanous {
2591da66f75SEd Tanous   public:
2601da66f75SEd Tanous     template <typename CrowApp>
261c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
262c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/", std::string())
263c4bf6374SJason M. Bills     {
264c4bf6374SJason M. Bills         entityPrivileges = {
265c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
266c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
267c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
268c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
269c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
270c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
271c4bf6374SJason M. Bills     }
272c4bf6374SJason M. Bills 
273c4bf6374SJason M. Bills   private:
274c4bf6374SJason M. Bills     /**
275c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
276c4bf6374SJason M. Bills      */
277c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
278c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
279c4bf6374SJason M. Bills     {
280c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
281c4bf6374SJason M. Bills         const std::string &name = params[0];
282c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
283c4bf6374SJason M. Bills         // it has a duplicate entry for members
284c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
285c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
286c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
287c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
288c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
289c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices";
290c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
291c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
292c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
293c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
294c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
295c4bf6374SJason M. Bills         logServiceArray.push_back({{"@odata.id", "/redfish/v1/Systems/" + name +
296c4bf6374SJason M. Bills                                                      "/LogServices/EventLog"}});
297c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
298c4bf6374SJason M. Bills             logServiceArray.size();
299c4bf6374SJason M. Bills     }
300c4bf6374SJason M. Bills };
301c4bf6374SJason M. Bills 
302c4bf6374SJason M. Bills class EventLogService : public Node
303c4bf6374SJason M. Bills {
304c4bf6374SJason M. Bills   public:
305c4bf6374SJason M. Bills     template <typename CrowApp>
306c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
307c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/",
308c4bf6374SJason M. Bills              std::string())
309c4bf6374SJason M. Bills     {
310c4bf6374SJason M. Bills         entityPrivileges = {
311c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
312c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
313c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
314c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
315c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
316c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
317c4bf6374SJason M. Bills     }
318c4bf6374SJason M. Bills 
319c4bf6374SJason M. Bills   private:
320c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
321c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
322c4bf6374SJason M. Bills     {
323c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
324c4bf6374SJason M. Bills 
325c4bf6374SJason M. Bills         const std::string &name = params[0];
326c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
327c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices/EventLog";
328c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
329c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
330c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
331c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
332c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
333c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
334c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
335c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
336c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
337c4bf6374SJason M. Bills             {"@odata.id",
338c4bf6374SJason M. Bills              "/redfish/v1/Systems/" + name + "/LogServices/EventLog/Entries"}};
339c4bf6374SJason M. Bills     }
340c4bf6374SJason M. Bills };
341c4bf6374SJason M. Bills 
342c4bf6374SJason M. Bills static int fillEventLogEntryJson(const std::string &systemName,
343c4bf6374SJason M. Bills                                  const std::string &bmcLogEntryID,
344c4bf6374SJason M. Bills                                  const boost::string_view &messageID,
345c4bf6374SJason M. Bills                                  sd_journal *journal,
346c4bf6374SJason M. Bills                                  nlohmann::json &bmcLogEntryJson)
347c4bf6374SJason M. Bills {
348c4bf6374SJason M. Bills     // Get the Log Entry contents
349c4bf6374SJason M. Bills     int ret = 0;
350c4bf6374SJason M. Bills 
351c4bf6374SJason M. Bills     boost::string_view msg;
352c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
353c4bf6374SJason M. Bills     if (ret < 0)
354c4bf6374SJason M. Bills     {
355c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
356c4bf6374SJason M. Bills         return 1;
357c4bf6374SJason M. Bills     }
358c4bf6374SJason M. Bills 
359c4bf6374SJason M. Bills     // Get the severity from the PRIORITY field
360c4bf6374SJason M. Bills     int severity = 8; // Default to an invalid priority
361c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
362c4bf6374SJason M. Bills     if (ret < 0)
363c4bf6374SJason M. Bills     {
364c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
365c4bf6374SJason M. Bills         return 1;
366c4bf6374SJason M. Bills     }
367c4bf6374SJason M. Bills 
368c4bf6374SJason M. Bills     // Get the MessageArgs from the journal entry by finding all of the
369c4bf6374SJason M. Bills     // REDFISH_MESSAGE_ARG_x fields
370c4bf6374SJason M. Bills     const void *data;
371c4bf6374SJason M. Bills     size_t length;
372c4bf6374SJason M. Bills     std::vector<std::string> messageArgs;
373c4bf6374SJason M. Bills     SD_JOURNAL_FOREACH_DATA(journal, data, length)
374c4bf6374SJason M. Bills     {
375c4bf6374SJason M. Bills         boost::string_view field(static_cast<const char *>(data), length);
376c4bf6374SJason M. Bills         if (field.starts_with("REDFISH_MESSAGE_ARG_"))
377c4bf6374SJason M. Bills         {
378c4bf6374SJason M. Bills             // Get the Arg number from the field name
379c4bf6374SJason M. Bills             field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
380c4bf6374SJason M. Bills             if (field.empty())
381c4bf6374SJason M. Bills             {
382c4bf6374SJason M. Bills                 continue;
383c4bf6374SJason M. Bills             }
384c4bf6374SJason M. Bills             int argNum = std::strtoul(field.data(), nullptr, 10);
385c4bf6374SJason M. Bills             if (argNum == 0)
386c4bf6374SJason M. Bills             {
387c4bf6374SJason M. Bills                 continue;
388c4bf6374SJason M. Bills             }
389c4bf6374SJason M. Bills             // Get the Arg value after the "=" character.
390c4bf6374SJason M. Bills             field.remove_prefix(std::min(field.find("=") + 1, field.size()));
391c4bf6374SJason M. Bills             // Make sure we have enough space in messageArgs
392c4bf6374SJason M. Bills             if (argNum > messageArgs.size())
393c4bf6374SJason M. Bills             {
394c4bf6374SJason M. Bills                 messageArgs.resize(argNum);
395c4bf6374SJason M. Bills             }
396c4bf6374SJason M. Bills             messageArgs[argNum - 1] = field.to_string();
397c4bf6374SJason M. Bills         }
398c4bf6374SJason M. Bills     }
399c4bf6374SJason M. Bills 
400c4bf6374SJason M. Bills     // Get the Created time from the timestamp
401c4bf6374SJason M. Bills     std::string entryTimeStr;
402c4bf6374SJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
403c4bf6374SJason M. Bills     {
404c4bf6374SJason M. Bills         return 1;
405c4bf6374SJason M. Bills     }
406c4bf6374SJason M. Bills 
407c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
408c4bf6374SJason M. Bills     bmcLogEntryJson = {
409c4bf6374SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
410c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
411c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Systems/" + systemName +
412c4bf6374SJason M. Bills                           "/LogServices/EventLog/Entries/" + bmcLogEntryID},
413c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
414c4bf6374SJason M. Bills         {"Id", bmcLogEntryID},
415c4bf6374SJason M. Bills         {"Message", msg},
416c4bf6374SJason M. Bills         {"MessageId", messageID},
417c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
418c4bf6374SJason M. Bills         {"EntryType", "Event"},
419c4bf6374SJason M. Bills         {"Severity",
420c4bf6374SJason M. Bills          severity <= 2 ? "Critical"
421c4bf6374SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
422c4bf6374SJason M. Bills         {"Created", std::move(entryTimeStr)}};
423c4bf6374SJason M. Bills     return 0;
424c4bf6374SJason M. Bills }
425c4bf6374SJason M. Bills 
426c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
427c4bf6374SJason M. Bills {
428c4bf6374SJason M. Bills   public:
429c4bf6374SJason M. Bills     template <typename CrowApp>
430c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
431c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/",
432c4bf6374SJason M. Bills              std::string())
433c4bf6374SJason M. Bills     {
434c4bf6374SJason M. Bills         entityPrivileges = {
435c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
436c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
437c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
438c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
439c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
440c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
441c4bf6374SJason M. Bills     }
442c4bf6374SJason M. Bills 
443c4bf6374SJason M. Bills   private:
444c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
445c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
446c4bf6374SJason M. Bills     {
447c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
448c4bf6374SJason M. Bills         long skip = 0;
449c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
450c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
451c4bf6374SJason M. Bills         {
452c4bf6374SJason M. Bills             return;
453c4bf6374SJason M. Bills         }
454c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
455c4bf6374SJason M. Bills         {
456c4bf6374SJason M. Bills             return;
457c4bf6374SJason M. Bills         }
458c4bf6374SJason M. Bills         const std::string &name = params[0];
459c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
460c4bf6374SJason M. Bills         // it has a duplicate entry for members
461c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
462c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
463c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
464c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
465c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
466c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices/EventLog/Entries";
467c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
468c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
469c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
470c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
471c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
472c4bf6374SJason M. Bills 
473c4bf6374SJason M. Bills         // Go through the journal and create a unique ID for each entry
474c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
475c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
476c4bf6374SJason M. Bills         if (ret < 0)
477c4bf6374SJason M. Bills         {
478c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
479c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
480c4bf6374SJason M. Bills             return;
481c4bf6374SJason M. Bills         }
482c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
483c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
484c4bf6374SJason M. Bills         journalTmp = nullptr;
485c4bf6374SJason M. Bills         uint64_t entryCount = 0;
486c4bf6374SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
487c4bf6374SJason M. Bills         {
488c4bf6374SJason M. Bills             // Look for only journal entries that contain a REDFISH_MESSAGE_ID
489c4bf6374SJason M. Bills             // field
490c4bf6374SJason M. Bills             boost::string_view messageID;
491c4bf6374SJason M. Bills             ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID",
492c4bf6374SJason M. Bills                                      messageID);
493c4bf6374SJason M. Bills             if (ret < 0)
494c4bf6374SJason M. Bills             {
495c4bf6374SJason M. Bills                 continue;
496c4bf6374SJason M. Bills             }
497c4bf6374SJason M. Bills 
498c4bf6374SJason M. Bills             entryCount++;
499c4bf6374SJason M. Bills             // Handle paging using skip (number of entries to skip from the
500c4bf6374SJason M. Bills             // start) and top (number of entries to display)
501c4bf6374SJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
502c4bf6374SJason M. Bills             {
503c4bf6374SJason M. Bills                 continue;
504c4bf6374SJason M. Bills             }
505c4bf6374SJason M. Bills 
506c4bf6374SJason M. Bills             std::string idStr;
507c4bf6374SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
508c4bf6374SJason M. Bills             {
509c4bf6374SJason M. Bills                 continue;
510c4bf6374SJason M. Bills             }
511c4bf6374SJason M. Bills 
512c4bf6374SJason M. Bills             logEntryArray.push_back({});
513c4bf6374SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
514c4bf6374SJason M. Bills             if (fillEventLogEntryJson(name, idStr, messageID, journal.get(),
515c4bf6374SJason M. Bills                                       bmcLogEntry) != 0)
516c4bf6374SJason M. Bills             {
517c4bf6374SJason M. Bills                 messages::internalError(asyncResp->res);
518c4bf6374SJason M. Bills                 return;
519c4bf6374SJason M. Bills             }
520c4bf6374SJason M. Bills         }
521c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
522c4bf6374SJason M. Bills         if (skip + top < entryCount)
523c4bf6374SJason M. Bills         {
524c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
525c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
526c4bf6374SJason M. Bills                 std::to_string(skip + top);
527c4bf6374SJason M. Bills         }
528c4bf6374SJason M. Bills     }
529c4bf6374SJason M. Bills };
530c4bf6374SJason M. Bills 
531c4bf6374SJason M. Bills class EventLogEntry : public Node
532c4bf6374SJason M. Bills {
533c4bf6374SJason M. Bills   public:
534c4bf6374SJason M. Bills     EventLogEntry(CrowApp &app) :
535c4bf6374SJason M. Bills         Node(app,
536c4bf6374SJason M. Bills              "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/",
537c4bf6374SJason M. Bills              std::string(), std::string())
538c4bf6374SJason M. Bills     {
539c4bf6374SJason M. Bills         entityPrivileges = {
540c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
541c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
542c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
543c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
544c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
545c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
546c4bf6374SJason M. Bills     }
547c4bf6374SJason M. Bills 
548c4bf6374SJason M. Bills   private:
549c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
550c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
551c4bf6374SJason M. Bills     {
552c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
553c4bf6374SJason M. Bills         if (params.size() != 2)
554c4bf6374SJason M. Bills         {
555c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
556c4bf6374SJason M. Bills             return;
557c4bf6374SJason M. Bills         }
558c4bf6374SJason M. Bills         const std::string &name = params[0];
559c4bf6374SJason M. Bills         const std::string &entryID = params[1];
560c4bf6374SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
561c4bf6374SJason M. Bills         uint64_t ts = 0;
562c4bf6374SJason M. Bills         uint16_t index = 0;
563c4bf6374SJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
564c4bf6374SJason M. Bills         {
565c4bf6374SJason M. Bills             return;
566c4bf6374SJason M. Bills         }
567c4bf6374SJason M. Bills 
568c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
569c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
570c4bf6374SJason M. Bills         if (ret < 0)
571c4bf6374SJason M. Bills         {
572c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
573c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
574c4bf6374SJason M. Bills             return;
575c4bf6374SJason M. Bills         }
576c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
577c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
578c4bf6374SJason M. Bills         journalTmp = nullptr;
579c4bf6374SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
580c4bf6374SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
581c4bf6374SJason M. Bills         for (int i = 0; i <= index; i++)
582c4bf6374SJason M. Bills         {
583c4bf6374SJason M. Bills             sd_journal_next(journal.get());
584c4bf6374SJason M. Bills         }
585c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
586c4bf6374SJason M. Bills         std::string idStr;
587c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
588c4bf6374SJason M. Bills         {
589c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
590c4bf6374SJason M. Bills             return;
591c4bf6374SJason M. Bills         }
592c4bf6374SJason M. Bills 
593c4bf6374SJason M. Bills         // only use journal entries that contain a REDFISH_MESSAGE_ID
594c4bf6374SJason M. Bills         // field
595c4bf6374SJason M. Bills         boost::string_view messageID;
596c4bf6374SJason M. Bills         ret =
597c4bf6374SJason M. Bills             getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
598c4bf6374SJason M. Bills         if (ret < 0)
599c4bf6374SJason M. Bills         {
600c4bf6374SJason M. Bills             messages::resourceNotFound(asyncResp->res, "LogEntry", name);
601c4bf6374SJason M. Bills             return;
602c4bf6374SJason M. Bills         }
603c4bf6374SJason M. Bills 
604c4bf6374SJason M. Bills         if (fillEventLogEntryJson(name, entryID, messageID, journal.get(),
605c4bf6374SJason M. Bills                                   asyncResp->res.jsonValue) != 0)
606c4bf6374SJason M. Bills         {
607c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
608c4bf6374SJason M. Bills             return;
609c4bf6374SJason M. Bills         }
610c4bf6374SJason M. Bills     }
611c4bf6374SJason M. Bills };
612c4bf6374SJason M. Bills 
613c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
614c4bf6374SJason M. Bills {
615c4bf6374SJason M. Bills   public:
616c4bf6374SJason M. Bills     template <typename CrowApp>
617c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
6184ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
6191da66f75SEd Tanous     {
6201da66f75SEd Tanous         entityPrivileges = {
621e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
622e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
623e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
624e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
625e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
626e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6271da66f75SEd Tanous     }
6281da66f75SEd Tanous 
6291da66f75SEd Tanous   private:
6301da66f75SEd Tanous     /**
6311da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
6321da66f75SEd Tanous      */
6331da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
6341da66f75SEd Tanous                const std::vector<std::string> &params) override
6351da66f75SEd Tanous     {
636e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6371da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
6381da66f75SEd Tanous         // it has a duplicate entry for members
639e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
6401da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
641e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
642c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
643e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
644e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
645e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
646e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
6471da66f75SEd Tanous             "Collection of LogServices for this Manager";
648c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
649c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
650c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
651c4bf6374SJason M. Bills         logServiceArray.push_back(
652c4bf6374SJason M. Bills             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
653c4bf6374SJason M. Bills #endif
6541da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
655c4bf6374SJason M. Bills         logServiceArray.push_back(
6564ed77cd5SEd Tanous             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
6571da66f75SEd Tanous #endif
658e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
659c4bf6374SJason M. Bills             logServiceArray.size();
6601da66f75SEd Tanous     }
6611da66f75SEd Tanous };
6621da66f75SEd Tanous 
663c4bf6374SJason M. Bills class BMCJournalLogService : public Node
6641da66f75SEd Tanous {
6651da66f75SEd Tanous   public:
6661da66f75SEd Tanous     template <typename CrowApp>
667c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
668c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
669e1f26343SJason M. Bills     {
670e1f26343SJason M. Bills         entityPrivileges = {
671e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
672e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
673e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
674e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
675e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
676e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
677e1f26343SJason M. Bills     }
678e1f26343SJason M. Bills 
679e1f26343SJason M. Bills   private:
680e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
681e1f26343SJason M. Bills                const std::vector<std::string> &params) override
682e1f26343SJason M. Bills     {
683e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
684e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
685e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
6860f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
6870f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
688e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
689e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
690c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
691c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
692c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
693e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
694e1f26343SJason M. Bills     }
695e1f26343SJason M. Bills };
696e1f26343SJason M. Bills 
697c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
698e1f26343SJason M. Bills                                       sd_journal *journal,
699c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
700e1f26343SJason M. Bills {
701e1f26343SJason M. Bills     // Get the Log Entry contents
702e1f26343SJason M. Bills     int ret = 0;
703e1f26343SJason M. Bills 
70416428a1aSJason M. Bills     boost::string_view msg;
70516428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
706e1f26343SJason M. Bills     if (ret < 0)
707e1f26343SJason M. Bills     {
708e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
709e1f26343SJason M. Bills         return 1;
710e1f26343SJason M. Bills     }
711e1f26343SJason M. Bills 
712e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
713e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
71416428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
715e1f26343SJason M. Bills     if (ret < 0)
716e1f26343SJason M. Bills     {
717e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
718e1f26343SJason M. Bills         return 1;
719e1f26343SJason M. Bills     }
720e1f26343SJason M. Bills 
721e1f26343SJason M. Bills     // Get the Created time from the timestamp
72216428a1aSJason M. Bills     std::string entryTimeStr;
72316428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
724e1f26343SJason M. Bills     {
72516428a1aSJason M. Bills         return 1;
726e1f26343SJason M. Bills     }
727e1f26343SJason M. Bills 
728e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
729c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
730e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
731e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
732c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
733c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
734e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
735c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
73616428a1aSJason M. Bills         {"Message", msg},
737e1f26343SJason M. Bills         {"EntryType", "Oem"},
738e1f26343SJason M. Bills         {"Severity",
739e1f26343SJason M. Bills          severity <= 2 ? "Critical"
740e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
741e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
742e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
743e1f26343SJason M. Bills     return 0;
744e1f26343SJason M. Bills }
745e1f26343SJason M. Bills 
746c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
747e1f26343SJason M. Bills {
748e1f26343SJason M. Bills   public:
749e1f26343SJason M. Bills     template <typename CrowApp>
750c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
751c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
752e1f26343SJason M. Bills     {
753e1f26343SJason M. Bills         entityPrivileges = {
754e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
755e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
756e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
757e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
758e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
759e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
760e1f26343SJason M. Bills     }
761e1f26343SJason M. Bills 
762e1f26343SJason M. Bills   private:
763e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
764e1f26343SJason M. Bills                const std::vector<std::string> &params) override
765e1f26343SJason M. Bills     {
766e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
767193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
768193ad2faSJason M. Bills         long skip = 0;
769193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
77016428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
771193ad2faSJason M. Bills         {
772193ad2faSJason M. Bills             return;
773193ad2faSJason M. Bills         }
77416428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
775193ad2faSJason M. Bills         {
776193ad2faSJason M. Bills             return;
777193ad2faSJason M. Bills         }
778e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
779e1f26343SJason M. Bills         // it has a duplicate entry for members
780e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
781e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
7820f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
7830f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
784e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
785c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
786e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
787c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
788e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
789e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
790e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
7910f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
7920f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
793e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
794e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
795e1f26343SJason M. Bills 
796e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
797e1f26343SJason M. Bills         // for each entry
798e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
799e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
800e1f26343SJason M. Bills         if (ret < 0)
801e1f26343SJason M. Bills         {
802e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
803f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
804e1f26343SJason M. Bills             return;
805e1f26343SJason M. Bills         }
806e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
807e1f26343SJason M. Bills             journalTmp, sd_journal_close);
808e1f26343SJason M. Bills         journalTmp = nullptr;
809193ad2faSJason M. Bills         uint64_t entryCount = 0;
810e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
811e1f26343SJason M. Bills         {
812193ad2faSJason M. Bills             entryCount++;
813193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
814193ad2faSJason M. Bills             // start) and top (number of entries to display)
815193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
816193ad2faSJason M. Bills             {
817193ad2faSJason M. Bills                 continue;
818193ad2faSJason M. Bills             }
819193ad2faSJason M. Bills 
82016428a1aSJason M. Bills             std::string idStr;
82116428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
822e1f26343SJason M. Bills             {
823e1f26343SJason M. Bills                 continue;
824e1f26343SJason M. Bills             }
825e1f26343SJason M. Bills 
826e1f26343SJason M. Bills             logEntryArray.push_back({});
827c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
828c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
829c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
830e1f26343SJason M. Bills             {
831f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
832e1f26343SJason M. Bills                 return;
833e1f26343SJason M. Bills             }
834e1f26343SJason M. Bills         }
835193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
836193ad2faSJason M. Bills         if (skip + top < entryCount)
837193ad2faSJason M. Bills         {
838193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
839c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
840193ad2faSJason M. Bills                 std::to_string(skip + top);
841193ad2faSJason M. Bills         }
842e1f26343SJason M. Bills     }
843e1f26343SJason M. Bills };
844e1f26343SJason M. Bills 
845c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
846e1f26343SJason M. Bills {
847e1f26343SJason M. Bills   public:
848c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
849c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
850e1f26343SJason M. Bills              std::string())
851e1f26343SJason M. Bills     {
852e1f26343SJason M. Bills         entityPrivileges = {
853e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
854e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
855e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
856e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
857e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
858e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
859e1f26343SJason M. Bills     }
860e1f26343SJason M. Bills 
861e1f26343SJason M. Bills   private:
862e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
863e1f26343SJason M. Bills                const std::vector<std::string> &params) override
864e1f26343SJason M. Bills     {
865e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
866e1f26343SJason M. Bills         if (params.size() != 1)
867e1f26343SJason M. Bills         {
868f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
869e1f26343SJason M. Bills             return;
870e1f26343SJason M. Bills         }
87116428a1aSJason M. Bills         const std::string &entryID = params[0];
872e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
873e1f26343SJason M. Bills         uint64_t ts = 0;
874e1f26343SJason M. Bills         uint16_t index = 0;
87516428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
876e1f26343SJason M. Bills         {
87716428a1aSJason M. Bills             return;
878e1f26343SJason M. Bills         }
879e1f26343SJason M. Bills 
880e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
881e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
882e1f26343SJason M. Bills         if (ret < 0)
883e1f26343SJason M. Bills         {
884e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
885f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
886e1f26343SJason M. Bills             return;
887e1f26343SJason M. Bills         }
888e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
889e1f26343SJason M. Bills             journalTmp, sd_journal_close);
890e1f26343SJason M. Bills         journalTmp = nullptr;
891e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
892e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
893e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
894e1f26343SJason M. Bills         {
895e1f26343SJason M. Bills             sd_journal_next(journal.get());
896e1f26343SJason M. Bills         }
897c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
898c4bf6374SJason M. Bills         std::string idStr;
899c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
900c4bf6374SJason M. Bills         {
901c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
902c4bf6374SJason M. Bills             return;
903c4bf6374SJason M. Bills         }
904c4bf6374SJason M. Bills 
905c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
906e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
907e1f26343SJason M. Bills         {
908f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
909e1f26343SJason M. Bills             return;
910e1f26343SJason M. Bills         }
911e1f26343SJason M. Bills     }
912e1f26343SJason M. Bills };
913e1f26343SJason M. Bills 
914e1f26343SJason M. Bills class CPULogService : public Node
915e1f26343SJason M. Bills {
916e1f26343SJason M. Bills   public:
917e1f26343SJason M. Bills     template <typename CrowApp>
918e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
919e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
9201da66f75SEd Tanous     {
9211da66f75SEd Tanous         entityPrivileges = {
922e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
923e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
924e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
925e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
926e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
927e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9281da66f75SEd Tanous     }
9291da66f75SEd Tanous 
9301da66f75SEd Tanous   private:
9311da66f75SEd Tanous     /**
9321da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9331da66f75SEd Tanous      */
9341da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9351da66f75SEd Tanous                const std::vector<std::string> &params) override
9361da66f75SEd Tanous     {
937e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9381da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
9390f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
9400f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog";
941e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
942e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
943e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
944c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
945e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
946e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
947e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
948e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
949e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
950e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
9511da66f75SEd Tanous             {"Oem",
9521da66f75SEd Tanous              {{"#CpuLog.Immediate",
953c4bf6374SJason M. Bills                {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/"
954c4bf6374SJason M. Bills                            "Actions/Oem/CpuLog.Immediate"}}}}}};
9551da66f75SEd Tanous 
9561da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
957e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
9581da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
959c4bf6374SJason M. Bills              {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/"
960c4bf6374SJason M. Bills                          "Oem/CpuLog.SendRawPeci"}}});
9611da66f75SEd Tanous #endif
9621da66f75SEd Tanous     }
9631da66f75SEd Tanous };
9641da66f75SEd Tanous 
965e1f26343SJason M. Bills class CPULogEntryCollection : public Node
9661da66f75SEd Tanous {
9671da66f75SEd Tanous   public:
9681da66f75SEd Tanous     template <typename CrowApp>
969e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
970e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
9711da66f75SEd Tanous     {
9721da66f75SEd Tanous         entityPrivileges = {
973e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
974e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
975e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
976e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
977e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
978e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9791da66f75SEd Tanous     }
9801da66f75SEd Tanous 
9811da66f75SEd Tanous   private:
9821da66f75SEd Tanous     /**
9831da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9841da66f75SEd Tanous      */
9851da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9861da66f75SEd Tanous                const std::vector<std::string> &params) override
9871da66f75SEd Tanous     {
988e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9891da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
9901da66f75SEd Tanous         // it has a duplicate entry for members
991e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
992e1f26343SJason M. Bills                                          const boost::system::error_code ec,
9931da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
9941da66f75SEd Tanous             if (ec)
9951da66f75SEd Tanous             {
9961da66f75SEd Tanous                 if (ec.value() !=
9971da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
9981da66f75SEd Tanous                 {
9991da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
10001da66f75SEd Tanous                                      << ec.message();
1001f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
10021da66f75SEd Tanous                     return;
10031da66f75SEd Tanous                 }
10041da66f75SEd Tanous             }
1005e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
10061da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
10070f74e643SEd Tanous             asyncResp->res.jsonValue["@odata.id"] =
10080f74e643SEd Tanous                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
1009e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1010c4bf6374SJason M. Bills                 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1011e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.id"] =
1012e1f26343SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
1013e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
1014e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1015e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
1016e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1017e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
10181da66f75SEd Tanous             for (const std::string &objpath : resp)
10191da66f75SEd Tanous             {
10201da66f75SEd Tanous                 // Don't list the immediate log
10214ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
10221da66f75SEd Tanous                 {
10231da66f75SEd Tanous                     continue;
10241da66f75SEd Tanous                 }
10254ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
10264ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
10271da66f75SEd Tanous                 {
1028e1f26343SJason M. Bills                     logEntryArray.push_back(
1029e1f26343SJason M. Bills                         {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
1030e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
10314ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
10321da66f75SEd Tanous                 }
10331da66f75SEd Tanous             }
1034e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1035e1f26343SJason M. Bills                 logEntryArray.size();
10361da66f75SEd Tanous         };
10371da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
10381da66f75SEd Tanous             std::move(getLogEntriesCallback),
10391da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
10401da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
10411da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
10424ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
10431da66f75SEd Tanous     }
10441da66f75SEd Tanous };
10451da66f75SEd Tanous 
10461da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
10471da66f75SEd Tanous {
10481da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
10491da66f75SEd Tanous     if (metaIt != cpuLog.end())
10501da66f75SEd Tanous     {
10511da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
10521da66f75SEd Tanous         if (tsIt != metaIt->end())
10531da66f75SEd Tanous         {
10541da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
10551da66f75SEd Tanous             if (logTime != nullptr)
10561da66f75SEd Tanous             {
10571da66f75SEd Tanous                 return *logTime;
10581da66f75SEd Tanous             }
10591da66f75SEd Tanous         }
10601da66f75SEd Tanous     }
10611da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
10621da66f75SEd Tanous 
10631da66f75SEd Tanous     return std::string();
10641da66f75SEd Tanous }
10651da66f75SEd Tanous 
1066e1f26343SJason M. Bills class CPULogEntry : public Node
10671da66f75SEd Tanous {
10681da66f75SEd Tanous   public:
1069e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
10704ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
10711da66f75SEd Tanous              std::string())
10721da66f75SEd Tanous     {
10731da66f75SEd Tanous         entityPrivileges = {
1074e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1075e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1076e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1077e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1078e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1079e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
10801da66f75SEd Tanous     }
10811da66f75SEd Tanous 
10821da66f75SEd Tanous   private:
10831da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
10841da66f75SEd Tanous                const std::vector<std::string> &params) override
10851da66f75SEd Tanous     {
1086e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
10871da66f75SEd Tanous         if (params.size() != 1)
10881da66f75SEd Tanous         {
1089f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
10901da66f75SEd Tanous             return;
10911da66f75SEd Tanous         }
10924ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1093e1f26343SJason M. Bills         auto getStoredLogCallback =
1094e1f26343SJason M. Bills             [asyncResp,
10954ed77cd5SEd Tanous              logId](const boost::system::error_code ec,
1096e1f26343SJason M. Bills                     const sdbusplus::message::variant<std::string> &resp) {
10971da66f75SEd Tanous                 if (ec)
10981da66f75SEd Tanous                 {
1099e1f26343SJason M. Bills                     BMCWEB_LOG_DEBUG << "failed to get log ec: "
1100e1f26343SJason M. Bills                                      << ec.message();
1101f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
11021da66f75SEd Tanous                     return;
11031da66f75SEd Tanous                 }
1104e1f26343SJason M. Bills                 const std::string *log =
1105*1b6b96c5SEd Tanous                     sdbusplus::message::variant_ns::get_if<std::string>(&resp);
11061da66f75SEd Tanous                 if (log == nullptr)
11071da66f75SEd Tanous                 {
1108f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
11091da66f75SEd Tanous                     return;
11101da66f75SEd Tanous                 }
11111da66f75SEd Tanous                 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
11121da66f75SEd Tanous                 if (j.is_discarded())
11131da66f75SEd Tanous                 {
1114f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
11151da66f75SEd Tanous                     return;
11161da66f75SEd Tanous                 }
11171da66f75SEd Tanous                 std::string t = getLogCreatedTime(j);
1118e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {
11191da66f75SEd Tanous                     {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
1120e1f26343SJason M. Bills                     {"@odata.context",
1121e1f26343SJason M. Bills                      "/redfish/v1/$metadata#LogEntry.LogEntry"},
11221da66f75SEd Tanous                     {"@odata.id",
11234ed77cd5SEd Tanous                      "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
11244ed77cd5SEd Tanous                          std::to_string(logId)},
11251da66f75SEd Tanous                     {"Name", "CPU Debug Log"},
11264ed77cd5SEd Tanous                     {"Id", logId},
11271da66f75SEd Tanous                     {"EntryType", "Oem"},
11281da66f75SEd Tanous                     {"OemRecordFormat", "Intel CPU Log"},
11291da66f75SEd Tanous                     {"Oem", {{"Intel", std::move(j)}}},
11301da66f75SEd Tanous                     {"Created", std::move(t)}};
11311da66f75SEd Tanous             };
11321da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
11334ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
11344ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
11354ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
11361da66f75SEd Tanous     }
11371da66f75SEd Tanous };
11381da66f75SEd Tanous 
1139e1f26343SJason M. Bills class ImmediateCPULog : public Node
11401da66f75SEd Tanous {
11411da66f75SEd Tanous   public:
1142e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
11434ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
1144e1f26343SJason M. Bills                   "CpuLog.Immediate/")
11451da66f75SEd Tanous     {
11461da66f75SEd Tanous         entityPrivileges = {
1147e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1148e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1149e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1150e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1151e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1152e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
11531da66f75SEd Tanous     }
11541da66f75SEd Tanous 
11551da66f75SEd Tanous   private:
11561da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
11571da66f75SEd Tanous                 const std::vector<std::string> &params) override
11581da66f75SEd Tanous     {
1159e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
11601da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
11611da66f75SEd Tanous             immediateLogMatcher;
11621da66f75SEd Tanous 
11631da66f75SEd Tanous         // Only allow one Immediate Log request at a time
11641da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
11651da66f75SEd Tanous         {
1166e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1167f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
11681da66f75SEd Tanous             return;
11691da66f75SEd Tanous         }
11701da66f75SEd Tanous         // Make this static so it survives outside this method
11711da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
11721da66f75SEd Tanous 
11731da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1174e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
11751da66f75SEd Tanous             immediateLogMatcher = nullptr;
11761da66f75SEd Tanous             if (ec)
11771da66f75SEd Tanous             {
11781da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
11791da66f75SEd Tanous                 // completion.
11801da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
11811da66f75SEd Tanous                 {
11821da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
11831da66f75SEd Tanous                 }
11841da66f75SEd Tanous                 return;
11851da66f75SEd Tanous             }
11861da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
11871da66f75SEd Tanous 
1188f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
11891da66f75SEd Tanous         });
11901da66f75SEd Tanous 
1191e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
11921da66f75SEd Tanous                                                sdbusplus::message::message &m) {
11931da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
11941da66f75SEd Tanous             boost::system::error_code ec;
11951da66f75SEd Tanous             timeout.cancel(ec);
11961da66f75SEd Tanous             if (ec)
11971da66f75SEd Tanous             {
11981da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
11991da66f75SEd Tanous             }
12004ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
12011da66f75SEd Tanous             boost::container::flat_map<
12021da66f75SEd Tanous                 std::string,
12031da66f75SEd Tanous                 boost::container::flat_map<
12041da66f75SEd Tanous                     std::string, sdbusplus::message::variant<std::string>>>
12054ed77cd5SEd Tanous                 interfacesAdded;
12064ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1207*1b6b96c5SEd Tanous             const std::string *log =
1208*1b6b96c5SEd Tanous                 sdbusplus::message::variant_ns::get_if<std::string>(
1209*1b6b96c5SEd Tanous                     &interfacesAdded[cpuLogInterface]["Log"]);
12101da66f75SEd Tanous             if (log == nullptr)
12111da66f75SEd Tanous             {
1212f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12131da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
12141da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
12151da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
12161da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
12171da66f75SEd Tanous                 // be the last thing done.
12181da66f75SEd Tanous                 immediateLogMatcher = nullptr;
12191da66f75SEd Tanous                 return;
12201da66f75SEd Tanous             }
12211da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
12221da66f75SEd Tanous             if (j.is_discarded())
12231da66f75SEd Tanous             {
1224f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12251da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
12261da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
12271da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
12281da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
12291da66f75SEd Tanous                 // be the last thing done.
12301da66f75SEd Tanous                 immediateLogMatcher = nullptr;
12311da66f75SEd Tanous                 return;
12321da66f75SEd Tanous             }
12331da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1234e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
12351da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
12361da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
12371da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
12381da66f75SEd Tanous                 {"EntryType", "Oem"},
12391da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
12401da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
12411da66f75SEd Tanous                 {"Created", std::move(t)}};
12421da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
12431da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
12441da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
12451da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
12461da66f75SEd Tanous             // thing done.
12471da66f75SEd Tanous             immediateLogMatcher = nullptr;
12481da66f75SEd Tanous         };
12491da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
12501da66f75SEd Tanous             *crow::connections::systemBus,
12511da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
12524ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
12531da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
12541da66f75SEd Tanous 
12551da66f75SEd Tanous         auto generateImmediateLogCallback =
1256e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
12571da66f75SEd Tanous                         const std::string &resp) {
12581da66f75SEd Tanous                 if (ec)
12591da66f75SEd Tanous                 {
12601da66f75SEd Tanous                     if (ec.value() ==
12611da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
12621da66f75SEd Tanous                     {
1263f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
12641da66f75SEd Tanous                     }
12651da66f75SEd Tanous                     else
12661da66f75SEd Tanous                     {
1267f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
12681da66f75SEd Tanous                     }
12691da66f75SEd Tanous                     boost::system::error_code timeoutec;
12701da66f75SEd Tanous                     timeout.cancel(timeoutec);
12711da66f75SEd Tanous                     if (timeoutec)
12721da66f75SEd Tanous                     {
12731da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
12741da66f75SEd Tanous                                          << timeoutec;
12751da66f75SEd Tanous                     }
12761da66f75SEd Tanous                     immediateLogMatcher = nullptr;
12771da66f75SEd Tanous                     return;
12781da66f75SEd Tanous                 }
12791da66f75SEd Tanous             };
12801da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
12814ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
12824ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
12831da66f75SEd Tanous     }
12841da66f75SEd Tanous };
12851da66f75SEd Tanous 
1286e1f26343SJason M. Bills class SendRawPECI : public Node
12871da66f75SEd Tanous {
12881da66f75SEd Tanous   public:
1289e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
12904ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
1291e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
12921da66f75SEd Tanous     {
12931da66f75SEd Tanous         entityPrivileges = {
12941da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
12951da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
12961da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
12971da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
12981da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
12991da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
13001da66f75SEd Tanous     }
13011da66f75SEd Tanous 
13021da66f75SEd Tanous   private:
13031da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
13041da66f75SEd Tanous                 const std::vector<std::string> &params) override
13051da66f75SEd Tanous     {
1306e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1307b1556427SEd Tanous         uint8_t clientAddress = 0;
1308b1556427SEd Tanous         uint8_t readLength = 0;
13091da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
1310b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1311b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
1312b1556427SEd Tanous                                  peciCommand))
13131da66f75SEd Tanous         {
13141da66f75SEd Tanous             return;
13151da66f75SEd Tanous         }
1316b1556427SEd Tanous 
13171da66f75SEd Tanous         // Callback to return the Raw PECI response
1318e1f26343SJason M. Bills         auto sendRawPECICallback =
1319e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
13201da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
13211da66f75SEd Tanous                 if (ec)
13221da66f75SEd Tanous                 {
13231da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
13241da66f75SEd Tanous                                      << ec.message();
1325f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
13261da66f75SEd Tanous                     return;
13271da66f75SEd Tanous                 }
1328e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
13291da66f75SEd Tanous                                             {"PECIResponse", resp}};
13301da66f75SEd Tanous             };
13311da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
13321da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1333e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
1334e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
13354ed77cd5SEd Tanous             peciCommand);
13361da66f75SEd Tanous     }
13371da66f75SEd Tanous };
13381da66f75SEd Tanous 
13391da66f75SEd Tanous } // namespace redfish
1340