xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 39e77504ba0d1e8de273cd54e1f000166bfa6d0d)
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,
42*39e77504SEd Tanous                               const std::string_view &field,
43*39e77504SEd Tanous                               std::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     }
55*39e77504SEd Tanous     contents = std::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,
62*39e77504SEd Tanous                               const std::string_view &field, const int &base,
6316428a1aSJason M. Bills                               int &contents)
6416428a1aSJason M. Bills {
6516428a1aSJason M. Bills     int ret = 0;
66*39e77504SEd Tanous     std::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
97*39e77504SEd Tanous     std::string_view t1(entryTime);
98*39e77504SEd Tanous     std::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     }
104*39e77504SEd Tanous     entryTimestamp = std::string(t1) + ":" + std::string(t2);
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
204*39e77504SEd Tanous     std::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);
211*39e77504SEd Tanous         std::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         {
216*39e77504SEd Tanous             index = std::stoul(std::string(indexStr), &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     {
238*39e77504SEd Tanous         timestamp = std::stoull(std::string(tsStr), &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"}});
297d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
298d53dd41fSJason M. Bills         logServiceArray.push_back(
299d53dd41fSJason M. Bills             {{"@odata.id", "/redfish/v1/Systems/system/LogServices/CpuLog"}});
300d53dd41fSJason M. Bills #endif
301c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
302c4bf6374SJason M. Bills             logServiceArray.size();
303c4bf6374SJason M. Bills     }
304c4bf6374SJason M. Bills };
305c4bf6374SJason M. Bills 
306c4bf6374SJason M. Bills class EventLogService : public Node
307c4bf6374SJason M. Bills {
308c4bf6374SJason M. Bills   public:
309c4bf6374SJason M. Bills     template <typename CrowApp>
310c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
311029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
312c4bf6374SJason M. Bills     {
313c4bf6374SJason M. Bills         entityPrivileges = {
314c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
315c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
316c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
317c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
318c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
319c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
320c4bf6374SJason M. Bills     }
321c4bf6374SJason M. Bills 
322c4bf6374SJason M. Bills   private:
323c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
324c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
325c4bf6374SJason M. Bills     {
326c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
327c4bf6374SJason M. Bills 
328c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
329029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog";
330c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
331c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
332c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
333c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
334c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
335c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
336c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
337c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
338c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
339c4bf6374SJason M. Bills             {"@odata.id",
340029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
341c4bf6374SJason M. Bills     }
342c4bf6374SJason M. Bills };
343c4bf6374SJason M. Bills 
344029573d4SEd Tanous static int fillEventLogEntryJson(const std::string &bmcLogEntryID,
345*39e77504SEd Tanous                                  const std::string_view &messageID,
346c4bf6374SJason M. Bills                                  sd_journal *journal,
347c4bf6374SJason M. Bills                                  nlohmann::json &bmcLogEntryJson)
348c4bf6374SJason M. Bills {
349c4bf6374SJason M. Bills     // Get the Log Entry contents
350c4bf6374SJason M. Bills     int ret = 0;
351c4bf6374SJason M. Bills 
352*39e77504SEd Tanous     std::string_view msg;
353c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
354c4bf6374SJason M. Bills     if (ret < 0)
355c4bf6374SJason M. Bills     {
356c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
357c4bf6374SJason M. Bills         return 1;
358c4bf6374SJason M. Bills     }
359c4bf6374SJason M. Bills 
360c4bf6374SJason M. Bills     // Get the severity from the PRIORITY field
361c4bf6374SJason M. Bills     int severity = 8; // Default to an invalid priority
362c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
363c4bf6374SJason M. Bills     if (ret < 0)
364c4bf6374SJason M. Bills     {
365c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
366c4bf6374SJason M. Bills         return 1;
367c4bf6374SJason M. Bills     }
368c4bf6374SJason M. Bills 
369c4bf6374SJason M. Bills     // Get the MessageArgs from the journal entry by finding all of the
370c4bf6374SJason M. Bills     // REDFISH_MESSAGE_ARG_x fields
371c4bf6374SJason M. Bills     const void *data;
372c4bf6374SJason M. Bills     size_t length;
373c4bf6374SJason M. Bills     std::vector<std::string> messageArgs;
374c4bf6374SJason M. Bills     SD_JOURNAL_FOREACH_DATA(journal, data, length)
375c4bf6374SJason M. Bills     {
376*39e77504SEd Tanous         std::string_view field(static_cast<const char *>(data), length);
377*39e77504SEd Tanous         if (boost::starts_with(field, "REDFISH_MESSAGE_ARG_"))
378c4bf6374SJason M. Bills         {
379c4bf6374SJason M. Bills             // Get the Arg number from the field name
380c4bf6374SJason M. Bills             field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
381c4bf6374SJason M. Bills             if (field.empty())
382c4bf6374SJason M. Bills             {
383c4bf6374SJason M. Bills                 continue;
384c4bf6374SJason M. Bills             }
385c4bf6374SJason M. Bills             int argNum = std::strtoul(field.data(), nullptr, 10);
386c4bf6374SJason M. Bills             if (argNum == 0)
387c4bf6374SJason M. Bills             {
388c4bf6374SJason M. Bills                 continue;
389c4bf6374SJason M. Bills             }
390c4bf6374SJason M. Bills             // Get the Arg value after the "=" character.
391c4bf6374SJason M. Bills             field.remove_prefix(std::min(field.find("=") + 1, field.size()));
392c4bf6374SJason M. Bills             // Make sure we have enough space in messageArgs
393c4bf6374SJason M. Bills             if (argNum > messageArgs.size())
394c4bf6374SJason M. Bills             {
395c4bf6374SJason M. Bills                 messageArgs.resize(argNum);
396c4bf6374SJason M. Bills             }
397*39e77504SEd Tanous             messageArgs[argNum - 1] = std::string(field);
398c4bf6374SJason M. Bills         }
399c4bf6374SJason M. Bills     }
400c4bf6374SJason M. Bills 
401c4bf6374SJason M. Bills     // Get the Created time from the timestamp
402c4bf6374SJason M. Bills     std::string entryTimeStr;
403c4bf6374SJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
404c4bf6374SJason M. Bills     {
405c4bf6374SJason M. Bills         return 1;
406c4bf6374SJason M. Bills     }
407c4bf6374SJason M. Bills 
408c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
409c4bf6374SJason M. Bills     bmcLogEntryJson = {
410c4bf6374SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
411c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
412029573d4SEd Tanous         {"@odata.id",
413029573d4SEd Tanous          "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
414029573d4SEd Tanous              bmcLogEntryID},
415c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
416c4bf6374SJason M. Bills         {"Id", bmcLogEntryID},
417c4bf6374SJason M. Bills         {"Message", msg},
418c4bf6374SJason M. Bills         {"MessageId", messageID},
419c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
420c4bf6374SJason M. Bills         {"EntryType", "Event"},
421c4bf6374SJason M. Bills         {"Severity",
422c4bf6374SJason M. Bills          severity <= 2 ? "Critical"
423c4bf6374SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
424c4bf6374SJason M. Bills         {"Created", std::move(entryTimeStr)}};
425c4bf6374SJason M. Bills     return 0;
426c4bf6374SJason M. Bills }
427c4bf6374SJason M. Bills 
428c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
429c4bf6374SJason M. Bills {
430c4bf6374SJason M. Bills   public:
431c4bf6374SJason M. Bills     template <typename CrowApp>
432c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
433029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
434c4bf6374SJason M. Bills     {
435c4bf6374SJason M. Bills         entityPrivileges = {
436c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
437c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
438c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
439c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
440c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
441c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
442c4bf6374SJason M. Bills     }
443c4bf6374SJason M. Bills 
444c4bf6374SJason M. Bills   private:
445c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
446c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
447c4bf6374SJason M. Bills     {
448c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
449c4bf6374SJason M. Bills         long skip = 0;
450c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
451c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
452c4bf6374SJason M. Bills         {
453c4bf6374SJason M. Bills             return;
454c4bf6374SJason M. Bills         }
455c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
456c4bf6374SJason M. Bills         {
457c4bf6374SJason M. Bills             return;
458c4bf6374SJason M. Bills         }
459c4bf6374SJason M. Bills         // 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"] =
466029573d4SEd Tanous             "/redfish/v1/Systems/system/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
490*39e77504SEd Tanous             std::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();
514029573d4SEd Tanous             if (fillEventLogEntryJson(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,
536029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
537029573d4SEd Tanous              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);
553029573d4SEd Tanous         if (params.size() != 1)
554c4bf6374SJason M. Bills         {
555c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
556c4bf6374SJason M. Bills             return;
557c4bf6374SJason M. Bills         }
558029573d4SEd Tanous         const std::string &entryID = params[0];
559c4bf6374SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
560c4bf6374SJason M. Bills         uint64_t ts = 0;
561c4bf6374SJason M. Bills         uint16_t index = 0;
562c4bf6374SJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
563c4bf6374SJason M. Bills         {
564c4bf6374SJason M. Bills             return;
565c4bf6374SJason M. Bills         }
566c4bf6374SJason M. Bills 
567c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
568c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
569c4bf6374SJason M. Bills         if (ret < 0)
570c4bf6374SJason M. Bills         {
571c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
572c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
573c4bf6374SJason M. Bills             return;
574c4bf6374SJason M. Bills         }
575c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
576c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
577c4bf6374SJason M. Bills         journalTmp = nullptr;
578c4bf6374SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
579c4bf6374SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
580c4bf6374SJason M. Bills         for (int i = 0; i <= index; i++)
581c4bf6374SJason M. Bills         {
582c4bf6374SJason M. Bills             sd_journal_next(journal.get());
583c4bf6374SJason M. Bills         }
584c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
585c4bf6374SJason M. Bills         std::string idStr;
586c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
587c4bf6374SJason M. Bills         {
588c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
589c4bf6374SJason M. Bills             return;
590c4bf6374SJason M. Bills         }
591c4bf6374SJason M. Bills 
592cd50aa42SJason M. Bills         // only use journal entries that contain a REDFISH_MESSAGE_ID field
593*39e77504SEd Tanous         std::string_view messageID;
594c4bf6374SJason M. Bills         ret =
595c4bf6374SJason M. Bills             getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
596c4bf6374SJason M. Bills         if (ret < 0)
597c4bf6374SJason M. Bills         {
598029573d4SEd Tanous             messages::resourceNotFound(asyncResp->res, "LogEntry", "system");
599c4bf6374SJason M. Bills             return;
600c4bf6374SJason M. Bills         }
601c4bf6374SJason M. Bills 
602029573d4SEd Tanous         if (fillEventLogEntryJson(entryID, messageID, journal.get(),
603c4bf6374SJason M. Bills                                   asyncResp->res.jsonValue) != 0)
604c4bf6374SJason M. Bills         {
605c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
606c4bf6374SJason M. Bills             return;
607c4bf6374SJason M. Bills         }
608c4bf6374SJason M. Bills     }
609c4bf6374SJason M. Bills };
610c4bf6374SJason M. Bills 
611c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
612c4bf6374SJason M. Bills {
613c4bf6374SJason M. Bills   public:
614c4bf6374SJason M. Bills     template <typename CrowApp>
615c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
6164ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
6171da66f75SEd Tanous     {
6181da66f75SEd Tanous         entityPrivileges = {
619e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
620e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
621e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
622e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
623e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
624e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6251da66f75SEd Tanous     }
6261da66f75SEd Tanous 
6271da66f75SEd Tanous   private:
6281da66f75SEd Tanous     /**
6291da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
6301da66f75SEd Tanous      */
6311da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
6321da66f75SEd Tanous                const std::vector<std::string> &params) override
6331da66f75SEd Tanous     {
634e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6351da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
6361da66f75SEd Tanous         // it has a duplicate entry for members
637e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
6381da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
639e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
640c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
641e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
642e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
643e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
644e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
6451da66f75SEd Tanous             "Collection of LogServices for this Manager";
646c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
647c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
648c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
649c4bf6374SJason M. Bills         logServiceArray.push_back(
650c4bf6374SJason M. Bills             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
651c4bf6374SJason M. Bills #endif
652e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
653c4bf6374SJason M. Bills             logServiceArray.size();
6541da66f75SEd Tanous     }
6551da66f75SEd Tanous };
6561da66f75SEd Tanous 
657c4bf6374SJason M. Bills class BMCJournalLogService : public Node
6581da66f75SEd Tanous {
6591da66f75SEd Tanous   public:
6601da66f75SEd Tanous     template <typename CrowApp>
661c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
662c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
663e1f26343SJason M. Bills     {
664e1f26343SJason M. Bills         entityPrivileges = {
665e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
666e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
667e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
668e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
669e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
670e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
671e1f26343SJason M. Bills     }
672e1f26343SJason M. Bills 
673e1f26343SJason M. Bills   private:
674e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
675e1f26343SJason M. Bills                const std::vector<std::string> &params) override
676e1f26343SJason M. Bills     {
677e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
678e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
679e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
6800f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
6810f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
682e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
683e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
684c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
685c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
686c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
687e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
688cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
689cd50aa42SJason M. Bills             {"@odata.id",
690cd50aa42SJason M. Bills              "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}};
691e1f26343SJason M. Bills     }
692e1f26343SJason M. Bills };
693e1f26343SJason M. Bills 
694c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
695e1f26343SJason M. Bills                                       sd_journal *journal,
696c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
697e1f26343SJason M. Bills {
698e1f26343SJason M. Bills     // Get the Log Entry contents
699e1f26343SJason M. Bills     int ret = 0;
700e1f26343SJason M. Bills 
701*39e77504SEd Tanous     std::string_view msg;
70216428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
703e1f26343SJason M. Bills     if (ret < 0)
704e1f26343SJason M. Bills     {
705e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
706e1f26343SJason M. Bills         return 1;
707e1f26343SJason M. Bills     }
708e1f26343SJason M. Bills 
709e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
710e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
71116428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
712e1f26343SJason M. Bills     if (ret < 0)
713e1f26343SJason M. Bills     {
714e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
715e1f26343SJason M. Bills         return 1;
716e1f26343SJason M. Bills     }
717e1f26343SJason M. Bills 
718e1f26343SJason M. Bills     // Get the Created time from the timestamp
71916428a1aSJason M. Bills     std::string entryTimeStr;
72016428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
721e1f26343SJason M. Bills     {
72216428a1aSJason M. Bills         return 1;
723e1f26343SJason M. Bills     }
724e1f26343SJason M. Bills 
725e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
726c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
727e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
728e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
729c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
730c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
731e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
732c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
73316428a1aSJason M. Bills         {"Message", msg},
734e1f26343SJason M. Bills         {"EntryType", "Oem"},
735e1f26343SJason M. Bills         {"Severity",
736e1f26343SJason M. Bills          severity <= 2 ? "Critical"
737e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
738e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
739e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
740e1f26343SJason M. Bills     return 0;
741e1f26343SJason M. Bills }
742e1f26343SJason M. Bills 
743c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
744e1f26343SJason M. Bills {
745e1f26343SJason M. Bills   public:
746e1f26343SJason M. Bills     template <typename CrowApp>
747c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
748c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
749e1f26343SJason M. Bills     {
750e1f26343SJason M. Bills         entityPrivileges = {
751e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
752e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
753e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
754e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
755e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
756e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
757e1f26343SJason M. Bills     }
758e1f26343SJason M. Bills 
759e1f26343SJason M. Bills   private:
760e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
761e1f26343SJason M. Bills                const std::vector<std::string> &params) override
762e1f26343SJason M. Bills     {
763e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
764193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
765193ad2faSJason M. Bills         long skip = 0;
766193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
76716428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
768193ad2faSJason M. Bills         {
769193ad2faSJason M. Bills             return;
770193ad2faSJason M. Bills         }
77116428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
772193ad2faSJason M. Bills         {
773193ad2faSJason M. Bills             return;
774193ad2faSJason M. Bills         }
775e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
776e1f26343SJason M. Bills         // it has a duplicate entry for members
777e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
778e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
7790f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
7800f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
781e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
782c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
783e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
784c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
785e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
786e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
787e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
7880f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
7890f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
790e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
791e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
792e1f26343SJason M. Bills 
793e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
794e1f26343SJason M. Bills         // for each entry
795e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
796e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
797e1f26343SJason M. Bills         if (ret < 0)
798e1f26343SJason M. Bills         {
799e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
800f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
801e1f26343SJason M. Bills             return;
802e1f26343SJason M. Bills         }
803e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
804e1f26343SJason M. Bills             journalTmp, sd_journal_close);
805e1f26343SJason M. Bills         journalTmp = nullptr;
806193ad2faSJason M. Bills         uint64_t entryCount = 0;
807e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
808e1f26343SJason M. Bills         {
809193ad2faSJason M. Bills             entryCount++;
810193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
811193ad2faSJason M. Bills             // start) and top (number of entries to display)
812193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
813193ad2faSJason M. Bills             {
814193ad2faSJason M. Bills                 continue;
815193ad2faSJason M. Bills             }
816193ad2faSJason M. Bills 
81716428a1aSJason M. Bills             std::string idStr;
81816428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
819e1f26343SJason M. Bills             {
820e1f26343SJason M. Bills                 continue;
821e1f26343SJason M. Bills             }
822e1f26343SJason M. Bills 
823e1f26343SJason M. Bills             logEntryArray.push_back({});
824c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
825c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
826c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
827e1f26343SJason M. Bills             {
828f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
829e1f26343SJason M. Bills                 return;
830e1f26343SJason M. Bills             }
831e1f26343SJason M. Bills         }
832193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
833193ad2faSJason M. Bills         if (skip + top < entryCount)
834193ad2faSJason M. Bills         {
835193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
836c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
837193ad2faSJason M. Bills                 std::to_string(skip + top);
838193ad2faSJason M. Bills         }
839e1f26343SJason M. Bills     }
840e1f26343SJason M. Bills };
841e1f26343SJason M. Bills 
842c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
843e1f26343SJason M. Bills {
844e1f26343SJason M. Bills   public:
845c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
846c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
847e1f26343SJason M. Bills              std::string())
848e1f26343SJason M. Bills     {
849e1f26343SJason M. Bills         entityPrivileges = {
850e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
851e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
852e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
853e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
854e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
855e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
856e1f26343SJason M. Bills     }
857e1f26343SJason M. Bills 
858e1f26343SJason M. Bills   private:
859e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
860e1f26343SJason M. Bills                const std::vector<std::string> &params) override
861e1f26343SJason M. Bills     {
862e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
863e1f26343SJason M. Bills         if (params.size() != 1)
864e1f26343SJason M. Bills         {
865f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
866e1f26343SJason M. Bills             return;
867e1f26343SJason M. Bills         }
86816428a1aSJason M. Bills         const std::string &entryID = params[0];
869e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
870e1f26343SJason M. Bills         uint64_t ts = 0;
871e1f26343SJason M. Bills         uint16_t index = 0;
87216428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
873e1f26343SJason M. Bills         {
87416428a1aSJason M. Bills             return;
875e1f26343SJason M. Bills         }
876e1f26343SJason M. Bills 
877e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
878e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
879e1f26343SJason M. Bills         if (ret < 0)
880e1f26343SJason M. Bills         {
881e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
882f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
883e1f26343SJason M. Bills             return;
884e1f26343SJason M. Bills         }
885e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
886e1f26343SJason M. Bills             journalTmp, sd_journal_close);
887e1f26343SJason M. Bills         journalTmp = nullptr;
888e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
889e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
890e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
891e1f26343SJason M. Bills         {
892e1f26343SJason M. Bills             sd_journal_next(journal.get());
893e1f26343SJason M. Bills         }
894c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
895c4bf6374SJason M. Bills         std::string idStr;
896c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
897c4bf6374SJason M. Bills         {
898c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
899c4bf6374SJason M. Bills             return;
900c4bf6374SJason M. Bills         }
901c4bf6374SJason M. Bills 
902c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
903e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
904e1f26343SJason M. Bills         {
905f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
906e1f26343SJason M. Bills             return;
907e1f26343SJason M. Bills         }
908e1f26343SJason M. Bills     }
909e1f26343SJason M. Bills };
910e1f26343SJason M. Bills 
911e1f26343SJason M. Bills class CPULogService : public Node
912e1f26343SJason M. Bills {
913e1f26343SJason M. Bills   public:
914e1f26343SJason M. Bills     template <typename CrowApp>
915e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
916d53dd41fSJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/")
9171da66f75SEd Tanous     {
9181da66f75SEd Tanous         entityPrivileges = {
919e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
920e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
921e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
922e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
923e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
924e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9251da66f75SEd Tanous     }
9261da66f75SEd Tanous 
9271da66f75SEd Tanous   private:
9281da66f75SEd Tanous     /**
9291da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9301da66f75SEd Tanous      */
9311da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9321da66f75SEd Tanous                const std::vector<std::string> &params) override
9331da66f75SEd Tanous     {
934e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9351da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
9360f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
937d53dd41fSJason M. Bills             "/redfish/v1/Systems/system/LogServices/CpuLog";
938e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
939e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
940e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
941c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
942e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
943e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
944e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
945e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
946e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
947cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
948cd50aa42SJason M. Bills             {"@odata.id",
949cd50aa42SJason M. Bills              "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"}};
950e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
9511da66f75SEd Tanous             {"Oem",
9521da66f75SEd Tanous              {{"#CpuLog.Immediate",
953d53dd41fSJason M. Bills                {{"target", "/redfish/v1/Systems/system/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",
959d53dd41fSJason M. Bills              {{"target", "/redfish/v1/Systems/system/LogServices/CpuLog/"
960d53dd41fSJason M. Bills                          "Actions/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) :
970d53dd41fSJason M. Bills         Node(app, "/redfish/v1/Systems/system/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"] =
1008d53dd41fSJason M. Bills                 "/redfish/v1/Systems/system/LogServices/CpuLog/Entries";
1009e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1010d53dd41fSJason M. Bills                 "/redfish/v1/"
1011d53dd41fSJason M. Bills                 "$metadata#LogEntryCollection.LogEntryCollection";
1012e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
1013e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1014e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
1015e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1016e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
10171da66f75SEd Tanous             for (const std::string &objpath : resp)
10181da66f75SEd Tanous             {
10191da66f75SEd Tanous                 // Don't list the immediate log
10204ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
10211da66f75SEd Tanous                 {
10221da66f75SEd Tanous                     continue;
10231da66f75SEd Tanous                 }
10244ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
10254ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
10261da66f75SEd Tanous                 {
1027e1f26343SJason M. Bills                     logEntryArray.push_back(
1028d53dd41fSJason M. Bills                         {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1029e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
10304ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
10311da66f75SEd Tanous                 }
10321da66f75SEd Tanous             }
1033e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1034e1f26343SJason M. Bills                 logEntryArray.size();
10351da66f75SEd Tanous         };
10361da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
10371da66f75SEd Tanous             std::move(getLogEntriesCallback),
10381da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
10391da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
10401da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
10414ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
10421da66f75SEd Tanous     }
10431da66f75SEd Tanous };
10441da66f75SEd Tanous 
10451da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
10461da66f75SEd Tanous {
1047c4d00437SJason M. Bills     nlohmann::json::const_iterator cdIt = cpuLog.find("crashlog_data");
1048c4d00437SJason M. Bills     if (cdIt != cpuLog.end())
10491da66f75SEd Tanous     {
1050c4d00437SJason M. Bills         nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1051c4d00437SJason M. Bills         if (siIt != cdIt->end())
10521da66f75SEd Tanous         {
1053c4d00437SJason M. Bills             nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1054c4d00437SJason M. Bills             if (tsIt != siIt->end())
1055c4d00437SJason M. Bills             {
1056c4d00437SJason M. Bills                 const std::string *logTime =
1057c4d00437SJason M. Bills                     tsIt->get_ptr<const std::string *>();
10581da66f75SEd Tanous                 if (logTime != nullptr)
10591da66f75SEd Tanous                 {
10601da66f75SEd Tanous                     return *logTime;
10611da66f75SEd Tanous                 }
10621da66f75SEd Tanous             }
10631da66f75SEd Tanous         }
1064c4d00437SJason M. Bills     }
10651da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
10661da66f75SEd Tanous 
10671da66f75SEd Tanous     return std::string();
10681da66f75SEd Tanous }
10691da66f75SEd Tanous 
1070e1f26343SJason M. Bills class CPULogEntry : public Node
10711da66f75SEd Tanous {
10721da66f75SEd Tanous   public:
1073e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
1074d53dd41fSJason M. Bills         Node(app,
1075d53dd41fSJason M. Bills              "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/<str>/",
10761da66f75SEd Tanous              std::string())
10771da66f75SEd Tanous     {
10781da66f75SEd Tanous         entityPrivileges = {
1079e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1080e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1081e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1082e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1083e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1084e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
10851da66f75SEd Tanous     }
10861da66f75SEd Tanous 
10871da66f75SEd Tanous   private:
10881da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
10891da66f75SEd Tanous                const std::vector<std::string> &params) override
10901da66f75SEd Tanous     {
1091e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
10921da66f75SEd Tanous         if (params.size() != 1)
10931da66f75SEd Tanous         {
1094f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
10951da66f75SEd Tanous             return;
10961da66f75SEd Tanous         }
10974ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1098abf2add6SEd Tanous         auto getStoredLogCallback = [asyncResp, logId](
1099abf2add6SEd Tanous                                         const boost::system::error_code ec,
1100abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
11011da66f75SEd Tanous             if (ec)
11021da66f75SEd Tanous             {
1103abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1104f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11051da66f75SEd Tanous                 return;
11061da66f75SEd Tanous             }
1107abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
11081da66f75SEd Tanous             if (log == nullptr)
11091da66f75SEd Tanous             {
1110f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11111da66f75SEd Tanous                 return;
11121da66f75SEd Tanous             }
11131da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
11141da66f75SEd Tanous             if (j.is_discarded())
11151da66f75SEd Tanous             {
1116f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
11171da66f75SEd Tanous                 return;
11181da66f75SEd Tanous             }
11191da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1120e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
11211da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
1122abf2add6SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
11231da66f75SEd Tanous                 {"@odata.id",
1124d53dd41fSJason M. Bills                  "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/" +
11254ed77cd5SEd Tanous                      std::to_string(logId)},
11261da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
11274ed77cd5SEd Tanous                 {"Id", logId},
11281da66f75SEd Tanous                 {"EntryType", "Oem"},
11291da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
11301da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
11311da66f75SEd Tanous                 {"Created", std::move(t)}};
11321da66f75SEd Tanous         };
11331da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
11344ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
11354ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
11364ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
11371da66f75SEd Tanous     }
11381da66f75SEd Tanous };
11391da66f75SEd Tanous 
1140e1f26343SJason M. Bills class ImmediateCPULog : public Node
11411da66f75SEd Tanous {
11421da66f75SEd Tanous   public:
1143e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
1144d53dd41fSJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Actions/Oem/"
1145e1f26343SJason M. Bills                   "CpuLog.Immediate/")
11461da66f75SEd Tanous     {
11471da66f75SEd Tanous         entityPrivileges = {
1148e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1149e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1150e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1151e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1152e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1153e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
11541da66f75SEd Tanous     }
11551da66f75SEd Tanous 
11561da66f75SEd Tanous   private:
11571da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
11581da66f75SEd Tanous                 const std::vector<std::string> &params) override
11591da66f75SEd Tanous     {
1160e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
11611da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
11621da66f75SEd Tanous             immediateLogMatcher;
11631da66f75SEd Tanous 
11641da66f75SEd Tanous         // Only allow one Immediate Log request at a time
11651da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
11661da66f75SEd Tanous         {
1167e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1168f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
11691da66f75SEd Tanous             return;
11701da66f75SEd Tanous         }
11711da66f75SEd Tanous         // Make this static so it survives outside this method
11721da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
11731da66f75SEd Tanous 
11741da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1175e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
11761da66f75SEd Tanous             immediateLogMatcher = nullptr;
11771da66f75SEd Tanous             if (ec)
11781da66f75SEd Tanous             {
11791da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
11801da66f75SEd Tanous                 // completion.
11811da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
11821da66f75SEd Tanous                 {
11831da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
11841da66f75SEd Tanous                 }
11851da66f75SEd Tanous                 return;
11861da66f75SEd Tanous             }
11871da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
11881da66f75SEd Tanous 
1189f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
11901da66f75SEd Tanous         });
11911da66f75SEd Tanous 
1192e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
11931da66f75SEd Tanous                                                sdbusplus::message::message &m) {
11941da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
11951da66f75SEd Tanous             boost::system::error_code ec;
11961da66f75SEd Tanous             timeout.cancel(ec);
11971da66f75SEd Tanous             if (ec)
11981da66f75SEd Tanous             {
11991da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
12001da66f75SEd Tanous             }
12014ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
12021da66f75SEd Tanous             boost::container::flat_map<
1203abf2add6SEd Tanous                 std::string, boost::container::flat_map<
1204abf2add6SEd Tanous                                  std::string, std::variant<std::string>>>
12054ed77cd5SEd Tanous                 interfacesAdded;
12064ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1207abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(
12081b6b96c5SEd Tanous                 &interfacesAdded[cpuLogInterface]["Log"]);
12091da66f75SEd Tanous             if (log == nullptr)
12101da66f75SEd Tanous             {
1211f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12121da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
12131da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
12141da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
12151da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
12161da66f75SEd Tanous                 // be the last thing done.
12171da66f75SEd Tanous                 immediateLogMatcher = nullptr;
12181da66f75SEd Tanous                 return;
12191da66f75SEd Tanous             }
12201da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
12211da66f75SEd Tanous             if (j.is_discarded())
12221da66f75SEd Tanous             {
1223f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12241da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
12251da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
12261da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
12271da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
12281da66f75SEd Tanous                 // be the last thing done.
12291da66f75SEd Tanous                 immediateLogMatcher = nullptr;
12301da66f75SEd Tanous                 return;
12311da66f75SEd Tanous             }
12321da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1233e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
12341da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
12351da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
12361da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
12371da66f75SEd Tanous                 {"EntryType", "Oem"},
12381da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
12391da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
12401da66f75SEd Tanous                 {"Created", std::move(t)}};
12411da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
12421da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
12431da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
12441da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
12451da66f75SEd Tanous             // thing done.
12461da66f75SEd Tanous             immediateLogMatcher = nullptr;
12471da66f75SEd Tanous         };
12481da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
12491da66f75SEd Tanous             *crow::connections::systemBus,
12501da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
12514ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
12521da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
12531da66f75SEd Tanous 
12541da66f75SEd Tanous         auto generateImmediateLogCallback =
1255e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
12561da66f75SEd Tanous                         const std::string &resp) {
12571da66f75SEd Tanous                 if (ec)
12581da66f75SEd Tanous                 {
12591da66f75SEd Tanous                     if (ec.value() ==
12601da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
12611da66f75SEd Tanous                     {
1262f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
12631da66f75SEd Tanous                     }
12641da66f75SEd Tanous                     else
12651da66f75SEd Tanous                     {
1266f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
12671da66f75SEd Tanous                     }
12681da66f75SEd Tanous                     boost::system::error_code timeoutec;
12691da66f75SEd Tanous                     timeout.cancel(timeoutec);
12701da66f75SEd Tanous                     if (timeoutec)
12711da66f75SEd Tanous                     {
12721da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
12731da66f75SEd Tanous                                          << timeoutec;
12741da66f75SEd Tanous                     }
12751da66f75SEd Tanous                     immediateLogMatcher = nullptr;
12761da66f75SEd Tanous                     return;
12771da66f75SEd Tanous                 }
12781da66f75SEd Tanous             };
12791da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
12804ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
12814ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
12821da66f75SEd Tanous     }
12831da66f75SEd Tanous };
12841da66f75SEd Tanous 
1285e1f26343SJason M. Bills class SendRawPECI : public Node
12861da66f75SEd Tanous {
12871da66f75SEd Tanous   public:
1288e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
1289d53dd41fSJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Actions/Oem/"
1290e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
12911da66f75SEd Tanous     {
12921da66f75SEd Tanous         entityPrivileges = {
12931da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
12941da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
12951da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
12961da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
12971da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
12981da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
12991da66f75SEd Tanous     }
13001da66f75SEd Tanous 
13011da66f75SEd Tanous   private:
13021da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
13031da66f75SEd Tanous                 const std::vector<std::string> &params) override
13041da66f75SEd Tanous     {
1305e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1306b1556427SEd Tanous         uint8_t clientAddress = 0;
1307b1556427SEd Tanous         uint8_t readLength = 0;
13081da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
1309b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1310b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
1311b1556427SEd Tanous                                  peciCommand))
13121da66f75SEd Tanous         {
13131da66f75SEd Tanous             return;
13141da66f75SEd Tanous         }
1315b1556427SEd Tanous 
13161da66f75SEd Tanous         // Callback to return the Raw PECI response
1317e1f26343SJason M. Bills         auto sendRawPECICallback =
1318e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
13191da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
13201da66f75SEd Tanous                 if (ec)
13211da66f75SEd Tanous                 {
13221da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
13231da66f75SEd Tanous                                      << ec.message();
1324f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
13251da66f75SEd Tanous                     return;
13261da66f75SEd Tanous                 }
1327e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
13281da66f75SEd Tanous                                             {"PECIResponse", resp}};
13291da66f75SEd Tanous             };
13301da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
13311da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1332e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
1333e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
13344ed77cd5SEd Tanous             peciCommand);
13351da66f75SEd Tanous     }
13361da66f75SEd Tanous };
13371da66f75SEd Tanous 
13381da66f75SEd Tanous } // namespace redfish
1339