xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 193ad2fad4c897cbba26b1345cb7b03309e28627)
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 
401da66f75SEd Tanous class LogServiceCollection : public Node
411da66f75SEd Tanous {
421da66f75SEd Tanous   public:
431da66f75SEd Tanous     template <typename CrowApp>
441da66f75SEd Tanous     LogServiceCollection(CrowApp &app) :
454ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
461da66f75SEd Tanous     {
471da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
481da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
494ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices";
501da66f75SEd Tanous         entityPrivileges = {
51e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
52e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
53e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
54e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
55e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
56e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
571da66f75SEd Tanous     }
581da66f75SEd Tanous 
591da66f75SEd Tanous   private:
601da66f75SEd Tanous     /**
611da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
621da66f75SEd Tanous      */
631da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
641da66f75SEd Tanous                const std::vector<std::string> &params) override
651da66f75SEd Tanous     {
66e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
671da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
681da66f75SEd Tanous         // it has a duplicate entry for members
69e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
701da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
71e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
721da66f75SEd Tanous             "/redfish/v1/"
731da66f75SEd Tanous             "$metadata#LogServiceCollection.LogServiceCollection";
74e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
75e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
76e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
77e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
781da66f75SEd Tanous             "Collection of LogServices for this Manager";
79e1f26343SJason M. Bills         nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"];
801da66f75SEd Tanous         logserviceArray = nlohmann::json::array();
81e1f26343SJason M. Bills         logserviceArray.push_back(
82e1f26343SJason M. Bills             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}});
831da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
841da66f75SEd Tanous         logserviceArray.push_back(
854ed77cd5SEd Tanous             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
861da66f75SEd Tanous #endif
87e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
88e1f26343SJason M. Bills             logserviceArray.size();
891da66f75SEd Tanous     }
901da66f75SEd Tanous };
911da66f75SEd Tanous 
92e1f26343SJason M. Bills class BMCLogService : public Node
931da66f75SEd Tanous {
941da66f75SEd Tanous   public:
951da66f75SEd Tanous     template <typename CrowApp>
96e1f26343SJason M. Bills     BMCLogService(CrowApp &app) :
97e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/")
98e1f26343SJason M. Bills     {
99e1f26343SJason M. Bills         // Set the id for SubRoute
100e1f26343SJason M. Bills         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog";
101e1f26343SJason M. Bills         entityPrivileges = {
102e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
103e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
104e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
105e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
106e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
107e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
108e1f26343SJason M. Bills     }
109e1f26343SJason M. Bills 
110e1f26343SJason M. Bills   private:
111e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
112e1f26343SJason M. Bills                const std::vector<std::string> &params) override
113e1f26343SJason M. Bills     {
114e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
115e1f26343SJason M. Bills         // Copy over the static data to include the entries added by SubRoute
116e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
117e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
118e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
119e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
120e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
121e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Service";
122e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Log Service";
123e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Log";
124e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
125e1f26343SJason M. Bills     }
126e1f26343SJason M. Bills };
127e1f26343SJason M. Bills 
128e1f26343SJason M. Bills static int fillBMCLogEntryJson(const std::string &bmcLogEntryID,
129e1f26343SJason M. Bills                                sd_journal *journal,
130e1f26343SJason M. Bills                                nlohmann::json &bmcLogEntryJson)
131e1f26343SJason M. Bills {
132e1f26343SJason M. Bills     // Get the Log Entry contents
133e1f26343SJason M. Bills     int ret = 0;
134e1f26343SJason M. Bills     const char *data = nullptr;
135e1f26343SJason M. Bills     size_t length = 0;
136e1f26343SJason M. Bills 
137e1f26343SJason M. Bills     ret =
138e1f26343SJason M. Bills         sd_journal_get_data(journal, "MESSAGE", (const void **)&data, &length);
139e1f26343SJason M. Bills     if (ret < 0)
140e1f26343SJason M. Bills     {
141e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
142e1f26343SJason M. Bills         return 1;
143e1f26343SJason M. Bills     }
144e1f26343SJason M. Bills     boost::string_view msg;
145e1f26343SJason M. Bills     msg = boost::string_view(data, length);
146e1f26343SJason M. Bills     // Only use the content after the "=" character.
147e1f26343SJason M. Bills     msg.remove_prefix(std::min(msg.find("=") + 1, msg.size()));
148e1f26343SJason M. Bills 
149e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
150e1f26343SJason M. Bills     boost::string_view priority;
151e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
152e1f26343SJason M. Bills     ret =
153e1f26343SJason M. Bills         sd_journal_get_data(journal, "PRIORITY", (const void **)&data, &length);
154e1f26343SJason M. Bills     if (ret < 0)
155e1f26343SJason M. Bills     {
156e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
157e1f26343SJason M. Bills         return 1;
158e1f26343SJason M. Bills     }
159e1f26343SJason M. Bills     priority = boost::string_view(data, length);
160e1f26343SJason M. Bills     // Check length for sanity. Must be a single digit in the form
161e1f26343SJason M. Bills     // "PRIORITY=[0-7]"
162e1f26343SJason M. Bills     if (priority.size() > sizeof("PRIORITY=0"))
163e1f26343SJason M. Bills     {
164e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Invalid PRIORITY field length";
165e1f26343SJason M. Bills         return 1;
166e1f26343SJason M. Bills     }
167e1f26343SJason M. Bills     // Only use the content after the "=" character.
168e1f26343SJason M. Bills     priority.remove_prefix(std::min(priority.find("=") + 1, priority.size()));
169e1f26343SJason M. Bills     severity = strtol(priority.data(), nullptr, 10);
170e1f26343SJason M. Bills 
171e1f26343SJason M. Bills     // Get the Created time from the timestamp
172e1f26343SJason M. Bills     // Get the entry timestamp
173e1f26343SJason M. Bills     uint64_t timestamp = 0;
174e1f26343SJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
175e1f26343SJason M. Bills     if (ret < 0)
176e1f26343SJason M. Bills     {
177e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
178e1f26343SJason M. Bills                          << strerror(-ret);
179e1f26343SJason M. Bills     }
180e1f26343SJason M. Bills     time_t t =
181e1f26343SJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
182e1f26343SJason M. Bills     struct tm *loctime = localtime(&t);
183e1f26343SJason M. Bills     char entryTime[64] = {};
184e1f26343SJason M. Bills     if (NULL != loctime)
185e1f26343SJason M. Bills     {
186e1f26343SJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
187e1f26343SJason M. Bills     }
188e1f26343SJason M. Bills     // Insert the ':' into the timezone
189e1f26343SJason M. Bills     boost::string_view t1(entryTime);
190e1f26343SJason M. Bills     boost::string_view t2(entryTime);
191e1f26343SJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
192e1f26343SJason M. Bills     {
193e1f26343SJason M. Bills         t1.remove_suffix(2);
194e1f26343SJason M. Bills         t2.remove_prefix(t2.size() - 2);
195e1f26343SJason M. Bills     }
196e1f26343SJason M. Bills     const std::string entryTimeStr(t1.to_string() + ":" + t2.to_string());
197e1f26343SJason M. Bills 
198e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
199e1f26343SJason M. Bills     bmcLogEntryJson = {
200e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
201e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
202e1f26343SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" +
203e1f26343SJason M. Bills                           bmcLogEntryID},
204e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
205e1f26343SJason M. Bills         {"Id", bmcLogEntryID},
206e1f26343SJason M. Bills         {"Message", msg.to_string()},
207e1f26343SJason M. Bills         {"EntryType", "Oem"},
208e1f26343SJason M. Bills         {"Severity",
209e1f26343SJason M. Bills          severity <= 2 ? "Critical"
210e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
211e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
212e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
213e1f26343SJason M. Bills     return 0;
214e1f26343SJason M. Bills }
215e1f26343SJason M. Bills 
216e1f26343SJason M. Bills class BMCLogEntryCollection : public Node
217e1f26343SJason M. Bills {
218e1f26343SJason M. Bills   public:
219e1f26343SJason M. Bills     template <typename CrowApp>
220e1f26343SJason M. Bills     BMCLogEntryCollection(CrowApp &app) :
221e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/")
222e1f26343SJason M. Bills     {
223e1f26343SJason M. Bills         // Collections use static ID for SubRoute to add to its parent, but only
224e1f26343SJason M. Bills         // load dynamic data so the duplicate static members don't get displayed
225e1f26343SJason M. Bills         Node::json["@odata.id"] =
226e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
227e1f26343SJason M. Bills         entityPrivileges = {
228e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
229e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
230e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
231e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
232e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
233e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
234e1f26343SJason M. Bills     }
235e1f26343SJason M. Bills 
236e1f26343SJason M. Bills   private:
237e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
238e1f26343SJason M. Bills                const std::vector<std::string> &params) override
239e1f26343SJason M. Bills     {
240e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
241*193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
242*193ad2faSJason M. Bills         long skip = 0;
243*193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
244*193ad2faSJason M. Bills         char *skipParam = req.urlParams.get("$skip");
245*193ad2faSJason M. Bills         if (skipParam != nullptr)
246*193ad2faSJason M. Bills         {
247*193ad2faSJason M. Bills             char *ptr = nullptr;
248*193ad2faSJason M. Bills             skip = std::strtol(skipParam, &ptr, 10);
249*193ad2faSJason M. Bills             if (*skipParam == '\0' || *ptr != '\0')
250*193ad2faSJason M. Bills             {
251*193ad2faSJason M. Bills                 messages::addMessageToErrorJson(
252*193ad2faSJason M. Bills                     asyncResp->res.jsonValue,
253*193ad2faSJason M. Bills                     messages::queryParameterValueTypeError(
254*193ad2faSJason M. Bills                         std::string(skipParam), "$skip"));
255*193ad2faSJason M. Bills                 asyncResp->res.result(boost::beast::http::status::bad_request);
256*193ad2faSJason M. Bills                 return;
257*193ad2faSJason M. Bills             }
258*193ad2faSJason M. Bills             if (skip < 0)
259*193ad2faSJason M. Bills             {
260*193ad2faSJason M. Bills                 messages::addMessageToErrorJson(
261*193ad2faSJason M. Bills                     asyncResp->res.jsonValue,
262*193ad2faSJason M. Bills                     messages::queryParameterOutOfRange(
263*193ad2faSJason M. Bills                         std::to_string(skip), "$skip", "greater than 0"));
264*193ad2faSJason M. Bills                 asyncResp->res.result(boost::beast::http::status::bad_request);
265*193ad2faSJason M. Bills                 return;
266*193ad2faSJason M. Bills             }
267*193ad2faSJason M. Bills         }
268*193ad2faSJason M. Bills         char *topParam = req.urlParams.get("$top");
269*193ad2faSJason M. Bills         if (topParam != nullptr)
270*193ad2faSJason M. Bills         {
271*193ad2faSJason M. Bills             char *ptr = nullptr;
272*193ad2faSJason M. Bills             top = std::strtol(topParam, &ptr, 10);
273*193ad2faSJason M. Bills             if (*topParam == '\0' || *ptr != '\0')
274*193ad2faSJason M. Bills             {
275*193ad2faSJason M. Bills                 messages::addMessageToErrorJson(
276*193ad2faSJason M. Bills                     asyncResp->res.jsonValue,
277*193ad2faSJason M. Bills                     messages::queryParameterValueTypeError(
278*193ad2faSJason M. Bills                         std::string(topParam), "$top"));
279*193ad2faSJason M. Bills                 asyncResp->res.result(boost::beast::http::status::bad_request);
280*193ad2faSJason M. Bills                 return;
281*193ad2faSJason M. Bills             }
282*193ad2faSJason M. Bills             if (top < 1 || top > maxEntriesPerPage)
283*193ad2faSJason M. Bills             {
284*193ad2faSJason M. Bills                 messages::addMessageToErrorJson(
285*193ad2faSJason M. Bills                     asyncResp->res.jsonValue,
286*193ad2faSJason M. Bills                     messages::queryParameterOutOfRange(
287*193ad2faSJason M. Bills                         std::to_string(top), "$top",
288*193ad2faSJason M. Bills                         "1-" + std::to_string(maxEntriesPerPage)));
289*193ad2faSJason M. Bills                 asyncResp->res.result(boost::beast::http::status::bad_request);
290*193ad2faSJason M. Bills                 return;
291*193ad2faSJason M. Bills             }
292*193ad2faSJason M. Bills         }
293e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
294e1f26343SJason M. Bills         // it has a duplicate entry for members
295e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
296e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
297e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
298e1f26343SJason M. Bills             "/redfish/v1/"
299e1f26343SJason M. Bills             "$metadata#LogEntryCollection.LogEntryCollection";
300e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
301e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
302e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
303e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
304e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
305e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
306e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
307e1f26343SJason M. Bills 
308e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
309e1f26343SJason M. Bills         // for each entry
310e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
311e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
312e1f26343SJason M. Bills         if (ret < 0)
313e1f26343SJason M. Bills         {
314e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
315f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
316e1f26343SJason M. Bills             return;
317e1f26343SJason M. Bills         }
318e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
319e1f26343SJason M. Bills             journalTmp, sd_journal_close);
320e1f26343SJason M. Bills         journalTmp = nullptr;
321e1f26343SJason M. Bills         uint64_t prevTs = 0;
322e1f26343SJason M. Bills         int index = 0;
323*193ad2faSJason M. Bills         uint64_t entryCount = 0;
324e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
325e1f26343SJason M. Bills         {
326*193ad2faSJason M. Bills             entryCount++;
327*193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
328*193ad2faSJason M. Bills             // start) and top (number of entries to display)
329*193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
330*193ad2faSJason M. Bills             {
331*193ad2faSJason M. Bills                 continue;
332*193ad2faSJason M. Bills             }
333*193ad2faSJason M. Bills 
334e1f26343SJason M. Bills             // Get the entry timestamp
335e1f26343SJason M. Bills             uint64_t curTs = 0;
336e1f26343SJason M. Bills             ret = sd_journal_get_realtime_usec(journal.get(), &curTs);
337e1f26343SJason M. Bills             if (ret < 0)
338e1f26343SJason M. Bills             {
339e1f26343SJason M. Bills                 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
340e1f26343SJason M. Bills                                  << strerror(-ret);
341e1f26343SJason M. Bills                 continue;
342e1f26343SJason M. Bills             }
343e1f26343SJason M. Bills             // If the timestamp isn't unique, increment the index
344e1f26343SJason M. Bills             if (curTs == prevTs)
345e1f26343SJason M. Bills             {
346e1f26343SJason M. Bills                 index++;
347e1f26343SJason M. Bills             }
348e1f26343SJason M. Bills             else
349e1f26343SJason M. Bills             {
350e1f26343SJason M. Bills                 // Otherwise, reset it
351e1f26343SJason M. Bills                 index = 0;
352e1f26343SJason M. Bills             }
353e1f26343SJason M. Bills             // Save the timestamp
354e1f26343SJason M. Bills             prevTs = curTs;
355e1f26343SJason M. Bills 
356e1f26343SJason M. Bills             std::string idStr(std::to_string(curTs));
357e1f26343SJason M. Bills             if (index > 0)
358e1f26343SJason M. Bills             {
359e1f26343SJason M. Bills                 idStr += "_" + std::to_string(index);
360e1f26343SJason M. Bills             }
361e1f26343SJason M. Bills             logEntryArray.push_back({});
362e1f26343SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
363e1f26343SJason M. Bills             if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
364e1f26343SJason M. Bills             {
365f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
366e1f26343SJason M. Bills                 return;
367e1f26343SJason M. Bills             }
368e1f26343SJason M. Bills         }
369*193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
370*193ad2faSJason M. Bills         if (skip + top < entryCount)
371*193ad2faSJason M. Bills         {
372*193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
373*193ad2faSJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
374*193ad2faSJason M. Bills                 std::to_string(skip + top);
375*193ad2faSJason M. Bills         }
376e1f26343SJason M. Bills     }
377e1f26343SJason M. Bills };
378e1f26343SJason M. Bills 
379e1f26343SJason M. Bills class BMCLogEntry : public Node
380e1f26343SJason M. Bills {
381e1f26343SJason M. Bills   public:
382e1f26343SJason M. Bills     BMCLogEntry(CrowApp &app) :
383e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
384e1f26343SJason M. Bills              std::string())
385e1f26343SJason M. Bills     {
386e1f26343SJason M. Bills         entityPrivileges = {
387e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
388e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
389e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
390e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
391e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
392e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
393e1f26343SJason M. Bills     }
394e1f26343SJason M. Bills 
395e1f26343SJason M. Bills   private:
396e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
397e1f26343SJason M. Bills                const std::vector<std::string> &params) override
398e1f26343SJason M. Bills     {
399e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
400e1f26343SJason M. Bills         if (params.size() != 1)
401e1f26343SJason M. Bills         {
402f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
403e1f26343SJason M. Bills             return;
404e1f26343SJason M. Bills         }
405e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
406e1f26343SJason M. Bills         boost::string_view tsStr(params[0]);
407e1f26343SJason M. Bills         boost::string_view indexStr(params[0]);
408e1f26343SJason M. Bills         uint64_t ts = 0;
409e1f26343SJason M. Bills         uint16_t index = 0;
410e1f26343SJason M. Bills         auto underscorePos = tsStr.find("_");
411e1f26343SJason M. Bills         if (underscorePos == tsStr.npos)
412e1f26343SJason M. Bills         {
413e1f26343SJason M. Bills             // Timestamp has no index
414e1f26343SJason M. Bills             ts = strtoull(tsStr.data(), nullptr, 10);
415e1f26343SJason M. Bills         }
416e1f26343SJason M. Bills         else
417e1f26343SJason M. Bills         {
418e1f26343SJason M. Bills             // Timestamp has an index
419e1f26343SJason M. Bills             tsStr.remove_suffix(tsStr.size() - underscorePos + 1);
420e1f26343SJason M. Bills             ts = strtoull(tsStr.data(), nullptr, 10);
421e1f26343SJason M. Bills             indexStr.remove_prefix(underscorePos + 1);
422e1f26343SJason M. Bills             index =
423e1f26343SJason M. Bills                 static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10));
424e1f26343SJason M. Bills         }
425e1f26343SJason M. Bills 
426e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
427e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
428e1f26343SJason M. Bills         if (ret < 0)
429e1f26343SJason M. Bills         {
430e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
431f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
432e1f26343SJason M. Bills             return;
433e1f26343SJason M. Bills         }
434e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
435e1f26343SJason M. Bills             journalTmp, sd_journal_close);
436e1f26343SJason M. Bills         journalTmp = nullptr;
437e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
438e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
439e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
440e1f26343SJason M. Bills         {
441e1f26343SJason M. Bills             sd_journal_next(journal.get());
442e1f26343SJason M. Bills         }
443e1f26343SJason M. Bills         if (fillBMCLogEntryJson(params[0], journal.get(),
444e1f26343SJason M. Bills                                 asyncResp->res.jsonValue) != 0)
445e1f26343SJason M. Bills         {
446f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
447e1f26343SJason M. Bills             return;
448e1f26343SJason M. Bills         }
449e1f26343SJason M. Bills     }
450e1f26343SJason M. Bills };
451e1f26343SJason M. Bills 
452e1f26343SJason M. Bills class CPULogService : public Node
453e1f26343SJason M. Bills {
454e1f26343SJason M. Bills   public:
455e1f26343SJason M. Bills     template <typename CrowApp>
456e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
457e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
4581da66f75SEd Tanous     {
4591da66f75SEd Tanous         // Set the id for SubRoute
4604ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
4611da66f75SEd Tanous         entityPrivileges = {
462e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
463e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
464e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
465e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
466e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
467e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
4681da66f75SEd Tanous     }
4691da66f75SEd Tanous 
4701da66f75SEd Tanous   private:
4711da66f75SEd Tanous     /**
4721da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
4731da66f75SEd Tanous      */
4741da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
4751da66f75SEd Tanous                const std::vector<std::string> &params) override
4761da66f75SEd Tanous     {
477e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
4781da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
479e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
480e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
481e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
482e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
483e1f26343SJason M. Bills             "/redfish/v1/"
4841da66f75SEd Tanous             "$metadata#LogService.LogService";
485e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
486e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
487e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
488e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
489e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
490e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
4911da66f75SEd Tanous             {"Oem",
4921da66f75SEd Tanous              {{"#CpuLog.Immediate",
4931da66f75SEd Tanous                {{"target",
4944ed77cd5SEd Tanous                  "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
4951da66f75SEd Tanous                  "CpuLog.Immediate"}}}}}};
4961da66f75SEd Tanous 
4971da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
498e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
4991da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
5001da66f75SEd Tanous              {{"target",
5014ed77cd5SEd Tanous                "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
5021da66f75SEd Tanous                "CpuLog.SendRawPeci"}}});
5031da66f75SEd Tanous #endif
5041da66f75SEd Tanous     }
5051da66f75SEd Tanous };
5061da66f75SEd Tanous 
507e1f26343SJason M. Bills class CPULogEntryCollection : public Node
5081da66f75SEd Tanous {
5091da66f75SEd Tanous   public:
5101da66f75SEd Tanous     template <typename CrowApp>
511e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
512e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
5131da66f75SEd Tanous     {
5141da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
5151da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
5161da66f75SEd Tanous         Node::json["@odata.id"] =
5174ed77cd5SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
5181da66f75SEd Tanous         entityPrivileges = {
519e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
520e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
521e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
522e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
523e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
524e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
5251da66f75SEd Tanous     }
5261da66f75SEd Tanous 
5271da66f75SEd Tanous   private:
5281da66f75SEd Tanous     /**
5291da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
5301da66f75SEd Tanous      */
5311da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
5321da66f75SEd Tanous                const std::vector<std::string> &params) override
5331da66f75SEd Tanous     {
534e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
5351da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
5361da66f75SEd Tanous         // it has a duplicate entry for members
537e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
538e1f26343SJason M. Bills                                          const boost::system::error_code ec,
5391da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
5401da66f75SEd Tanous             if (ec)
5411da66f75SEd Tanous             {
5421da66f75SEd Tanous                 if (ec.value() !=
5431da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
5441da66f75SEd Tanous                 {
5451da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
5461da66f75SEd Tanous                                      << ec.message();
547f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
5481da66f75SEd Tanous                     return;
5491da66f75SEd Tanous                 }
5501da66f75SEd Tanous             }
551e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
5521da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
553e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
5541da66f75SEd Tanous                 "/redfish/v1/"
5551da66f75SEd Tanous                 "$metadata#LogEntryCollection.LogEntryCollection";
556e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.id"] =
557e1f26343SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
558e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
559e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
560e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
561e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
562e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
5631da66f75SEd Tanous             for (const std::string &objpath : resp)
5641da66f75SEd Tanous             {
5651da66f75SEd Tanous                 // Don't list the immediate log
5664ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
5671da66f75SEd Tanous                 {
5681da66f75SEd Tanous                     continue;
5691da66f75SEd Tanous                 }
5704ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
5714ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
5721da66f75SEd Tanous                 {
573e1f26343SJason M. Bills                     logEntryArray.push_back(
574e1f26343SJason M. Bills                         {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
575e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
5764ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
5771da66f75SEd Tanous                 }
5781da66f75SEd Tanous             }
579e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
580e1f26343SJason M. Bills                 logEntryArray.size();
5811da66f75SEd Tanous         };
5821da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
5831da66f75SEd Tanous             std::move(getLogEntriesCallback),
5841da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
5851da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
5861da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
5874ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
5881da66f75SEd Tanous     }
5891da66f75SEd Tanous };
5901da66f75SEd Tanous 
5911da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
5921da66f75SEd Tanous {
5931da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
5941da66f75SEd Tanous     if (metaIt != cpuLog.end())
5951da66f75SEd Tanous     {
5961da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
5971da66f75SEd Tanous         if (tsIt != metaIt->end())
5981da66f75SEd Tanous         {
5991da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
6001da66f75SEd Tanous             if (logTime != nullptr)
6011da66f75SEd Tanous             {
6021da66f75SEd Tanous                 return *logTime;
6031da66f75SEd Tanous             }
6041da66f75SEd Tanous         }
6051da66f75SEd Tanous     }
6061da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
6071da66f75SEd Tanous 
6081da66f75SEd Tanous     return std::string();
6091da66f75SEd Tanous }
6101da66f75SEd Tanous 
611e1f26343SJason M. Bills class CPULogEntry : public Node
6121da66f75SEd Tanous {
6131da66f75SEd Tanous   public:
614e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
6154ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
6161da66f75SEd Tanous              std::string())
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     void doGet(crow::Response &res, const crow::Request &req,
6291da66f75SEd Tanous                const std::vector<std::string> &params) override
6301da66f75SEd Tanous     {
631e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6321da66f75SEd Tanous         if (params.size() != 1)
6331da66f75SEd Tanous         {
634f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
6351da66f75SEd Tanous             return;
6361da66f75SEd Tanous         }
6374ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
638e1f26343SJason M. Bills         auto getStoredLogCallback =
639e1f26343SJason M. Bills             [asyncResp,
6404ed77cd5SEd Tanous              logId](const boost::system::error_code ec,
641e1f26343SJason M. Bills                     const sdbusplus::message::variant<std::string> &resp) {
6421da66f75SEd Tanous                 if (ec)
6431da66f75SEd Tanous                 {
644e1f26343SJason M. Bills                     BMCWEB_LOG_DEBUG << "failed to get log ec: "
645e1f26343SJason M. Bills                                      << ec.message();
646f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
6471da66f75SEd Tanous                     return;
6481da66f75SEd Tanous                 }
649e1f26343SJason M. Bills                 const std::string *log =
650e1f26343SJason M. Bills                     mapbox::getPtr<const std::string>(resp);
6511da66f75SEd Tanous                 if (log == nullptr)
6521da66f75SEd Tanous                 {
653f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
6541da66f75SEd Tanous                     return;
6551da66f75SEd Tanous                 }
6561da66f75SEd Tanous                 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
6571da66f75SEd Tanous                 if (j.is_discarded())
6581da66f75SEd Tanous                 {
659f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
6601da66f75SEd Tanous                     return;
6611da66f75SEd Tanous                 }
6621da66f75SEd Tanous                 std::string t = getLogCreatedTime(j);
663e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {
6641da66f75SEd Tanous                     {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
665e1f26343SJason M. Bills                     {"@odata.context",
666e1f26343SJason M. Bills                      "/redfish/v1/$metadata#LogEntry.LogEntry"},
6671da66f75SEd Tanous                     {"@odata.id",
6684ed77cd5SEd Tanous                      "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
6694ed77cd5SEd Tanous                          std::to_string(logId)},
6701da66f75SEd Tanous                     {"Name", "CPU Debug Log"},
6714ed77cd5SEd Tanous                     {"Id", logId},
6721da66f75SEd Tanous                     {"EntryType", "Oem"},
6731da66f75SEd Tanous                     {"OemRecordFormat", "Intel CPU Log"},
6741da66f75SEd Tanous                     {"Oem", {{"Intel", std::move(j)}}},
6751da66f75SEd Tanous                     {"Created", std::move(t)}};
6761da66f75SEd Tanous             };
6771da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
6784ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
6794ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
6804ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
6811da66f75SEd Tanous     }
6821da66f75SEd Tanous };
6831da66f75SEd Tanous 
684e1f26343SJason M. Bills class ImmediateCPULog : public Node
6851da66f75SEd Tanous {
6861da66f75SEd Tanous   public:
687e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
6884ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
689e1f26343SJason M. Bills                   "CpuLog.Immediate/")
6901da66f75SEd Tanous     {
6911da66f75SEd Tanous         entityPrivileges = {
692e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
693e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
694e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
695e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
696e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
697e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6981da66f75SEd Tanous     }
6991da66f75SEd Tanous 
7001da66f75SEd Tanous   private:
7011da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
7021da66f75SEd Tanous                 const std::vector<std::string> &params) override
7031da66f75SEd Tanous     {
704e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
7051da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
7061da66f75SEd Tanous             immediateLogMatcher;
7071da66f75SEd Tanous 
7081da66f75SEd Tanous         // Only allow one Immediate Log request at a time
7091da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
7101da66f75SEd Tanous         {
711e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
712f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
7131da66f75SEd Tanous             return;
7141da66f75SEd Tanous         }
7151da66f75SEd Tanous         // Make this static so it survives outside this method
7161da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
7171da66f75SEd Tanous 
7181da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
719e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
7201da66f75SEd Tanous             immediateLogMatcher = nullptr;
7211da66f75SEd Tanous             if (ec)
7221da66f75SEd Tanous             {
7231da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
7241da66f75SEd Tanous                 // completion.
7251da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
7261da66f75SEd Tanous                 {
7271da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
7281da66f75SEd Tanous                 }
7291da66f75SEd Tanous                 return;
7301da66f75SEd Tanous             }
7311da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
7321da66f75SEd Tanous 
733f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
7341da66f75SEd Tanous         });
7351da66f75SEd Tanous 
736e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
7371da66f75SEd Tanous                                                sdbusplus::message::message &m) {
7381da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
7391da66f75SEd Tanous             boost::system::error_code ec;
7401da66f75SEd Tanous             timeout.cancel(ec);
7411da66f75SEd Tanous             if (ec)
7421da66f75SEd Tanous             {
7431da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
7441da66f75SEd Tanous             }
7454ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
7461da66f75SEd Tanous             boost::container::flat_map<
7471da66f75SEd Tanous                 std::string,
7481da66f75SEd Tanous                 boost::container::flat_map<
7491da66f75SEd Tanous                     std::string, sdbusplus::message::variant<std::string>>>
7504ed77cd5SEd Tanous                 interfacesAdded;
7514ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
7521da66f75SEd Tanous             const std::string *log = mapbox::getPtr<const std::string>(
7534ed77cd5SEd Tanous                 interfacesAdded[cpuLogInterface]["Log"]);
7541da66f75SEd Tanous             if (log == nullptr)
7551da66f75SEd Tanous             {
756f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
7571da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
7581da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
7591da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
7601da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
7611da66f75SEd Tanous                 // be the last thing done.
7621da66f75SEd Tanous                 immediateLogMatcher = nullptr;
7631da66f75SEd Tanous                 return;
7641da66f75SEd Tanous             }
7651da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
7661da66f75SEd Tanous             if (j.is_discarded())
7671da66f75SEd Tanous             {
768f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
7691da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
7701da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
7711da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
7721da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
7731da66f75SEd Tanous                 // be the last thing done.
7741da66f75SEd Tanous                 immediateLogMatcher = nullptr;
7751da66f75SEd Tanous                 return;
7761da66f75SEd Tanous             }
7771da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
778e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
7791da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
7801da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
7811da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
7821da66f75SEd Tanous                 {"EntryType", "Oem"},
7831da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
7841da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
7851da66f75SEd Tanous                 {"Created", std::move(t)}};
7861da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
7871da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
7881da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
7891da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
7901da66f75SEd Tanous             // thing done.
7911da66f75SEd Tanous             immediateLogMatcher = nullptr;
7921da66f75SEd Tanous         };
7931da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
7941da66f75SEd Tanous             *crow::connections::systemBus,
7951da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
7964ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
7971da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
7981da66f75SEd Tanous 
7991da66f75SEd Tanous         auto generateImmediateLogCallback =
800e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
8011da66f75SEd Tanous                         const std::string &resp) {
8021da66f75SEd Tanous                 if (ec)
8031da66f75SEd Tanous                 {
8041da66f75SEd Tanous                     if (ec.value() ==
8051da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
8061da66f75SEd Tanous                     {
807f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
8081da66f75SEd Tanous                     }
8091da66f75SEd Tanous                     else
8101da66f75SEd Tanous                     {
811f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
8121da66f75SEd Tanous                     }
8131da66f75SEd Tanous                     boost::system::error_code timeoutec;
8141da66f75SEd Tanous                     timeout.cancel(timeoutec);
8151da66f75SEd Tanous                     if (timeoutec)
8161da66f75SEd Tanous                     {
8171da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
8181da66f75SEd Tanous                                          << timeoutec;
8191da66f75SEd Tanous                     }
8201da66f75SEd Tanous                     immediateLogMatcher = nullptr;
8211da66f75SEd Tanous                     return;
8221da66f75SEd Tanous                 }
8231da66f75SEd Tanous             };
8241da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
8254ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
8264ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
8271da66f75SEd Tanous     }
8281da66f75SEd Tanous };
8291da66f75SEd Tanous 
830e1f26343SJason M. Bills class SendRawPECI : public Node
8311da66f75SEd Tanous {
8321da66f75SEd Tanous   public:
833e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
8344ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
835e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
8361da66f75SEd Tanous     {
8371da66f75SEd Tanous         entityPrivileges = {
8381da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
8391da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
8401da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
8411da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
8421da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
8431da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
8441da66f75SEd Tanous     }
8451da66f75SEd Tanous 
8461da66f75SEd Tanous   private:
8471da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
8481da66f75SEd Tanous                 const std::vector<std::string> &params) override
8491da66f75SEd Tanous     {
850e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
8511da66f75SEd Tanous         // Get the Raw PECI command from the request
852e1f26343SJason M. Bills         nlohmann::json rawPECICmd;
853e1f26343SJason M. Bills         if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
8541da66f75SEd Tanous         {
8551da66f75SEd Tanous             return;
8561da66f75SEd Tanous         }
8571da66f75SEd Tanous         // Get the Client Address from the request
858e1f26343SJason M. Bills         nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
859e1f26343SJason M. Bills         if (caIt == rawPECICmd.end())
8601da66f75SEd Tanous         {
861f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "ClientAddress",
862f12894f8SJason M. Bills                                       "/ClientAddress");
8631da66f75SEd Tanous             return;
8641da66f75SEd Tanous         }
8651da66f75SEd Tanous         const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
8661da66f75SEd Tanous         if (ca == nullptr)
8671da66f75SEd Tanous         {
868f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, caIt->dump(),
869f12894f8SJason M. Bills                                              "ClientAddress", "/ClientAddress");
8701da66f75SEd Tanous             return;
8711da66f75SEd Tanous         }
8721da66f75SEd Tanous         // Get the Read Length from the request
8731da66f75SEd Tanous         const uint8_t clientAddress = static_cast<uint8_t>(*ca);
874e1f26343SJason M. Bills         nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
875e1f26343SJason M. Bills         if (rlIt == rawPECICmd.end())
8761da66f75SEd Tanous         {
877f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "ReadLength",
8781da66f75SEd Tanous                                       "/ReadLength");
8791da66f75SEd Tanous             return;
8801da66f75SEd Tanous         }
8811da66f75SEd Tanous         const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
8821da66f75SEd Tanous         if (rl == nullptr)
8831da66f75SEd Tanous         {
884f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, rlIt->dump(),
885f12894f8SJason M. Bills                                              "ReadLength", "/ReadLength");
8861da66f75SEd Tanous             return;
8871da66f75SEd Tanous         }
8881da66f75SEd Tanous         // Get the PECI Command from the request
8891da66f75SEd Tanous         const uint32_t readLength = static_cast<uint32_t>(*rl);
890e1f26343SJason M. Bills         nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
891e1f26343SJason M. Bills         if (pcIt == rawPECICmd.end())
8921da66f75SEd Tanous         {
893f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "PECICommand",
8941da66f75SEd Tanous                                       "/PECICommand");
8951da66f75SEd Tanous             return;
8961da66f75SEd Tanous         }
8971da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
8981da66f75SEd Tanous         for (auto pc : *pcIt)
8991da66f75SEd Tanous         {
9001da66f75SEd Tanous             const uint64_t *val = pc.get_ptr<const uint64_t *>();
9011da66f75SEd Tanous             if (val == nullptr)
9021da66f75SEd Tanous             {
9031da66f75SEd Tanous                 messages::propertyValueTypeError(
904f12894f8SJason M. Bills                     asyncResp->res, pc.dump(),
905f12894f8SJason M. Bills                     "PECICommand/" + std::to_string(peciCommand.size()),
9061da66f75SEd Tanous                     "/PECICommand");
9071da66f75SEd Tanous                 return;
9081da66f75SEd Tanous             }
9091da66f75SEd Tanous             peciCommand.push_back(static_cast<uint8_t>(*val));
9101da66f75SEd Tanous         }
9111da66f75SEd Tanous         // Callback to return the Raw PECI response
912e1f26343SJason M. Bills         auto sendRawPECICallback =
913e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
9141da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
9151da66f75SEd Tanous                 if (ec)
9161da66f75SEd Tanous                 {
9171da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
9181da66f75SEd Tanous                                      << ec.message();
919f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
9201da66f75SEd Tanous                     return;
9211da66f75SEd Tanous                 }
922e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
9231da66f75SEd Tanous                                             {"PECIResponse", resp}};
9241da66f75SEd Tanous             };
9251da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
9261da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
927e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
928e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
9294ed77cd5SEd Tanous             peciCommand);
9301da66f75SEd Tanous     }
9311da66f75SEd Tanous };
9321da66f75SEd Tanous 
9331da66f75SEd Tanous } // namespace redfish
934