xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision c4bf6374242b5a9b98fa92a7ecfbc586724a9ca2)
11da66f75SEd Tanous /*
21da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation
31da66f75SEd Tanous //
41da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License");
51da66f75SEd Tanous // you may not use this file except in compliance with the License.
61da66f75SEd Tanous // You may obtain a copy of the License at
71da66f75SEd Tanous //
81da66f75SEd Tanous //      http://www.apache.org/licenses/LICENSE-2.0
91da66f75SEd Tanous //
101da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software
111da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS,
121da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131da66f75SEd Tanous // See the License for the specific language governing permissions and
141da66f75SEd Tanous // limitations under the License.
151da66f75SEd Tanous */
161da66f75SEd Tanous #pragma once
171da66f75SEd Tanous 
181da66f75SEd Tanous #include "node.hpp"
191da66f75SEd Tanous 
20e1f26343SJason M. Bills #include <systemd/sd-journal.h>
21e1f26343SJason M. Bills 
221da66f75SEd Tanous #include <boost/container/flat_map.hpp>
23e1f26343SJason M. Bills #include <boost/utility/string_view.hpp>
241da66f75SEd Tanous #include <experimental/filesystem>
251da66f75SEd Tanous 
261da66f75SEd Tanous namespace redfish
271da66f75SEd Tanous {
281da66f75SEd Tanous 
294ed77cd5SEd Tanous constexpr char const *cpuLogObject = "com.intel.CpuDebugLog";
304ed77cd5SEd Tanous constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog";
314ed77cd5SEd Tanous constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate";
324ed77cd5SEd Tanous constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
334ed77cd5SEd Tanous constexpr char const *cpuLogImmediateInterface =
341da66f75SEd Tanous     "com.intel.CpuDebugLog.Immediate";
35e1f26343SJason M. Bills constexpr char const *cpuLogRawPECIInterface =
361da66f75SEd Tanous     "com.intel.CpuDebugLog.SendRawPeci";
371da66f75SEd Tanous 
381da66f75SEd Tanous namespace fs = std::experimental::filesystem;
391da66f75SEd Tanous 
4016428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
4116428a1aSJason M. Bills                               const boost::string_view &field,
4216428a1aSJason M. Bills                               boost::string_view &contents)
4316428a1aSJason M. Bills {
4416428a1aSJason M. Bills     const char *data = nullptr;
4516428a1aSJason M. Bills     size_t length = 0;
4616428a1aSJason M. Bills     int ret = 0;
4716428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
4816428a1aSJason M. Bills     ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
4916428a1aSJason M. Bills                               &length);
5016428a1aSJason M. Bills     if (ret < 0)
5116428a1aSJason M. Bills     {
5216428a1aSJason M. Bills         return ret;
5316428a1aSJason M. Bills     }
5416428a1aSJason M. Bills     contents = boost::string_view(data, length);
5516428a1aSJason M. Bills     // Only use the content after the "=" character.
5616428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
5716428a1aSJason M. Bills     return ret;
5816428a1aSJason M. Bills }
5916428a1aSJason M. Bills 
6016428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
6116428a1aSJason M. Bills                               const boost::string_view &field, const int &base,
6216428a1aSJason M. Bills                               int &contents)
6316428a1aSJason M. Bills {
6416428a1aSJason M. Bills     int ret = 0;
6516428a1aSJason M. Bills     boost::string_view metadata;
6616428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
6716428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
6816428a1aSJason M. Bills     if (ret < 0)
6916428a1aSJason M. Bills     {
7016428a1aSJason M. Bills         return ret;
7116428a1aSJason M. Bills     }
7216428a1aSJason M. Bills     contents = strtol(metadata.data(), nullptr, base);
7316428a1aSJason M. Bills     return ret;
7416428a1aSJason M. Bills }
7516428a1aSJason M. Bills 
7616428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
7716428a1aSJason M. Bills {
7816428a1aSJason M. Bills     int ret = 0;
7916428a1aSJason M. Bills     uint64_t timestamp = 0;
8016428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
8116428a1aSJason M. Bills     if (ret < 0)
8216428a1aSJason M. Bills     {
8316428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
8416428a1aSJason M. Bills                          << strerror(-ret);
8516428a1aSJason M. Bills         return false;
8616428a1aSJason M. Bills     }
8716428a1aSJason M. Bills     time_t t =
8816428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
8916428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
9016428a1aSJason M. Bills     char entryTime[64] = {};
9116428a1aSJason M. Bills     if (NULL != loctime)
9216428a1aSJason M. Bills     {
9316428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
9416428a1aSJason M. Bills     }
9516428a1aSJason M. Bills     // Insert the ':' into the timezone
9616428a1aSJason M. Bills     boost::string_view t1(entryTime);
9716428a1aSJason M. Bills     boost::string_view t2(entryTime);
9816428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
9916428a1aSJason M. Bills     {
10016428a1aSJason M. Bills         t1.remove_suffix(2);
10116428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
10216428a1aSJason M. Bills     }
10316428a1aSJason M. Bills     entryTimestamp = t1.to_string() + ":" + t2.to_string();
10416428a1aSJason M. Bills     return true;
10516428a1aSJason M. Bills }
10616428a1aSJason M. Bills 
10716428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
10816428a1aSJason M. Bills                          long &skip)
10916428a1aSJason M. Bills {
11016428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
11116428a1aSJason M. Bills     if (skipParam != nullptr)
11216428a1aSJason M. Bills     {
11316428a1aSJason M. Bills         char *ptr = nullptr;
11416428a1aSJason M. Bills         skip = std::strtol(skipParam, &ptr, 10);
11516428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
11616428a1aSJason M. Bills         {
11716428a1aSJason M. Bills 
11816428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
11916428a1aSJason M. Bills                                                    "$skip");
12016428a1aSJason M. Bills             return false;
12116428a1aSJason M. Bills         }
12216428a1aSJason M. Bills         if (skip < 0)
12316428a1aSJason M. Bills         {
12416428a1aSJason M. Bills 
12516428a1aSJason M. Bills             messages::queryParameterOutOfRange(res, std::to_string(skip),
12616428a1aSJason M. Bills                                                "$skip", "greater than 0");
12716428a1aSJason M. Bills             return false;
12816428a1aSJason M. Bills         }
12916428a1aSJason M. Bills     }
13016428a1aSJason M. Bills     return true;
13116428a1aSJason M. Bills }
13216428a1aSJason M. Bills 
13316428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000;
13416428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
13516428a1aSJason M. Bills                         long &top)
13616428a1aSJason M. Bills {
13716428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
13816428a1aSJason M. Bills     if (topParam != nullptr)
13916428a1aSJason M. Bills     {
14016428a1aSJason M. Bills         char *ptr = nullptr;
14116428a1aSJason M. Bills         top = std::strtol(topParam, &ptr, 10);
14216428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
14316428a1aSJason M. Bills         {
14416428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
14516428a1aSJason M. Bills                                                    "$top");
14616428a1aSJason M. Bills             return false;
14716428a1aSJason M. Bills         }
14816428a1aSJason M. Bills         if (top < 1 || top > maxEntriesPerPage)
14916428a1aSJason M. Bills         {
15016428a1aSJason M. Bills 
15116428a1aSJason M. Bills             messages::queryParameterOutOfRange(
15216428a1aSJason M. Bills                 res, std::to_string(top), "$top",
15316428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
15416428a1aSJason M. Bills             return false;
15516428a1aSJason M. Bills         }
15616428a1aSJason M. Bills     }
15716428a1aSJason M. Bills     return true;
15816428a1aSJason M. Bills }
15916428a1aSJason M. Bills 
16016428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
16116428a1aSJason M. Bills {
16216428a1aSJason M. Bills     int ret = 0;
16316428a1aSJason M. Bills     static uint64_t prevTs = 0;
16416428a1aSJason M. Bills     static int index = 0;
16516428a1aSJason M. Bills     // Get the entry timestamp
16616428a1aSJason M. Bills     uint64_t curTs = 0;
16716428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &curTs);
16816428a1aSJason M. Bills     if (ret < 0)
16916428a1aSJason M. Bills     {
17016428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
17116428a1aSJason M. Bills                          << strerror(-ret);
17216428a1aSJason M. Bills         return false;
17316428a1aSJason M. Bills     }
17416428a1aSJason M. Bills     // If the timestamp isn't unique, increment the index
17516428a1aSJason M. Bills     if (curTs == prevTs)
17616428a1aSJason M. Bills     {
17716428a1aSJason M. Bills         index++;
17816428a1aSJason M. Bills     }
17916428a1aSJason M. Bills     else
18016428a1aSJason M. Bills     {
18116428a1aSJason M. Bills         // Otherwise, reset it
18216428a1aSJason M. Bills         index = 0;
18316428a1aSJason M. Bills     }
18416428a1aSJason M. Bills     // Save the timestamp
18516428a1aSJason M. Bills     prevTs = curTs;
18616428a1aSJason M. Bills 
18716428a1aSJason M. Bills     entryID = std::to_string(curTs);
18816428a1aSJason M. Bills     if (index > 0)
18916428a1aSJason M. Bills     {
19016428a1aSJason M. Bills         entryID += "_" + std::to_string(index);
19116428a1aSJason M. Bills     }
19216428a1aSJason M. Bills     return true;
19316428a1aSJason M. Bills }
19416428a1aSJason M. Bills 
19516428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
19616428a1aSJason M. Bills                                uint64_t &timestamp, uint16_t &index)
19716428a1aSJason M. Bills {
19816428a1aSJason M. Bills     if (entryID.empty())
19916428a1aSJason M. Bills     {
20016428a1aSJason M. Bills         return false;
20116428a1aSJason M. Bills     }
20216428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
20316428a1aSJason M. Bills     boost::string_view tsStr(entryID);
20416428a1aSJason M. Bills 
20516428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
20616428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
20716428a1aSJason M. Bills     {
20816428a1aSJason M. Bills         // Timestamp has an index
20916428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
21016428a1aSJason M. Bills         boost::string_view indexStr(entryID);
21116428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
21216428a1aSJason M. Bills         std::size_t pos;
21316428a1aSJason M. Bills         try
21416428a1aSJason M. Bills         {
21516428a1aSJason M. Bills             index = std::stoul(indexStr.to_string(), &pos);
21616428a1aSJason M. Bills         }
21716428a1aSJason M. Bills         catch (std::invalid_argument)
21816428a1aSJason M. Bills         {
21916428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
22016428a1aSJason M. Bills             return false;
22116428a1aSJason M. Bills         }
22216428a1aSJason M. Bills         catch (std::out_of_range)
22316428a1aSJason M. Bills         {
22416428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
22516428a1aSJason M. Bills             return false;
22616428a1aSJason M. Bills         }
22716428a1aSJason M. Bills         if (pos != indexStr.size())
22816428a1aSJason M. Bills         {
22916428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
23016428a1aSJason M. Bills             return false;
23116428a1aSJason M. Bills         }
23216428a1aSJason M. Bills     }
23316428a1aSJason M. Bills     // Timestamp has no index
23416428a1aSJason M. Bills     std::size_t pos;
23516428a1aSJason M. Bills     try
23616428a1aSJason M. Bills     {
23716428a1aSJason M. Bills         timestamp = std::stoull(tsStr.to_string(), &pos);
23816428a1aSJason M. Bills     }
23916428a1aSJason M. Bills     catch (std::invalid_argument)
24016428a1aSJason M. Bills     {
24116428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
24216428a1aSJason M. Bills         return false;
24316428a1aSJason M. Bills     }
24416428a1aSJason M. Bills     catch (std::out_of_range)
24516428a1aSJason M. Bills     {
24616428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
24716428a1aSJason M. Bills         return false;
24816428a1aSJason M. Bills     }
24916428a1aSJason M. Bills     if (pos != tsStr.size())
25016428a1aSJason M. Bills     {
25116428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
25216428a1aSJason M. Bills         return false;
25316428a1aSJason M. Bills     }
25416428a1aSJason M. Bills     return true;
25516428a1aSJason M. Bills }
25616428a1aSJason M. Bills 
257*c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
2581da66f75SEd Tanous {
2591da66f75SEd Tanous   public:
2601da66f75SEd Tanous     template <typename CrowApp>
261*c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
262*c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/", std::string())
263*c4bf6374SJason M. Bills     {
264*c4bf6374SJason M. Bills         entityPrivileges = {
265*c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
266*c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
267*c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
268*c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
269*c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
270*c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
271*c4bf6374SJason M. Bills     }
272*c4bf6374SJason M. Bills 
273*c4bf6374SJason M. Bills   private:
274*c4bf6374SJason M. Bills     /**
275*c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
276*c4bf6374SJason M. Bills      */
277*c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
278*c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
279*c4bf6374SJason M. Bills     {
280*c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
281*c4bf6374SJason M. Bills         const std::string &name = params[0];
282*c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
283*c4bf6374SJason M. Bills         // it has a duplicate entry for members
284*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
285*c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
286*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
287*c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
288*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
289*c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices";
290*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
291*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
292*c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
293*c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
294*c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
295*c4bf6374SJason M. Bills         logServiceArray.push_back({{"@odata.id", "/redfish/v1/Systems/" + name +
296*c4bf6374SJason M. Bills                                                      "/LogServices/EventLog"}});
297*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
298*c4bf6374SJason M. Bills             logServiceArray.size();
299*c4bf6374SJason M. Bills     }
300*c4bf6374SJason M. Bills };
301*c4bf6374SJason M. Bills 
302*c4bf6374SJason M. Bills class EventLogService : public Node
303*c4bf6374SJason M. Bills {
304*c4bf6374SJason M. Bills   public:
305*c4bf6374SJason M. Bills     template <typename CrowApp>
306*c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
307*c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/",
308*c4bf6374SJason M. Bills              std::string())
309*c4bf6374SJason M. Bills     {
310*c4bf6374SJason M. Bills         entityPrivileges = {
311*c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
312*c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
313*c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
314*c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
315*c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
316*c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
317*c4bf6374SJason M. Bills     }
318*c4bf6374SJason M. Bills 
319*c4bf6374SJason M. Bills   private:
320*c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
321*c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
322*c4bf6374SJason M. Bills     {
323*c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
324*c4bf6374SJason M. Bills 
325*c4bf6374SJason M. Bills         const std::string &name = params[0];
326*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
327*c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices/EventLog";
328*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
329*c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
330*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
331*c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
332*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
333*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
334*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
335*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
336*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
337*c4bf6374SJason M. Bills             {"@odata.id",
338*c4bf6374SJason M. Bills              "/redfish/v1/Systems/" + name + "/LogServices/EventLog/Entries"}};
339*c4bf6374SJason M. Bills     }
340*c4bf6374SJason M. Bills };
341*c4bf6374SJason M. Bills 
342*c4bf6374SJason M. Bills static int fillEventLogEntryJson(const std::string &systemName,
343*c4bf6374SJason M. Bills                                  const std::string &bmcLogEntryID,
344*c4bf6374SJason M. Bills                                  const boost::string_view &messageID,
345*c4bf6374SJason M. Bills                                  sd_journal *journal,
346*c4bf6374SJason M. Bills                                  nlohmann::json &bmcLogEntryJson)
347*c4bf6374SJason M. Bills {
348*c4bf6374SJason M. Bills     // Get the Log Entry contents
349*c4bf6374SJason M. Bills     int ret = 0;
350*c4bf6374SJason M. Bills 
351*c4bf6374SJason M. Bills     boost::string_view msg;
352*c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
353*c4bf6374SJason M. Bills     if (ret < 0)
354*c4bf6374SJason M. Bills     {
355*c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
356*c4bf6374SJason M. Bills         return 1;
357*c4bf6374SJason M. Bills     }
358*c4bf6374SJason M. Bills 
359*c4bf6374SJason M. Bills     // Get the severity from the PRIORITY field
360*c4bf6374SJason M. Bills     int severity = 8; // Default to an invalid priority
361*c4bf6374SJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
362*c4bf6374SJason M. Bills     if (ret < 0)
363*c4bf6374SJason M. Bills     {
364*c4bf6374SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
365*c4bf6374SJason M. Bills         return 1;
366*c4bf6374SJason M. Bills     }
367*c4bf6374SJason M. Bills 
368*c4bf6374SJason M. Bills     // Get the MessageArgs from the journal entry by finding all of the
369*c4bf6374SJason M. Bills     // REDFISH_MESSAGE_ARG_x fields
370*c4bf6374SJason M. Bills     const void *data;
371*c4bf6374SJason M. Bills     size_t length;
372*c4bf6374SJason M. Bills     std::vector<std::string> messageArgs;
373*c4bf6374SJason M. Bills     SD_JOURNAL_FOREACH_DATA(journal, data, length)
374*c4bf6374SJason M. Bills     {
375*c4bf6374SJason M. Bills         boost::string_view field(static_cast<const char *>(data), length);
376*c4bf6374SJason M. Bills         if (field.starts_with("REDFISH_MESSAGE_ARG_"))
377*c4bf6374SJason M. Bills         {
378*c4bf6374SJason M. Bills             // Get the Arg number from the field name
379*c4bf6374SJason M. Bills             field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
380*c4bf6374SJason M. Bills             if (field.empty())
381*c4bf6374SJason M. Bills             {
382*c4bf6374SJason M. Bills                 continue;
383*c4bf6374SJason M. Bills             }
384*c4bf6374SJason M. Bills             int argNum = std::strtoul(field.data(), nullptr, 10);
385*c4bf6374SJason M. Bills             if (argNum == 0)
386*c4bf6374SJason M. Bills             {
387*c4bf6374SJason M. Bills                 continue;
388*c4bf6374SJason M. Bills             }
389*c4bf6374SJason M. Bills             // Get the Arg value after the "=" character.
390*c4bf6374SJason M. Bills             field.remove_prefix(std::min(field.find("=") + 1, field.size()));
391*c4bf6374SJason M. Bills             // Make sure we have enough space in messageArgs
392*c4bf6374SJason M. Bills             if (argNum > messageArgs.size())
393*c4bf6374SJason M. Bills             {
394*c4bf6374SJason M. Bills                 messageArgs.resize(argNum);
395*c4bf6374SJason M. Bills             }
396*c4bf6374SJason M. Bills             messageArgs[argNum - 1] = field.to_string();
397*c4bf6374SJason M. Bills         }
398*c4bf6374SJason M. Bills     }
399*c4bf6374SJason M. Bills 
400*c4bf6374SJason M. Bills     // Get the Created time from the timestamp
401*c4bf6374SJason M. Bills     std::string entryTimeStr;
402*c4bf6374SJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
403*c4bf6374SJason M. Bills     {
404*c4bf6374SJason M. Bills         return 1;
405*c4bf6374SJason M. Bills     }
406*c4bf6374SJason M. Bills 
407*c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
408*c4bf6374SJason M. Bills     bmcLogEntryJson = {
409*c4bf6374SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
410*c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
411*c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Systems/" + systemName +
412*c4bf6374SJason M. Bills                           "/LogServices/EventLog/Entries/" + bmcLogEntryID},
413*c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
414*c4bf6374SJason M. Bills         {"Id", bmcLogEntryID},
415*c4bf6374SJason M. Bills         {"Message", msg},
416*c4bf6374SJason M. Bills         {"MessageId", messageID},
417*c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
418*c4bf6374SJason M. Bills         {"EntryType", "Event"},
419*c4bf6374SJason M. Bills         {"Severity",
420*c4bf6374SJason M. Bills          severity <= 2 ? "Critical"
421*c4bf6374SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
422*c4bf6374SJason M. Bills         {"Created", std::move(entryTimeStr)}};
423*c4bf6374SJason M. Bills     return 0;
424*c4bf6374SJason M. Bills }
425*c4bf6374SJason M. Bills 
426*c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
427*c4bf6374SJason M. Bills {
428*c4bf6374SJason M. Bills   public:
429*c4bf6374SJason M. Bills     template <typename CrowApp>
430*c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
431*c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/",
432*c4bf6374SJason M. Bills              std::string())
433*c4bf6374SJason M. Bills     {
434*c4bf6374SJason M. Bills         entityPrivileges = {
435*c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
436*c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
437*c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
438*c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
439*c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
440*c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
441*c4bf6374SJason M. Bills     }
442*c4bf6374SJason M. Bills 
443*c4bf6374SJason M. Bills   private:
444*c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
445*c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
446*c4bf6374SJason M. Bills     {
447*c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
448*c4bf6374SJason M. Bills         long skip = 0;
449*c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
450*c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
451*c4bf6374SJason M. Bills         {
452*c4bf6374SJason M. Bills             return;
453*c4bf6374SJason M. Bills         }
454*c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
455*c4bf6374SJason M. Bills         {
456*c4bf6374SJason M. Bills             return;
457*c4bf6374SJason M. Bills         }
458*c4bf6374SJason M. Bills         const std::string &name = params[0];
459*c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
460*c4bf6374SJason M. Bills         // it has a duplicate entry for members
461*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
462*c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
463*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
464*c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
465*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
466*c4bf6374SJason M. Bills             "/redfish/v1/Systems/" + name + "/LogServices/EventLog/Entries";
467*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
468*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
469*c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
470*c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
471*c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
472*c4bf6374SJason M. Bills 
473*c4bf6374SJason M. Bills         // Go through the journal and create a unique ID for each entry
474*c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
475*c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
476*c4bf6374SJason M. Bills         if (ret < 0)
477*c4bf6374SJason M. Bills         {
478*c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
479*c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
480*c4bf6374SJason M. Bills             return;
481*c4bf6374SJason M. Bills         }
482*c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
483*c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
484*c4bf6374SJason M. Bills         journalTmp = nullptr;
485*c4bf6374SJason M. Bills         uint64_t entryCount = 0;
486*c4bf6374SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
487*c4bf6374SJason M. Bills         {
488*c4bf6374SJason M. Bills             // Look for only journal entries that contain a REDFISH_MESSAGE_ID
489*c4bf6374SJason M. Bills             // field
490*c4bf6374SJason M. Bills             boost::string_view messageID;
491*c4bf6374SJason M. Bills             ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID",
492*c4bf6374SJason M. Bills                                      messageID);
493*c4bf6374SJason M. Bills             if (ret < 0)
494*c4bf6374SJason M. Bills             {
495*c4bf6374SJason M. Bills                 continue;
496*c4bf6374SJason M. Bills             }
497*c4bf6374SJason M. Bills 
498*c4bf6374SJason M. Bills             entryCount++;
499*c4bf6374SJason M. Bills             // Handle paging using skip (number of entries to skip from the
500*c4bf6374SJason M. Bills             // start) and top (number of entries to display)
501*c4bf6374SJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
502*c4bf6374SJason M. Bills             {
503*c4bf6374SJason M. Bills                 continue;
504*c4bf6374SJason M. Bills             }
505*c4bf6374SJason M. Bills 
506*c4bf6374SJason M. Bills             std::string idStr;
507*c4bf6374SJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
508*c4bf6374SJason M. Bills             {
509*c4bf6374SJason M. Bills                 continue;
510*c4bf6374SJason M. Bills             }
511*c4bf6374SJason M. Bills 
512*c4bf6374SJason M. Bills             logEntryArray.push_back({});
513*c4bf6374SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
514*c4bf6374SJason M. Bills             if (fillEventLogEntryJson(name, idStr, messageID, journal.get(),
515*c4bf6374SJason M. Bills                                       bmcLogEntry) != 0)
516*c4bf6374SJason M. Bills             {
517*c4bf6374SJason M. Bills                 messages::internalError(asyncResp->res);
518*c4bf6374SJason M. Bills                 return;
519*c4bf6374SJason M. Bills             }
520*c4bf6374SJason M. Bills         }
521*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
522*c4bf6374SJason M. Bills         if (skip + top < entryCount)
523*c4bf6374SJason M. Bills         {
524*c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
525*c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
526*c4bf6374SJason M. Bills                 std::to_string(skip + top);
527*c4bf6374SJason M. Bills         }
528*c4bf6374SJason M. Bills     }
529*c4bf6374SJason M. Bills };
530*c4bf6374SJason M. Bills 
531*c4bf6374SJason M. Bills class EventLogEntry : public Node
532*c4bf6374SJason M. Bills {
533*c4bf6374SJason M. Bills   public:
534*c4bf6374SJason M. Bills     EventLogEntry(CrowApp &app) :
535*c4bf6374SJason M. Bills         Node(app,
536*c4bf6374SJason M. Bills              "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/",
537*c4bf6374SJason M. Bills              std::string(), std::string())
538*c4bf6374SJason M. Bills     {
539*c4bf6374SJason M. Bills         entityPrivileges = {
540*c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
541*c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
542*c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
543*c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
544*c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
545*c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
546*c4bf6374SJason M. Bills     }
547*c4bf6374SJason M. Bills 
548*c4bf6374SJason M. Bills   private:
549*c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
550*c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
551*c4bf6374SJason M. Bills     {
552*c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
553*c4bf6374SJason M. Bills         if (params.size() != 2)
554*c4bf6374SJason M. Bills         {
555*c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
556*c4bf6374SJason M. Bills             return;
557*c4bf6374SJason M. Bills         }
558*c4bf6374SJason M. Bills         const std::string &name = params[0];
559*c4bf6374SJason M. Bills         const std::string &entryID = params[1];
560*c4bf6374SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
561*c4bf6374SJason M. Bills         uint64_t ts = 0;
562*c4bf6374SJason M. Bills         uint16_t index = 0;
563*c4bf6374SJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
564*c4bf6374SJason M. Bills         {
565*c4bf6374SJason M. Bills             return;
566*c4bf6374SJason M. Bills         }
567*c4bf6374SJason M. Bills 
568*c4bf6374SJason M. Bills         sd_journal *journalTmp = nullptr;
569*c4bf6374SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
570*c4bf6374SJason M. Bills         if (ret < 0)
571*c4bf6374SJason M. Bills         {
572*c4bf6374SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
573*c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
574*c4bf6374SJason M. Bills             return;
575*c4bf6374SJason M. Bills         }
576*c4bf6374SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
577*c4bf6374SJason M. Bills             journalTmp, sd_journal_close);
578*c4bf6374SJason M. Bills         journalTmp = nullptr;
579*c4bf6374SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
580*c4bf6374SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
581*c4bf6374SJason M. Bills         for (int i = 0; i <= index; i++)
582*c4bf6374SJason M. Bills         {
583*c4bf6374SJason M. Bills             sd_journal_next(journal.get());
584*c4bf6374SJason M. Bills         }
585*c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
586*c4bf6374SJason M. Bills         std::string idStr;
587*c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
588*c4bf6374SJason M. Bills         {
589*c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
590*c4bf6374SJason M. Bills             return;
591*c4bf6374SJason M. Bills         }
592*c4bf6374SJason M. Bills 
593*c4bf6374SJason M. Bills         // only use journal entries that contain a REDFISH_MESSAGE_ID
594*c4bf6374SJason M. Bills         // field
595*c4bf6374SJason M. Bills         boost::string_view messageID;
596*c4bf6374SJason M. Bills         ret =
597*c4bf6374SJason M. Bills             getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
598*c4bf6374SJason M. Bills         if (ret < 0)
599*c4bf6374SJason M. Bills         {
600*c4bf6374SJason M. Bills             messages::resourceNotFound(asyncResp->res, "LogEntry", name);
601*c4bf6374SJason M. Bills             return;
602*c4bf6374SJason M. Bills         }
603*c4bf6374SJason M. Bills 
604*c4bf6374SJason M. Bills         if (fillEventLogEntryJson(name, entryID, messageID, journal.get(),
605*c4bf6374SJason M. Bills                                   asyncResp->res.jsonValue) != 0)
606*c4bf6374SJason M. Bills         {
607*c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
608*c4bf6374SJason M. Bills             return;
609*c4bf6374SJason M. Bills         }
610*c4bf6374SJason M. Bills     }
611*c4bf6374SJason M. Bills };
612*c4bf6374SJason M. Bills 
613*c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
614*c4bf6374SJason M. Bills {
615*c4bf6374SJason M. Bills   public:
616*c4bf6374SJason M. Bills     template <typename CrowApp>
617*c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
6184ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
6191da66f75SEd Tanous     {
6201da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
6211da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
6224ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices";
6231da66f75SEd Tanous         entityPrivileges = {
624e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
625e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
626e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
627e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
628e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
629e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6301da66f75SEd Tanous     }
6311da66f75SEd Tanous 
6321da66f75SEd Tanous   private:
6331da66f75SEd Tanous     /**
6341da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
6351da66f75SEd Tanous      */
6361da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
6371da66f75SEd Tanous                const std::vector<std::string> &params) override
6381da66f75SEd Tanous     {
639e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6401da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
6411da66f75SEd Tanous         // it has a duplicate entry for members
642e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
6431da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
644e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
645*c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
646e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
647e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
648e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
649e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
6501da66f75SEd Tanous             "Collection of LogServices for this Manager";
651*c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
652*c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
653*c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
654*c4bf6374SJason M. Bills         logServiceArray.push_back(
655*c4bf6374SJason M. Bills             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
656*c4bf6374SJason M. Bills #endif
6571da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
658*c4bf6374SJason M. Bills         logServiceArray.push_back(
6594ed77cd5SEd Tanous             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
6601da66f75SEd Tanous #endif
661e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
662*c4bf6374SJason M. Bills             logServiceArray.size();
6631da66f75SEd Tanous     }
6641da66f75SEd Tanous };
6651da66f75SEd Tanous 
666*c4bf6374SJason M. Bills class BMCJournalLogService : public Node
6671da66f75SEd Tanous {
6681da66f75SEd Tanous   public:
6691da66f75SEd Tanous     template <typename CrowApp>
670*c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
671*c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
672e1f26343SJason M. Bills     {
673e1f26343SJason M. Bills         // Set the id for SubRoute
674*c4bf6374SJason M. Bills         Node::json["@odata.id"] =
675*c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal";
676e1f26343SJason M. Bills         entityPrivileges = {
677e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
678e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
679e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
680e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
681e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
682e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
683e1f26343SJason M. Bills     }
684e1f26343SJason M. Bills 
685e1f26343SJason M. Bills   private:
686e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
687e1f26343SJason M. Bills                const std::vector<std::string> &params) override
688e1f26343SJason M. Bills     {
689e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
690e1f26343SJason M. Bills         // Copy over the static data to include the entries added by SubRoute
691e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
692e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
693e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
694e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
695e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
696*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
697*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
698*c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
699e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
700e1f26343SJason M. Bills     }
701e1f26343SJason M. Bills };
702e1f26343SJason M. Bills 
703*c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
704e1f26343SJason M. Bills                                       sd_journal *journal,
705*c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
706e1f26343SJason M. Bills {
707e1f26343SJason M. Bills     // Get the Log Entry contents
708e1f26343SJason M. Bills     int ret = 0;
709e1f26343SJason M. Bills 
71016428a1aSJason M. Bills     boost::string_view msg;
71116428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
712e1f26343SJason M. Bills     if (ret < 0)
713e1f26343SJason M. Bills     {
714e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
715e1f26343SJason M. Bills         return 1;
716e1f26343SJason M. Bills     }
717e1f26343SJason M. Bills 
718e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
719e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
72016428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
721e1f26343SJason M. Bills     if (ret < 0)
722e1f26343SJason M. Bills     {
723e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
724e1f26343SJason M. Bills         return 1;
725e1f26343SJason M. Bills     }
726e1f26343SJason M. Bills 
727e1f26343SJason M. Bills     // Get the Created time from the timestamp
72816428a1aSJason M. Bills     std::string entryTimeStr;
72916428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
730e1f26343SJason M. Bills     {
73116428a1aSJason M. Bills         return 1;
732e1f26343SJason M. Bills     }
733e1f26343SJason M. Bills 
734e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
735*c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
736e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
737e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
738*c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
739*c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
740e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
741*c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
74216428a1aSJason M. Bills         {"Message", msg},
743e1f26343SJason M. Bills         {"EntryType", "Oem"},
744e1f26343SJason M. Bills         {"Severity",
745e1f26343SJason M. Bills          severity <= 2 ? "Critical"
746e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
747e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
748e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
749e1f26343SJason M. Bills     return 0;
750e1f26343SJason M. Bills }
751e1f26343SJason M. Bills 
752*c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
753e1f26343SJason M. Bills {
754e1f26343SJason M. Bills   public:
755e1f26343SJason M. Bills     template <typename CrowApp>
756*c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
757*c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
758e1f26343SJason M. Bills     {
759e1f26343SJason M. Bills         // Collections use static ID for SubRoute to add to its parent, but only
760e1f26343SJason M. Bills         // load dynamic data so the duplicate static members don't get displayed
761e1f26343SJason M. Bills         Node::json["@odata.id"] =
762*c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
763e1f26343SJason M. Bills         entityPrivileges = {
764e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
765e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
766e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
767e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
768e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
769e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
770e1f26343SJason M. Bills     }
771e1f26343SJason M. Bills 
772e1f26343SJason M. Bills   private:
773e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
774e1f26343SJason M. Bills                const std::vector<std::string> &params) override
775e1f26343SJason M. Bills     {
776e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
777193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
778193ad2faSJason M. Bills         long skip = 0;
779193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
78016428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
781193ad2faSJason M. Bills         {
782193ad2faSJason M. Bills             return;
783193ad2faSJason M. Bills         }
78416428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
785193ad2faSJason M. Bills         {
786193ad2faSJason M. Bills             return;
787193ad2faSJason M. Bills         }
788e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
789e1f26343SJason M. Bills         // it has a duplicate entry for members
790e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
791e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
792e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
793*c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
794e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
795*c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
796e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
797e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
798e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
799e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
800e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
801e1f26343SJason M. Bills 
802e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
803e1f26343SJason M. Bills         // for each entry
804e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
805e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
806e1f26343SJason M. Bills         if (ret < 0)
807e1f26343SJason M. Bills         {
808e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
809f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
810e1f26343SJason M. Bills             return;
811e1f26343SJason M. Bills         }
812e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
813e1f26343SJason M. Bills             journalTmp, sd_journal_close);
814e1f26343SJason M. Bills         journalTmp = nullptr;
815193ad2faSJason M. Bills         uint64_t entryCount = 0;
816e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
817e1f26343SJason M. Bills         {
818193ad2faSJason M. Bills             entryCount++;
819193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
820193ad2faSJason M. Bills             // start) and top (number of entries to display)
821193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
822193ad2faSJason M. Bills             {
823193ad2faSJason M. Bills                 continue;
824193ad2faSJason M. Bills             }
825193ad2faSJason M. Bills 
82616428a1aSJason M. Bills             std::string idStr;
82716428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
828e1f26343SJason M. Bills             {
829e1f26343SJason M. Bills                 continue;
830e1f26343SJason M. Bills             }
831e1f26343SJason M. Bills 
832e1f26343SJason M. Bills             logEntryArray.push_back({});
833*c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
834*c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
835*c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
836e1f26343SJason M. Bills             {
837f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
838e1f26343SJason M. Bills                 return;
839e1f26343SJason M. Bills             }
840e1f26343SJason M. Bills         }
841193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
842193ad2faSJason M. Bills         if (skip + top < entryCount)
843193ad2faSJason M. Bills         {
844193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
845*c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
846193ad2faSJason M. Bills                 std::to_string(skip + top);
847193ad2faSJason M. Bills         }
848e1f26343SJason M. Bills     }
849e1f26343SJason M. Bills };
850e1f26343SJason M. Bills 
851*c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
852e1f26343SJason M. Bills {
853e1f26343SJason M. Bills   public:
854*c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
855*c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
856e1f26343SJason M. Bills              std::string())
857e1f26343SJason M. Bills     {
858e1f26343SJason M. Bills         entityPrivileges = {
859e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
860e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
861e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
862e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
863e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
864e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
865e1f26343SJason M. Bills     }
866e1f26343SJason M. Bills 
867e1f26343SJason M. Bills   private:
868e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
869e1f26343SJason M. Bills                const std::vector<std::string> &params) override
870e1f26343SJason M. Bills     {
871e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
872e1f26343SJason M. Bills         if (params.size() != 1)
873e1f26343SJason M. Bills         {
874f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
875e1f26343SJason M. Bills             return;
876e1f26343SJason M. Bills         }
87716428a1aSJason M. Bills         const std::string &entryID = params[0];
878e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
879e1f26343SJason M. Bills         uint64_t ts = 0;
880e1f26343SJason M. Bills         uint16_t index = 0;
88116428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
882e1f26343SJason M. Bills         {
88316428a1aSJason M. Bills             return;
884e1f26343SJason M. Bills         }
885e1f26343SJason M. Bills 
886e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
887e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
888e1f26343SJason M. Bills         if (ret < 0)
889e1f26343SJason M. Bills         {
890e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
891f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
892e1f26343SJason M. Bills             return;
893e1f26343SJason M. Bills         }
894e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
895e1f26343SJason M. Bills             journalTmp, sd_journal_close);
896e1f26343SJason M. Bills         journalTmp = nullptr;
897e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
898e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
899e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
900e1f26343SJason M. Bills         {
901e1f26343SJason M. Bills             sd_journal_next(journal.get());
902e1f26343SJason M. Bills         }
903*c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
904*c4bf6374SJason M. Bills         std::string idStr;
905*c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
906*c4bf6374SJason M. Bills         {
907*c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
908*c4bf6374SJason M. Bills             return;
909*c4bf6374SJason M. Bills         }
910*c4bf6374SJason M. Bills 
911*c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
912e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
913e1f26343SJason M. Bills         {
914f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
915e1f26343SJason M. Bills             return;
916e1f26343SJason M. Bills         }
917e1f26343SJason M. Bills     }
918e1f26343SJason M. Bills };
919e1f26343SJason M. Bills 
920e1f26343SJason M. Bills class CPULogService : public Node
921e1f26343SJason M. Bills {
922e1f26343SJason M. Bills   public:
923e1f26343SJason M. Bills     template <typename CrowApp>
924e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
925e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
9261da66f75SEd Tanous     {
9271da66f75SEd Tanous         // Set the id for SubRoute
9284ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
9291da66f75SEd Tanous         entityPrivileges = {
930e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
931e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
932e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
933e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
934e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
935e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9361da66f75SEd Tanous     }
9371da66f75SEd Tanous 
9381da66f75SEd Tanous   private:
9391da66f75SEd Tanous     /**
9401da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9411da66f75SEd Tanous      */
9421da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9431da66f75SEd Tanous                const std::vector<std::string> &params) override
9441da66f75SEd Tanous     {
945e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9461da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
947e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
948e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
949e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
950e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
951*c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
952e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
953e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
954e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
955e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
956e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
957e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
9581da66f75SEd Tanous             {"Oem",
9591da66f75SEd Tanous              {{"#CpuLog.Immediate",
960*c4bf6374SJason M. Bills                {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/"
961*c4bf6374SJason M. Bills                            "Actions/Oem/CpuLog.Immediate"}}}}}};
9621da66f75SEd Tanous 
9631da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
964e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
9651da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
966*c4bf6374SJason M. Bills              {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/"
967*c4bf6374SJason M. Bills                          "Oem/CpuLog.SendRawPeci"}}});
9681da66f75SEd Tanous #endif
9691da66f75SEd Tanous     }
9701da66f75SEd Tanous };
9711da66f75SEd Tanous 
972e1f26343SJason M. Bills class CPULogEntryCollection : public Node
9731da66f75SEd Tanous {
9741da66f75SEd Tanous   public:
9751da66f75SEd Tanous     template <typename CrowApp>
976e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
977e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
9781da66f75SEd Tanous     {
9791da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
9801da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
9811da66f75SEd Tanous         Node::json["@odata.id"] =
9824ed77cd5SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
9831da66f75SEd Tanous         entityPrivileges = {
984e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
985e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
986e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
987e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
988e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
989e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9901da66f75SEd Tanous     }
9911da66f75SEd Tanous 
9921da66f75SEd Tanous   private:
9931da66f75SEd Tanous     /**
9941da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
9951da66f75SEd Tanous      */
9961da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
9971da66f75SEd Tanous                const std::vector<std::string> &params) override
9981da66f75SEd Tanous     {
999e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
10001da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
10011da66f75SEd Tanous         // it has a duplicate entry for members
1002e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
1003e1f26343SJason M. Bills                                          const boost::system::error_code ec,
10041da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
10051da66f75SEd Tanous             if (ec)
10061da66f75SEd Tanous             {
10071da66f75SEd Tanous                 if (ec.value() !=
10081da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
10091da66f75SEd Tanous                 {
10101da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
10111da66f75SEd Tanous                                      << ec.message();
1012f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
10131da66f75SEd Tanous                     return;
10141da66f75SEd Tanous                 }
10151da66f75SEd Tanous             }
1016e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
10171da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
1018e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1019*c4bf6374SJason M. Bills                 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1020e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.id"] =
1021e1f26343SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
1022e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
1023e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1024e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
1025e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1026e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
10271da66f75SEd Tanous             for (const std::string &objpath : resp)
10281da66f75SEd Tanous             {
10291da66f75SEd Tanous                 // Don't list the immediate log
10304ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
10311da66f75SEd Tanous                 {
10321da66f75SEd Tanous                     continue;
10331da66f75SEd Tanous                 }
10344ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
10354ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
10361da66f75SEd Tanous                 {
1037e1f26343SJason M. Bills                     logEntryArray.push_back(
1038e1f26343SJason M. Bills                         {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
1039e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
10404ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
10411da66f75SEd Tanous                 }
10421da66f75SEd Tanous             }
1043e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1044e1f26343SJason M. Bills                 logEntryArray.size();
10451da66f75SEd Tanous         };
10461da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
10471da66f75SEd Tanous             std::move(getLogEntriesCallback),
10481da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
10491da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
10501da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
10514ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
10521da66f75SEd Tanous     }
10531da66f75SEd Tanous };
10541da66f75SEd Tanous 
10551da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
10561da66f75SEd Tanous {
10571da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
10581da66f75SEd Tanous     if (metaIt != cpuLog.end())
10591da66f75SEd Tanous     {
10601da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
10611da66f75SEd Tanous         if (tsIt != metaIt->end())
10621da66f75SEd Tanous         {
10631da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
10641da66f75SEd Tanous             if (logTime != nullptr)
10651da66f75SEd Tanous             {
10661da66f75SEd Tanous                 return *logTime;
10671da66f75SEd Tanous             }
10681da66f75SEd Tanous         }
10691da66f75SEd Tanous     }
10701da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
10711da66f75SEd Tanous 
10721da66f75SEd Tanous     return std::string();
10731da66f75SEd Tanous }
10741da66f75SEd Tanous 
1075e1f26343SJason M. Bills class CPULogEntry : public Node
10761da66f75SEd Tanous {
10771da66f75SEd Tanous   public:
1078e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
10794ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
10801da66f75SEd Tanous              std::string())
10811da66f75SEd Tanous     {
10821da66f75SEd Tanous         entityPrivileges = {
1083e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1084e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1085e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1086e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1087e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1088e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
10891da66f75SEd Tanous     }
10901da66f75SEd Tanous 
10911da66f75SEd Tanous   private:
10921da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
10931da66f75SEd Tanous                const std::vector<std::string> &params) override
10941da66f75SEd Tanous     {
1095e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
10961da66f75SEd Tanous         if (params.size() != 1)
10971da66f75SEd Tanous         {
1098f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
10991da66f75SEd Tanous             return;
11001da66f75SEd Tanous         }
11014ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1102e1f26343SJason M. Bills         auto getStoredLogCallback =
1103e1f26343SJason M. Bills             [asyncResp,
11044ed77cd5SEd Tanous              logId](const boost::system::error_code ec,
1105e1f26343SJason M. Bills                     const sdbusplus::message::variant<std::string> &resp) {
11061da66f75SEd Tanous                 if (ec)
11071da66f75SEd Tanous                 {
1108e1f26343SJason M. Bills                     BMCWEB_LOG_DEBUG << "failed to get log ec: "
1109e1f26343SJason M. Bills                                      << ec.message();
1110f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
11111da66f75SEd Tanous                     return;
11121da66f75SEd Tanous                 }
1113e1f26343SJason M. Bills                 const std::string *log =
1114e1f26343SJason M. Bills                     mapbox::getPtr<const std::string>(resp);
11151da66f75SEd Tanous                 if (log == nullptr)
11161da66f75SEd Tanous                 {
1117f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
11181da66f75SEd Tanous                     return;
11191da66f75SEd Tanous                 }
11201da66f75SEd Tanous                 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
11211da66f75SEd Tanous                 if (j.is_discarded())
11221da66f75SEd Tanous                 {
1123f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
11241da66f75SEd Tanous                     return;
11251da66f75SEd Tanous                 }
11261da66f75SEd Tanous                 std::string t = getLogCreatedTime(j);
1127e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {
11281da66f75SEd Tanous                     {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
1129e1f26343SJason M. Bills                     {"@odata.context",
1130e1f26343SJason M. Bills                      "/redfish/v1/$metadata#LogEntry.LogEntry"},
11311da66f75SEd Tanous                     {"@odata.id",
11324ed77cd5SEd Tanous                      "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
11334ed77cd5SEd Tanous                          std::to_string(logId)},
11341da66f75SEd Tanous                     {"Name", "CPU Debug Log"},
11354ed77cd5SEd Tanous                     {"Id", logId},
11361da66f75SEd Tanous                     {"EntryType", "Oem"},
11371da66f75SEd Tanous                     {"OemRecordFormat", "Intel CPU Log"},
11381da66f75SEd Tanous                     {"Oem", {{"Intel", std::move(j)}}},
11391da66f75SEd Tanous                     {"Created", std::move(t)}};
11401da66f75SEd Tanous             };
11411da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
11424ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
11434ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
11444ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
11451da66f75SEd Tanous     }
11461da66f75SEd Tanous };
11471da66f75SEd Tanous 
1148e1f26343SJason M. Bills class ImmediateCPULog : public Node
11491da66f75SEd Tanous {
11501da66f75SEd Tanous   public:
1151e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
11524ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
1153e1f26343SJason M. Bills                   "CpuLog.Immediate/")
11541da66f75SEd Tanous     {
11551da66f75SEd Tanous         entityPrivileges = {
1156e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1157e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1158e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1159e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1160e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1161e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
11621da66f75SEd Tanous     }
11631da66f75SEd Tanous 
11641da66f75SEd Tanous   private:
11651da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
11661da66f75SEd Tanous                 const std::vector<std::string> &params) override
11671da66f75SEd Tanous     {
1168e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
11691da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
11701da66f75SEd Tanous             immediateLogMatcher;
11711da66f75SEd Tanous 
11721da66f75SEd Tanous         // Only allow one Immediate Log request at a time
11731da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
11741da66f75SEd Tanous         {
1175e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1176f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
11771da66f75SEd Tanous             return;
11781da66f75SEd Tanous         }
11791da66f75SEd Tanous         // Make this static so it survives outside this method
11801da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
11811da66f75SEd Tanous 
11821da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1183e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
11841da66f75SEd Tanous             immediateLogMatcher = nullptr;
11851da66f75SEd Tanous             if (ec)
11861da66f75SEd Tanous             {
11871da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
11881da66f75SEd Tanous                 // completion.
11891da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
11901da66f75SEd Tanous                 {
11911da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
11921da66f75SEd Tanous                 }
11931da66f75SEd Tanous                 return;
11941da66f75SEd Tanous             }
11951da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
11961da66f75SEd Tanous 
1197f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
11981da66f75SEd Tanous         });
11991da66f75SEd Tanous 
1200e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
12011da66f75SEd Tanous                                                sdbusplus::message::message &m) {
12021da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
12031da66f75SEd Tanous             boost::system::error_code ec;
12041da66f75SEd Tanous             timeout.cancel(ec);
12051da66f75SEd Tanous             if (ec)
12061da66f75SEd Tanous             {
12071da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
12081da66f75SEd Tanous             }
12094ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
12101da66f75SEd Tanous             boost::container::flat_map<
12111da66f75SEd Tanous                 std::string,
12121da66f75SEd Tanous                 boost::container::flat_map<
12131da66f75SEd Tanous                     std::string, sdbusplus::message::variant<std::string>>>
12144ed77cd5SEd Tanous                 interfacesAdded;
12154ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
12161da66f75SEd Tanous             const std::string *log = mapbox::getPtr<const std::string>(
12174ed77cd5SEd Tanous                 interfacesAdded[cpuLogInterface]["Log"]);
12181da66f75SEd Tanous             if (log == nullptr)
12191da66f75SEd Tanous             {
1220f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12211da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
12221da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
12231da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
12241da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
12251da66f75SEd Tanous                 // be the last thing done.
12261da66f75SEd Tanous                 immediateLogMatcher = nullptr;
12271da66f75SEd Tanous                 return;
12281da66f75SEd Tanous             }
12291da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
12301da66f75SEd Tanous             if (j.is_discarded())
12311da66f75SEd Tanous             {
1232f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12331da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
12341da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
12351da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
12361da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
12371da66f75SEd Tanous                 // be the last thing done.
12381da66f75SEd Tanous                 immediateLogMatcher = nullptr;
12391da66f75SEd Tanous                 return;
12401da66f75SEd Tanous             }
12411da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1242e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
12431da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
12441da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
12451da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
12461da66f75SEd Tanous                 {"EntryType", "Oem"},
12471da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
12481da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
12491da66f75SEd Tanous                 {"Created", std::move(t)}};
12501da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
12511da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
12521da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
12531da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
12541da66f75SEd Tanous             // thing done.
12551da66f75SEd Tanous             immediateLogMatcher = nullptr;
12561da66f75SEd Tanous         };
12571da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
12581da66f75SEd Tanous             *crow::connections::systemBus,
12591da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
12604ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
12611da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
12621da66f75SEd Tanous 
12631da66f75SEd Tanous         auto generateImmediateLogCallback =
1264e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
12651da66f75SEd Tanous                         const std::string &resp) {
12661da66f75SEd Tanous                 if (ec)
12671da66f75SEd Tanous                 {
12681da66f75SEd Tanous                     if (ec.value() ==
12691da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
12701da66f75SEd Tanous                     {
1271f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
12721da66f75SEd Tanous                     }
12731da66f75SEd Tanous                     else
12741da66f75SEd Tanous                     {
1275f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
12761da66f75SEd Tanous                     }
12771da66f75SEd Tanous                     boost::system::error_code timeoutec;
12781da66f75SEd Tanous                     timeout.cancel(timeoutec);
12791da66f75SEd Tanous                     if (timeoutec)
12801da66f75SEd Tanous                     {
12811da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
12821da66f75SEd Tanous                                          << timeoutec;
12831da66f75SEd Tanous                     }
12841da66f75SEd Tanous                     immediateLogMatcher = nullptr;
12851da66f75SEd Tanous                     return;
12861da66f75SEd Tanous                 }
12871da66f75SEd Tanous             };
12881da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
12894ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
12904ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
12911da66f75SEd Tanous     }
12921da66f75SEd Tanous };
12931da66f75SEd Tanous 
1294e1f26343SJason M. Bills class SendRawPECI : public Node
12951da66f75SEd Tanous {
12961da66f75SEd Tanous   public:
1297e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
12984ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
1299e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
13001da66f75SEd Tanous     {
13011da66f75SEd Tanous         entityPrivileges = {
13021da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
13031da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
13041da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
13051da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
13061da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
13071da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
13081da66f75SEd Tanous     }
13091da66f75SEd Tanous 
13101da66f75SEd Tanous   private:
13111da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
13121da66f75SEd Tanous                 const std::vector<std::string> &params) override
13131da66f75SEd Tanous     {
1314e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
13151da66f75SEd Tanous         // Get the Raw PECI command from the request
1316e1f26343SJason M. Bills         nlohmann::json rawPECICmd;
1317e1f26343SJason M. Bills         if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
13181da66f75SEd Tanous         {
13191da66f75SEd Tanous             return;
13201da66f75SEd Tanous         }
13211da66f75SEd Tanous         // Get the Client Address from the request
1322e1f26343SJason M. Bills         nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
1323e1f26343SJason M. Bills         if (caIt == rawPECICmd.end())
13241da66f75SEd Tanous         {
1325a08b46ccSJason M. Bills             messages::propertyMissing(asyncResp->res, "ClientAddress");
13261da66f75SEd Tanous             return;
13271da66f75SEd Tanous         }
13281da66f75SEd Tanous         const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
13291da66f75SEd Tanous         if (ca == nullptr)
13301da66f75SEd Tanous         {
1331f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, caIt->dump(),
1332a08b46ccSJason M. Bills                                              "ClientAddress");
13331da66f75SEd Tanous             return;
13341da66f75SEd Tanous         }
13351da66f75SEd Tanous         // Get the Read Length from the request
13361da66f75SEd Tanous         const uint8_t clientAddress = static_cast<uint8_t>(*ca);
1337e1f26343SJason M. Bills         nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
1338e1f26343SJason M. Bills         if (rlIt == rawPECICmd.end())
13391da66f75SEd Tanous         {
1340a08b46ccSJason M. Bills             messages::propertyMissing(asyncResp->res, "ReadLength");
13411da66f75SEd Tanous             return;
13421da66f75SEd Tanous         }
13431da66f75SEd Tanous         const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
13441da66f75SEd Tanous         if (rl == nullptr)
13451da66f75SEd Tanous         {
1346f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, rlIt->dump(),
1347a08b46ccSJason M. Bills                                              "ReadLength");
13481da66f75SEd Tanous             return;
13491da66f75SEd Tanous         }
13501da66f75SEd Tanous         // Get the PECI Command from the request
13511da66f75SEd Tanous         const uint32_t readLength = static_cast<uint32_t>(*rl);
1352e1f26343SJason M. Bills         nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
1353e1f26343SJason M. Bills         if (pcIt == rawPECICmd.end())
13541da66f75SEd Tanous         {
1355a08b46ccSJason M. Bills             messages::propertyMissing(asyncResp->res, "PECICommand");
13561da66f75SEd Tanous             return;
13571da66f75SEd Tanous         }
13581da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
13591da66f75SEd Tanous         for (auto pc : *pcIt)
13601da66f75SEd Tanous         {
13611da66f75SEd Tanous             const uint64_t *val = pc.get_ptr<const uint64_t *>();
13621da66f75SEd Tanous             if (val == nullptr)
13631da66f75SEd Tanous             {
13641da66f75SEd Tanous                 messages::propertyValueTypeError(
1365f12894f8SJason M. Bills                     asyncResp->res, pc.dump(),
1366a08b46ccSJason M. Bills                     "PECICommand/" + std::to_string(peciCommand.size()));
13671da66f75SEd Tanous                 return;
13681da66f75SEd Tanous             }
13691da66f75SEd Tanous             peciCommand.push_back(static_cast<uint8_t>(*val));
13701da66f75SEd Tanous         }
13711da66f75SEd Tanous         // Callback to return the Raw PECI response
1372e1f26343SJason M. Bills         auto sendRawPECICallback =
1373e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
13741da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
13751da66f75SEd Tanous                 if (ec)
13761da66f75SEd Tanous                 {
13771da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
13781da66f75SEd Tanous                                      << ec.message();
1379f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
13801da66f75SEd Tanous                     return;
13811da66f75SEd Tanous                 }
1382e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
13831da66f75SEd Tanous                                             {"PECIResponse", resp}};
13841da66f75SEd Tanous             };
13851da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
13861da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1387e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
1388e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
13894ed77cd5SEd Tanous             peciCommand);
13901da66f75SEd Tanous     }
13911da66f75SEd Tanous };
13921da66f75SEd Tanous 
13931da66f75SEd Tanous } // namespace redfish
1394