xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision cd50aa42f5fe1f7acd04144f877c7b909484fb6c)
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>
25abf2add6SEd Tanous #include <variant>
261da66f75SEd Tanous 
271da66f75SEd Tanous namespace redfish
281da66f75SEd Tanous {
291da66f75SEd Tanous 
304ed77cd5SEd Tanous constexpr char const *cpuLogObject = "com.intel.CpuDebugLog";
314ed77cd5SEd Tanous constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog";
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) :
263029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/")
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         // 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"] =
289029573d4SEd Tanous             "/redfish/v1/Systems/system/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();
295029573d4SEd Tanous         logServiceArray.push_back(
296029573d4SEd Tanous             {{"@odata.id", "/redfish/v1/Systems/system/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) :
307029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
308c4bf6374SJason M. Bills     {
309c4bf6374SJason M. Bills         entityPrivileges = {
310c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
311c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
312c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
313c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
314c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
315c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
316c4bf6374SJason M. Bills     }
317c4bf6374SJason M. Bills 
318c4bf6374SJason M. Bills   private:
319c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
320c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
321c4bf6374SJason M. Bills     {
322c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
323c4bf6374SJason M. Bills 
324c4bf6374SJason M. Bills         const std::string &name = params[0];
325c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
326029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog";
327c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
328c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
329c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
330c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
331c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
332c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
333c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
334c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
335c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
336c4bf6374SJason M. Bills             {"@odata.id",
337029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
338c4bf6374SJason M. Bills     }
339c4bf6374SJason M. Bills };
340c4bf6374SJason M. Bills 
341029573d4SEd Tanous static int fillEventLogEntryJson(const std::string &bmcLogEntryID,
342c4bf6374SJason M. Bills                                  const boost::string_view &messageID,
343c4bf6374SJason M. Bills                                  sd_journal *journal,
344c4bf6374SJason M. Bills                                  nlohmann::json &bmcLogEntryJson)
345c4bf6374SJason M. Bills {
346c4bf6374SJason M. Bills     // Get the Log Entry contents
347c4bf6374SJason M. Bills     int ret = 0;
348c4bf6374SJason M. Bills 
349c4bf6374SJason M. Bills     boost::string_view msg;
350c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
351c4bf6374SJason M. Bills     if (ret < 0)
352c4bf6374SJason M. Bills     {
353c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
354c4bf6374SJason M. Bills         return 1;
355c4bf6374SJason M. Bills     }
356c4bf6374SJason M. Bills 
357c4bf6374SJason M. Bills     // Get the severity from the PRIORITY field
358c4bf6374SJason M. Bills     int severity = 8; // Default to an invalid priority
359c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
360c4bf6374SJason M. Bills     if (ret < 0)
361c4bf6374SJason M. Bills     {
362c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
363c4bf6374SJason M. Bills         return 1;
364c4bf6374SJason M. Bills     }
365c4bf6374SJason M. Bills 
366c4bf6374SJason M. Bills     // Get the MessageArgs from the journal entry by finding all of the
367c4bf6374SJason M. Bills     // REDFISH_MESSAGE_ARG_x fields
368c4bf6374SJason M. Bills     const void *data;
369c4bf6374SJason M. Bills     size_t length;
370c4bf6374SJason M. Bills     std::vector<std::string> messageArgs;
371c4bf6374SJason M. Bills     SD_JOURNAL_FOREACH_DATA(journal, data, length)
372c4bf6374SJason M. Bills     {
373c4bf6374SJason M. Bills         boost::string_view field(static_cast<const char *>(data), length);
374c4bf6374SJason M. Bills         if (field.starts_with("REDFISH_MESSAGE_ARG_"))
375c4bf6374SJason M. Bills         {
376c4bf6374SJason M. Bills             // Get the Arg number from the field name
377c4bf6374SJason M. Bills             field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
378c4bf6374SJason M. Bills             if (field.empty())
379c4bf6374SJason M. Bills             {
380c4bf6374SJason M. Bills                 continue;
381c4bf6374SJason M. Bills             }
382c4bf6374SJason M. Bills             int argNum = std::strtoul(field.data(), nullptr, 10);
383c4bf6374SJason M. Bills             if (argNum == 0)
384c4bf6374SJason M. Bills             {
385c4bf6374SJason M. Bills                 continue;
386c4bf6374SJason M. Bills             }
387c4bf6374SJason M. Bills             // Get the Arg value after the "=" character.
388c4bf6374SJason M. Bills             field.remove_prefix(std::min(field.find("=") + 1, field.size()));
389c4bf6374SJason M. Bills             // Make sure we have enough space in messageArgs
390c4bf6374SJason M. Bills             if (argNum > messageArgs.size())
391c4bf6374SJason M. Bills             {
392c4bf6374SJason M. Bills                 messageArgs.resize(argNum);
393c4bf6374SJason M. Bills             }
394c4bf6374SJason M. Bills             messageArgs[argNum - 1] = field.to_string();
395c4bf6374SJason M. Bills         }
396c4bf6374SJason M. Bills     }
397c4bf6374SJason M. Bills 
398c4bf6374SJason M. Bills     // Get the Created time from the timestamp
399c4bf6374SJason M. Bills     std::string entryTimeStr;
400c4bf6374SJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
401c4bf6374SJason M. Bills     {
402c4bf6374SJason M. Bills         return 1;
403c4bf6374SJason M. Bills     }
404c4bf6374SJason M. Bills 
405c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
406c4bf6374SJason M. Bills     bmcLogEntryJson = {
407c4bf6374SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
408c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
409029573d4SEd Tanous         {"@odata.id",
410029573d4SEd Tanous          "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
411029573d4SEd Tanous              bmcLogEntryID},
412c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
413c4bf6374SJason M. Bills         {"Id", bmcLogEntryID},
414c4bf6374SJason M. Bills         {"Message", msg},
415c4bf6374SJason M. Bills         {"MessageId", messageID},
416c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
417c4bf6374SJason M. Bills         {"EntryType", "Event"},
418c4bf6374SJason M. Bills         {"Severity",
419c4bf6374SJason M. Bills          severity <= 2 ? "Critical"
420c4bf6374SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
421c4bf6374SJason M. Bills         {"Created", std::move(entryTimeStr)}};
422c4bf6374SJason M. Bills     return 0;
423c4bf6374SJason M. Bills }
424c4bf6374SJason M. Bills 
425c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
426c4bf6374SJason M. Bills {
427c4bf6374SJason M. Bills   public:
428c4bf6374SJason M. Bills     template <typename CrowApp>
429c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
430029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
431c4bf6374SJason M. Bills     {
432c4bf6374SJason M. Bills         entityPrivileges = {
433c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
434c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
435c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
436c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
437c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
438c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
439c4bf6374SJason M. Bills     }
440c4bf6374SJason M. Bills 
441c4bf6374SJason M. Bills   private:
442c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
443c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
444c4bf6374SJason M. Bills     {
445c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
446c4bf6374SJason M. Bills         long skip = 0;
447c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
448c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
449c4bf6374SJason M. Bills         {
450c4bf6374SJason M. Bills             return;
451c4bf6374SJason M. Bills         }
452c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
453c4bf6374SJason M. Bills         {
454c4bf6374SJason M. Bills             return;
455c4bf6374SJason M. Bills         }
456c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
457c4bf6374SJason M. Bills         // it has a duplicate entry for members
458c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
459c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
460c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
461c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
462c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
463029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
464c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
465c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
466c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
467c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
468c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
469c4bf6374SJason M. Bills 
470c4bf6374SJason M. Bills         // Go through the journal and create a unique ID for each entry
471c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
472c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
473c4bf6374SJason M. Bills         if (ret < 0)
474c4bf6374SJason M. Bills         {
475c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
476c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
477c4bf6374SJason M. Bills             return;
478c4bf6374SJason M. Bills         }
479c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
480c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
481c4bf6374SJason M. Bills         journalTmp = nullptr;
482c4bf6374SJason M. Bills         uint64_t entryCount = 0;
483c4bf6374SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
484c4bf6374SJason M. Bills         {
485c4bf6374SJason M. Bills             // Look for only journal entries that contain a REDFISH_MESSAGE_ID
486c4bf6374SJason M. Bills             // field
487c4bf6374SJason M. Bills             boost::string_view messageID;
488c4bf6374SJason M. Bills             ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID",
489c4bf6374SJason M. Bills                                      messageID);
490c4bf6374SJason M. Bills             if (ret < 0)
491c4bf6374SJason M. Bills             {
492c4bf6374SJason M. Bills                 continue;
493c4bf6374SJason M. Bills             }
494c4bf6374SJason M. Bills 
495c4bf6374SJason M. Bills             entryCount++;
496c4bf6374SJason M. Bills             // Handle paging using skip (number of entries to skip from the
497c4bf6374SJason M. Bills             // start) and top (number of entries to display)
498c4bf6374SJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
499c4bf6374SJason M. Bills             {
500c4bf6374SJason M. Bills                 continue;
501c4bf6374SJason M. Bills             }
502c4bf6374SJason M. Bills 
503c4bf6374SJason M. Bills             std::string idStr;
504c4bf6374SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
505c4bf6374SJason M. Bills             {
506c4bf6374SJason M. Bills                 continue;
507c4bf6374SJason M. Bills             }
508c4bf6374SJason M. Bills 
509c4bf6374SJason M. Bills             logEntryArray.push_back({});
510c4bf6374SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
511029573d4SEd Tanous             if (fillEventLogEntryJson(idStr, messageID, journal.get(),
512c4bf6374SJason M. Bills                                       bmcLogEntry) != 0)
513c4bf6374SJason M. Bills             {
514c4bf6374SJason M. Bills                 messages::internalError(asyncResp->res);
515c4bf6374SJason M. Bills                 return;
516c4bf6374SJason M. Bills             }
517c4bf6374SJason M. Bills         }
518c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
519c4bf6374SJason M. Bills         if (skip + top < entryCount)
520c4bf6374SJason M. Bills         {
521c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
522c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
523c4bf6374SJason M. Bills                 std::to_string(skip + top);
524c4bf6374SJason M. Bills         }
525c4bf6374SJason M. Bills     }
526c4bf6374SJason M. Bills };
527c4bf6374SJason M. Bills 
528c4bf6374SJason M. Bills class EventLogEntry : public Node
529c4bf6374SJason M. Bills {
530c4bf6374SJason M. Bills   public:
531c4bf6374SJason M. Bills     EventLogEntry(CrowApp &app) :
532c4bf6374SJason M. Bills         Node(app,
533029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
534029573d4SEd Tanous              std::string())
535c4bf6374SJason M. Bills     {
536c4bf6374SJason M. Bills         entityPrivileges = {
537c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
538c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
539c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
540c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
541c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
542c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
543c4bf6374SJason M. Bills     }
544c4bf6374SJason M. Bills 
545c4bf6374SJason M. Bills   private:
546c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
547c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
548c4bf6374SJason M. Bills     {
549c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
550029573d4SEd Tanous         if (params.size() != 1)
551c4bf6374SJason M. Bills         {
552c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
553c4bf6374SJason M. Bills             return;
554c4bf6374SJason M. Bills         }
555029573d4SEd Tanous         const std::string &entryID = params[0];
556c4bf6374SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
557c4bf6374SJason M. Bills         uint64_t ts = 0;
558c4bf6374SJason M. Bills         uint16_t index = 0;
559c4bf6374SJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
560c4bf6374SJason M. Bills         {
561c4bf6374SJason M. Bills             return;
562c4bf6374SJason M. Bills         }
563c4bf6374SJason M. Bills 
564c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
565c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
566c4bf6374SJason M. Bills         if (ret < 0)
567c4bf6374SJason M. Bills         {
568c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
569c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
570c4bf6374SJason M. Bills             return;
571c4bf6374SJason M. Bills         }
572c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
573c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
574c4bf6374SJason M. Bills         journalTmp = nullptr;
575c4bf6374SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
576c4bf6374SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
577c4bf6374SJason M. Bills         for (int i = 0; i <= index; i++)
578c4bf6374SJason M. Bills         {
579c4bf6374SJason M. Bills             sd_journal_next(journal.get());
580c4bf6374SJason M. Bills         }
581c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
582c4bf6374SJason M. Bills         std::string idStr;
583c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
584c4bf6374SJason M. Bills         {
585c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
586c4bf6374SJason M. Bills             return;
587c4bf6374SJason M. Bills         }
588c4bf6374SJason M. Bills 
589*cd50aa42SJason M. Bills         // only use journal entries that contain a REDFISH_MESSAGE_ID field
590c4bf6374SJason M. Bills         boost::string_view messageID;
591c4bf6374SJason M. Bills         ret =
592c4bf6374SJason M. Bills             getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
593c4bf6374SJason M. Bills         if (ret < 0)
594c4bf6374SJason M. Bills         {
595029573d4SEd Tanous             messages::resourceNotFound(asyncResp->res, "LogEntry", "system");
596c4bf6374SJason M. Bills             return;
597c4bf6374SJason M. Bills         }
598c4bf6374SJason M. Bills 
599029573d4SEd Tanous         if (fillEventLogEntryJson(entryID, messageID, journal.get(),
600c4bf6374SJason M. Bills                                   asyncResp->res.jsonValue) != 0)
601c4bf6374SJason M. Bills         {
602c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
603c4bf6374SJason M. Bills             return;
604c4bf6374SJason M. Bills         }
605c4bf6374SJason M. Bills     }
606c4bf6374SJason M. Bills };
607c4bf6374SJason M. Bills 
608c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
609c4bf6374SJason M. Bills {
610c4bf6374SJason M. Bills   public:
611c4bf6374SJason M. Bills     template <typename CrowApp>
612c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
6134ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
6141da66f75SEd Tanous     {
6151da66f75SEd Tanous         entityPrivileges = {
616e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
617e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
618e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
619e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
620e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
621e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6221da66f75SEd Tanous     }
6231da66f75SEd Tanous 
6241da66f75SEd Tanous   private:
6251da66f75SEd Tanous     /**
6261da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
6271da66f75SEd Tanous      */
6281da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
6291da66f75SEd Tanous                const std::vector<std::string> &params) override
6301da66f75SEd Tanous     {
631e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6321da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
6331da66f75SEd Tanous         // it has a duplicate entry for members
634e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
6351da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
636e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
637c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
638e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
639e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
640e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
641e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
6421da66f75SEd Tanous             "Collection of LogServices for this Manager";
643c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
644c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
645c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
646c4bf6374SJason M. Bills         logServiceArray.push_back(
647c4bf6374SJason M. Bills             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
648c4bf6374SJason M. Bills #endif
6491da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
650c4bf6374SJason M. Bills         logServiceArray.push_back(
6514ed77cd5SEd Tanous             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
6521da66f75SEd Tanous #endif
653e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
654c4bf6374SJason M. Bills             logServiceArray.size();
6551da66f75SEd Tanous     }
6561da66f75SEd Tanous };
6571da66f75SEd Tanous 
658c4bf6374SJason M. Bills class BMCJournalLogService : public Node
6591da66f75SEd Tanous {
6601da66f75SEd Tanous   public:
6611da66f75SEd Tanous     template <typename CrowApp>
662c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
663c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
664e1f26343SJason M. Bills     {
665e1f26343SJason M. Bills         entityPrivileges = {
666e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
667e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
668e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
669e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
670e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
671e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
672e1f26343SJason M. Bills     }
673e1f26343SJason M. Bills 
674e1f26343SJason M. Bills   private:
675e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
676e1f26343SJason M. Bills                const std::vector<std::string> &params) override
677e1f26343SJason M. Bills     {
678e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
679e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
680e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
6810f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
6820f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
683e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
684e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
685c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
686c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
687c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
688e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
689*cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
690*cd50aa42SJason M. Bills             {"@odata.id",
691*cd50aa42SJason M. Bills              "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}};
692e1f26343SJason M. Bills     }
693e1f26343SJason M. Bills };
694e1f26343SJason M. Bills 
695c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
696e1f26343SJason M. Bills                                       sd_journal *journal,
697c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
698e1f26343SJason M. Bills {
699e1f26343SJason M. Bills     // Get the Log Entry contents
700e1f26343SJason M. Bills     int ret = 0;
701e1f26343SJason M. Bills 
70216428a1aSJason M. Bills     boost::string_view msg;
70316428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
704e1f26343SJason M. Bills     if (ret < 0)
705e1f26343SJason M. Bills     {
706e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
707e1f26343SJason M. Bills         return 1;
708e1f26343SJason M. Bills     }
709e1f26343SJason M. Bills 
710e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
711e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
71216428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
713e1f26343SJason M. Bills     if (ret < 0)
714e1f26343SJason M. Bills     {
715e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
716e1f26343SJason M. Bills         return 1;
717e1f26343SJason M. Bills     }
718e1f26343SJason M. Bills 
719e1f26343SJason M. Bills     // Get the Created time from the timestamp
72016428a1aSJason M. Bills     std::string entryTimeStr;
72116428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
722e1f26343SJason M. Bills     {
72316428a1aSJason M. Bills         return 1;
724e1f26343SJason M. Bills     }
725e1f26343SJason M. Bills 
726e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
727c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
728e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
729e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
730c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
731c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
732e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
733c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
73416428a1aSJason M. Bills         {"Message", msg},
735e1f26343SJason M. Bills         {"EntryType", "Oem"},
736e1f26343SJason M. Bills         {"Severity",
737e1f26343SJason M. Bills          severity <= 2 ? "Critical"
738e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
739e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
740e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
741e1f26343SJason M. Bills     return 0;
742e1f26343SJason M. Bills }
743e1f26343SJason M. Bills 
744c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
745e1f26343SJason M. Bills {
746e1f26343SJason M. Bills   public:
747e1f26343SJason M. Bills     template <typename CrowApp>
748c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
749c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
750e1f26343SJason M. Bills     {
751e1f26343SJason M. Bills         entityPrivileges = {
752e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
753e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
754e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
755e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
756e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
757e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
758e1f26343SJason M. Bills     }
759e1f26343SJason M. Bills 
760e1f26343SJason M. Bills   private:
761e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
762e1f26343SJason M. Bills                const std::vector<std::string> &params) override
763e1f26343SJason M. Bills     {
764e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
765193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
766193ad2faSJason M. Bills         long skip = 0;
767193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
76816428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
769193ad2faSJason M. Bills         {
770193ad2faSJason M. Bills             return;
771193ad2faSJason M. Bills         }
77216428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
773193ad2faSJason M. Bills         {
774193ad2faSJason M. Bills             return;
775193ad2faSJason M. Bills         }
776e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
777e1f26343SJason M. Bills         // it has a duplicate entry for members
778e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
779e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
7800f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
7810f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
782e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
783c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
784e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
785c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
786e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
787e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
788e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
7890f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
7900f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
791e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
792e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
793e1f26343SJason M. Bills 
794e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
795e1f26343SJason M. Bills         // for each entry
796e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
797e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
798e1f26343SJason M. Bills         if (ret < 0)
799e1f26343SJason M. Bills         {
800e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
801f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
802e1f26343SJason M. Bills             return;
803e1f26343SJason M. Bills         }
804e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
805e1f26343SJason M. Bills             journalTmp, sd_journal_close);
806e1f26343SJason M. Bills         journalTmp = nullptr;
807193ad2faSJason M. Bills         uint64_t entryCount = 0;
808e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
809e1f26343SJason M. Bills         {
810193ad2faSJason M. Bills             entryCount++;
811193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
812193ad2faSJason M. Bills             // start) and top (number of entries to display)
813193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
814193ad2faSJason M. Bills             {
815193ad2faSJason M. Bills                 continue;
816193ad2faSJason M. Bills             }
817193ad2faSJason M. Bills 
81816428a1aSJason M. Bills             std::string idStr;
81916428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
820e1f26343SJason M. Bills             {
821e1f26343SJason M. Bills                 continue;
822e1f26343SJason M. Bills             }
823e1f26343SJason M. Bills 
824e1f26343SJason M. Bills             logEntryArray.push_back({});
825c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
826c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
827c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
828e1f26343SJason M. Bills             {
829f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
830e1f26343SJason M. Bills                 return;
831e1f26343SJason M. Bills             }
832e1f26343SJason M. Bills         }
833193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
834193ad2faSJason M. Bills         if (skip + top < entryCount)
835193ad2faSJason M. Bills         {
836193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
837c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
838193ad2faSJason M. Bills                 std::to_string(skip + top);
839193ad2faSJason M. Bills         }
840e1f26343SJason M. Bills     }
841e1f26343SJason M. Bills };
842e1f26343SJason M. Bills 
843c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
844e1f26343SJason M. Bills {
845e1f26343SJason M. Bills   public:
846c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
847c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
848e1f26343SJason M. Bills              std::string())
849e1f26343SJason M. Bills     {
850e1f26343SJason M. Bills         entityPrivileges = {
851e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
852e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
853e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
854e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
855e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
856e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
857e1f26343SJason M. Bills     }
858e1f26343SJason M. Bills 
859e1f26343SJason M. Bills   private:
860e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
861e1f26343SJason M. Bills                const std::vector<std::string> &params) override
862e1f26343SJason M. Bills     {
863e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
864e1f26343SJason M. Bills         if (params.size() != 1)
865e1f26343SJason M. Bills         {
866f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
867e1f26343SJason M. Bills             return;
868e1f26343SJason M. Bills         }
86916428a1aSJason M. Bills         const std::string &entryID = params[0];
870e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
871e1f26343SJason M. Bills         uint64_t ts = 0;
872e1f26343SJason M. Bills         uint16_t index = 0;
87316428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
874e1f26343SJason M. Bills         {
87516428a1aSJason M. Bills             return;
876e1f26343SJason M. Bills         }
877e1f26343SJason M. Bills 
878e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
879e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
880e1f26343SJason M. Bills         if (ret < 0)
881e1f26343SJason M. Bills         {
882e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
883f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
884e1f26343SJason M. Bills             return;
885e1f26343SJason M. Bills         }
886e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
887e1f26343SJason M. Bills             journalTmp, sd_journal_close);
888e1f26343SJason M. Bills         journalTmp = nullptr;
889e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
890e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
891e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
892e1f26343SJason M. Bills         {
893e1f26343SJason M. Bills             sd_journal_next(journal.get());
894e1f26343SJason M. Bills         }
895c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
896c4bf6374SJason M. Bills         std::string idStr;
897c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
898c4bf6374SJason M. Bills         {
899c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
900c4bf6374SJason M. Bills             return;
901c4bf6374SJason M. Bills         }
902c4bf6374SJason M. Bills 
903c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
904e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
905e1f26343SJason M. Bills         {
906f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
907e1f26343SJason M. Bills             return;
908e1f26343SJason M. Bills         }
909e1f26343SJason M. Bills     }
910e1f26343SJason M. Bills };
911e1f26343SJason M. Bills 
912e1f26343SJason M. Bills class CPULogService : public Node
913e1f26343SJason M. Bills {
914e1f26343SJason M. Bills   public:
915e1f26343SJason M. Bills     template <typename CrowApp>
916e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
917e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
9181da66f75SEd Tanous     {
9191da66f75SEd Tanous         entityPrivileges = {
920e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
921e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
922e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
923e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
924e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
925e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9261da66f75SEd Tanous     }
9271da66f75SEd Tanous 
9281da66f75SEd Tanous   private:
9291da66f75SEd Tanous     /**
9301da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9311da66f75SEd Tanous      */
9321da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9331da66f75SEd Tanous                const std::vector<std::string> &params) override
9341da66f75SEd Tanous     {
935e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9361da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
9370f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
9380f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog";
939e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
940e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
941e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
942c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
943e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
944e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
945e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
946e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
947e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
948*cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
949*cd50aa42SJason M. Bills             {"@odata.id",
950*cd50aa42SJason M. Bills              "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"}};
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 {
1049c4d00437SJason M. Bills     nlohmann::json::const_iterator cdIt = cpuLog.find("crashlog_data");
1050c4d00437SJason M. Bills     if (cdIt != cpuLog.end())
10511da66f75SEd Tanous     {
1052c4d00437SJason M. Bills         nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1053c4d00437SJason M. Bills         if (siIt != cdIt->end())
10541da66f75SEd Tanous         {
1055c4d00437SJason M. Bills             nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1056c4d00437SJason M. Bills             if (tsIt != siIt->end())
1057c4d00437SJason M. Bills             {
1058c4d00437SJason M. Bills                 const std::string *logTime =
1059c4d00437SJason M. Bills                     tsIt->get_ptr<const std::string *>();
10601da66f75SEd Tanous                 if (logTime != nullptr)
10611da66f75SEd Tanous                 {
10621da66f75SEd Tanous                     return *logTime;
10631da66f75SEd Tanous                 }
10641da66f75SEd Tanous             }
10651da66f75SEd Tanous         }
1066c4d00437SJason M. Bills     }
10671da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
10681da66f75SEd Tanous 
10691da66f75SEd Tanous     return std::string();
10701da66f75SEd Tanous }
10711da66f75SEd Tanous 
1072e1f26343SJason M. Bills class CPULogEntry : public Node
10731da66f75SEd Tanous {
10741da66f75SEd Tanous   public:
1075e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
10764ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
10771da66f75SEd Tanous              std::string())
10781da66f75SEd Tanous     {
10791da66f75SEd Tanous         entityPrivileges = {
1080e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1081e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1082e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1083e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1084e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1085e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
10861da66f75SEd Tanous     }
10871da66f75SEd Tanous 
10881da66f75SEd Tanous   private:
10891da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
10901da66f75SEd Tanous                const std::vector<std::string> &params) override
10911da66f75SEd Tanous     {
1092e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
10931da66f75SEd Tanous         if (params.size() != 1)
10941da66f75SEd Tanous         {
1095f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
10961da66f75SEd Tanous             return;
10971da66f75SEd Tanous         }
10984ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1099abf2add6SEd Tanous         auto getStoredLogCallback = [asyncResp, logId](
1100abf2add6SEd Tanous                                         const boost::system::error_code ec,
1101abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
11021da66f75SEd Tanous             if (ec)
11031da66f75SEd Tanous             {
1104abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1105f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11061da66f75SEd Tanous                 return;
11071da66f75SEd Tanous             }
1108abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
11091da66f75SEd Tanous             if (log == nullptr)
11101da66f75SEd Tanous             {
1111f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11121da66f75SEd Tanous                 return;
11131da66f75SEd Tanous             }
11141da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
11151da66f75SEd Tanous             if (j.is_discarded())
11161da66f75SEd Tanous             {
1117f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11181da66f75SEd Tanous                 return;
11191da66f75SEd Tanous             }
11201da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1121e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
11221da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
1123abf2add6SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
11241da66f75SEd Tanous                 {"@odata.id",
11254ed77cd5SEd Tanous                  "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
11264ed77cd5SEd Tanous                      std::to_string(logId)},
11271da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
11284ed77cd5SEd Tanous                 {"Id", logId},
11291da66f75SEd Tanous                 {"EntryType", "Oem"},
11301da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
11311da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
11321da66f75SEd Tanous                 {"Created", std::move(t)}};
11331da66f75SEd Tanous         };
11341da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
11354ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
11364ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
11374ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
11381da66f75SEd Tanous     }
11391da66f75SEd Tanous };
11401da66f75SEd Tanous 
1141e1f26343SJason M. Bills class ImmediateCPULog : public Node
11421da66f75SEd Tanous {
11431da66f75SEd Tanous   public:
1144e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
11454ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
1146e1f26343SJason M. Bills                   "CpuLog.Immediate/")
11471da66f75SEd Tanous     {
11481da66f75SEd Tanous         entityPrivileges = {
1149e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1150e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1151e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1152e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1153e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1154e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
11551da66f75SEd Tanous     }
11561da66f75SEd Tanous 
11571da66f75SEd Tanous   private:
11581da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
11591da66f75SEd Tanous                 const std::vector<std::string> &params) override
11601da66f75SEd Tanous     {
1161e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
11621da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
11631da66f75SEd Tanous             immediateLogMatcher;
11641da66f75SEd Tanous 
11651da66f75SEd Tanous         // Only allow one Immediate Log request at a time
11661da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
11671da66f75SEd Tanous         {
1168e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1169f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
11701da66f75SEd Tanous             return;
11711da66f75SEd Tanous         }
11721da66f75SEd Tanous         // Make this static so it survives outside this method
11731da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
11741da66f75SEd Tanous 
11751da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1176e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
11771da66f75SEd Tanous             immediateLogMatcher = nullptr;
11781da66f75SEd Tanous             if (ec)
11791da66f75SEd Tanous             {
11801da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
11811da66f75SEd Tanous                 // completion.
11821da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
11831da66f75SEd Tanous                 {
11841da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
11851da66f75SEd Tanous                 }
11861da66f75SEd Tanous                 return;
11871da66f75SEd Tanous             }
11881da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
11891da66f75SEd Tanous 
1190f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
11911da66f75SEd Tanous         });
11921da66f75SEd Tanous 
1193e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
11941da66f75SEd Tanous                                                sdbusplus::message::message &m) {
11951da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
11961da66f75SEd Tanous             boost::system::error_code ec;
11971da66f75SEd Tanous             timeout.cancel(ec);
11981da66f75SEd Tanous             if (ec)
11991da66f75SEd Tanous             {
12001da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
12011da66f75SEd Tanous             }
12024ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
12031da66f75SEd Tanous             boost::container::flat_map<
1204abf2add6SEd Tanous                 std::string, boost::container::flat_map<
1205abf2add6SEd Tanous                                  std::string, std::variant<std::string>>>
12064ed77cd5SEd Tanous                 interfacesAdded;
12074ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1208abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(
12091b6b96c5SEd 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