xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 35a62c7ca3990858cc2e04b10c4754e92c4a3e41)
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);
241193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
242193ad2faSJason M. Bills         long skip = 0;
243193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
244193ad2faSJason M. Bills         char *skipParam = req.urlParams.get("$skip");
245193ad2faSJason M. Bills         if (skipParam != nullptr)
246193ad2faSJason M. Bills         {
247193ad2faSJason M. Bills             char *ptr = nullptr;
248193ad2faSJason M. Bills             skip = std::strtol(skipParam, &ptr, 10);
249193ad2faSJason M. Bills             if (*skipParam == '\0' || *ptr != '\0')
250193ad2faSJason M. Bills             {
251*35a62c7cSJason M. Bills 
252193ad2faSJason M. Bills                 messages::queryParameterValueTypeError(
253*35a62c7cSJason M. Bills                     asyncResp->res, std::string(skipParam), "$skip");
254193ad2faSJason M. Bills                 return;
255193ad2faSJason M. Bills             }
256193ad2faSJason M. Bills             if (skip < 0)
257193ad2faSJason M. Bills             {
258*35a62c7cSJason M. Bills 
259*35a62c7cSJason M. Bills                 messages::queryParameterOutOfRange(asyncResp->res,
260*35a62c7cSJason M. Bills                                                    std::to_string(skip),
261*35a62c7cSJason M. Bills                                                    "$skip", "greater than 0");
262193ad2faSJason M. Bills                 return;
263193ad2faSJason M. Bills             }
264193ad2faSJason M. Bills         }
265193ad2faSJason M. Bills         char *topParam = req.urlParams.get("$top");
266193ad2faSJason M. Bills         if (topParam != nullptr)
267193ad2faSJason M. Bills         {
268193ad2faSJason M. Bills             char *ptr = nullptr;
269193ad2faSJason M. Bills             top = std::strtol(topParam, &ptr, 10);
270193ad2faSJason M. Bills             if (*topParam == '\0' || *ptr != '\0')
271193ad2faSJason M. Bills             {
272193ad2faSJason M. Bills                 messages::queryParameterValueTypeError(
273*35a62c7cSJason M. Bills                     asyncResp->res, std::string(topParam), "$top");
274193ad2faSJason M. Bills                 return;
275193ad2faSJason M. Bills             }
276193ad2faSJason M. Bills             if (top < 1 || top > maxEntriesPerPage)
277193ad2faSJason M. Bills             {
278*35a62c7cSJason M. Bills 
279193ad2faSJason M. Bills                 messages::queryParameterOutOfRange(
280*35a62c7cSJason M. Bills                     asyncResp->res, std::to_string(top), "$top",
281*35a62c7cSJason M. Bills                     "1-" + std::to_string(maxEntriesPerPage));
282193ad2faSJason M. Bills                 asyncResp->res.result(boost::beast::http::status::bad_request);
283193ad2faSJason M. Bills                 return;
284193ad2faSJason M. Bills             }
285193ad2faSJason M. Bills         }
286e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
287e1f26343SJason M. Bills         // it has a duplicate entry for members
288e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
289e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
290e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
291e1f26343SJason M. Bills             "/redfish/v1/"
292e1f26343SJason M. Bills             "$metadata#LogEntryCollection.LogEntryCollection";
293e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
294e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
295e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
296e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
297e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
298e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
299e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
300e1f26343SJason M. Bills 
301e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
302e1f26343SJason M. Bills         // for each entry
303e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
304e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
305e1f26343SJason M. Bills         if (ret < 0)
306e1f26343SJason M. Bills         {
307e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
308f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
309e1f26343SJason M. Bills             return;
310e1f26343SJason M. Bills         }
311e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
312e1f26343SJason M. Bills             journalTmp, sd_journal_close);
313e1f26343SJason M. Bills         journalTmp = nullptr;
314e1f26343SJason M. Bills         uint64_t prevTs = 0;
315e1f26343SJason M. Bills         int index = 0;
316193ad2faSJason M. Bills         uint64_t entryCount = 0;
317e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
318e1f26343SJason M. Bills         {
319193ad2faSJason M. Bills             entryCount++;
320193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
321193ad2faSJason M. Bills             // start) and top (number of entries to display)
322193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
323193ad2faSJason M. Bills             {
324193ad2faSJason M. Bills                 continue;
325193ad2faSJason M. Bills             }
326193ad2faSJason M. Bills 
327e1f26343SJason M. Bills             // Get the entry timestamp
328e1f26343SJason M. Bills             uint64_t curTs = 0;
329e1f26343SJason M. Bills             ret = sd_journal_get_realtime_usec(journal.get(), &curTs);
330e1f26343SJason M. Bills             if (ret < 0)
331e1f26343SJason M. Bills             {
332e1f26343SJason M. Bills                 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
333e1f26343SJason M. Bills                                  << strerror(-ret);
334e1f26343SJason M. Bills                 continue;
335e1f26343SJason M. Bills             }
336e1f26343SJason M. Bills             // If the timestamp isn't unique, increment the index
337e1f26343SJason M. Bills             if (curTs == prevTs)
338e1f26343SJason M. Bills             {
339e1f26343SJason M. Bills                 index++;
340e1f26343SJason M. Bills             }
341e1f26343SJason M. Bills             else
342e1f26343SJason M. Bills             {
343e1f26343SJason M. Bills                 // Otherwise, reset it
344e1f26343SJason M. Bills                 index = 0;
345e1f26343SJason M. Bills             }
346e1f26343SJason M. Bills             // Save the timestamp
347e1f26343SJason M. Bills             prevTs = curTs;
348e1f26343SJason M. Bills 
349e1f26343SJason M. Bills             std::string idStr(std::to_string(curTs));
350e1f26343SJason M. Bills             if (index > 0)
351e1f26343SJason M. Bills             {
352e1f26343SJason M. Bills                 idStr += "_" + std::to_string(index);
353e1f26343SJason M. Bills             }
354e1f26343SJason M. Bills             logEntryArray.push_back({});
355e1f26343SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
356e1f26343SJason M. Bills             if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
357e1f26343SJason M. Bills             {
358f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
359e1f26343SJason M. Bills                 return;
360e1f26343SJason M. Bills             }
361e1f26343SJason M. Bills         }
362193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
363193ad2faSJason M. Bills         if (skip + top < entryCount)
364193ad2faSJason M. Bills         {
365193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
366193ad2faSJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
367193ad2faSJason M. Bills                 std::to_string(skip + top);
368193ad2faSJason M. Bills         }
369e1f26343SJason M. Bills     }
370e1f26343SJason M. Bills };
371e1f26343SJason M. Bills 
372e1f26343SJason M. Bills class BMCLogEntry : public Node
373e1f26343SJason M. Bills {
374e1f26343SJason M. Bills   public:
375e1f26343SJason M. Bills     BMCLogEntry(CrowApp &app) :
376e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
377e1f26343SJason M. Bills              std::string())
378e1f26343SJason M. Bills     {
379e1f26343SJason M. Bills         entityPrivileges = {
380e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
381e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
382e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
383e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
384e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
385e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
386e1f26343SJason M. Bills     }
387e1f26343SJason M. Bills 
388e1f26343SJason M. Bills   private:
389e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
390e1f26343SJason M. Bills                const std::vector<std::string> &params) override
391e1f26343SJason M. Bills     {
392e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
393e1f26343SJason M. Bills         if (params.size() != 1)
394e1f26343SJason M. Bills         {
395f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
396e1f26343SJason M. Bills             return;
397e1f26343SJason M. Bills         }
398e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
399e1f26343SJason M. Bills         boost::string_view tsStr(params[0]);
400e1f26343SJason M. Bills         boost::string_view indexStr(params[0]);
401e1f26343SJason M. Bills         uint64_t ts = 0;
402e1f26343SJason M. Bills         uint16_t index = 0;
403e1f26343SJason M. Bills         auto underscorePos = tsStr.find("_");
404e1f26343SJason M. Bills         if (underscorePos == tsStr.npos)
405e1f26343SJason M. Bills         {
406e1f26343SJason M. Bills             // Timestamp has no index
407e1f26343SJason M. Bills             ts = strtoull(tsStr.data(), nullptr, 10);
408e1f26343SJason M. Bills         }
409e1f26343SJason M. Bills         else
410e1f26343SJason M. Bills         {
411e1f26343SJason M. Bills             // Timestamp has an index
412e1f26343SJason M. Bills             tsStr.remove_suffix(tsStr.size() - underscorePos + 1);
413e1f26343SJason M. Bills             ts = strtoull(tsStr.data(), nullptr, 10);
414e1f26343SJason M. Bills             indexStr.remove_prefix(underscorePos + 1);
415e1f26343SJason M. Bills             index =
416e1f26343SJason M. Bills                 static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10));
417e1f26343SJason M. Bills         }
418e1f26343SJason M. Bills 
419e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
420e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
421e1f26343SJason M. Bills         if (ret < 0)
422e1f26343SJason M. Bills         {
423e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
424f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
425e1f26343SJason M. Bills             return;
426e1f26343SJason M. Bills         }
427e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
428e1f26343SJason M. Bills             journalTmp, sd_journal_close);
429e1f26343SJason M. Bills         journalTmp = nullptr;
430e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
431e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
432e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
433e1f26343SJason M. Bills         {
434e1f26343SJason M. Bills             sd_journal_next(journal.get());
435e1f26343SJason M. Bills         }
436e1f26343SJason M. Bills         if (fillBMCLogEntryJson(params[0], journal.get(),
437e1f26343SJason M. Bills                                 asyncResp->res.jsonValue) != 0)
438e1f26343SJason M. Bills         {
439f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
440e1f26343SJason M. Bills             return;
441e1f26343SJason M. Bills         }
442e1f26343SJason M. Bills     }
443e1f26343SJason M. Bills };
444e1f26343SJason M. Bills 
445e1f26343SJason M. Bills class CPULogService : public Node
446e1f26343SJason M. Bills {
447e1f26343SJason M. Bills   public:
448e1f26343SJason M. Bills     template <typename CrowApp>
449e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
450e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
4511da66f75SEd Tanous     {
4521da66f75SEd Tanous         // Set the id for SubRoute
4534ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
4541da66f75SEd Tanous         entityPrivileges = {
455e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
456e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
457e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
458e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
459e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
460e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
4611da66f75SEd Tanous     }
4621da66f75SEd Tanous 
4631da66f75SEd Tanous   private:
4641da66f75SEd Tanous     /**
4651da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
4661da66f75SEd Tanous      */
4671da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
4681da66f75SEd Tanous                const std::vector<std::string> &params) override
4691da66f75SEd Tanous     {
470e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
4711da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
472e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
473e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
474e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
475e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
476e1f26343SJason M. Bills             "/redfish/v1/"
4771da66f75SEd Tanous             "$metadata#LogService.LogService";
478e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
479e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
480e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
481e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
482e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
483e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
4841da66f75SEd Tanous             {"Oem",
4851da66f75SEd Tanous              {{"#CpuLog.Immediate",
4861da66f75SEd Tanous                {{"target",
4874ed77cd5SEd Tanous                  "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
4881da66f75SEd Tanous                  "CpuLog.Immediate"}}}}}};
4891da66f75SEd Tanous 
4901da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
491e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
4921da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
4931da66f75SEd Tanous              {{"target",
4944ed77cd5SEd Tanous                "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
4951da66f75SEd Tanous                "CpuLog.SendRawPeci"}}});
4961da66f75SEd Tanous #endif
4971da66f75SEd Tanous     }
4981da66f75SEd Tanous };
4991da66f75SEd Tanous 
500e1f26343SJason M. Bills class CPULogEntryCollection : public Node
5011da66f75SEd Tanous {
5021da66f75SEd Tanous   public:
5031da66f75SEd Tanous     template <typename CrowApp>
504e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
505e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
5061da66f75SEd Tanous     {
5071da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
5081da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
5091da66f75SEd Tanous         Node::json["@odata.id"] =
5104ed77cd5SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
5111da66f75SEd Tanous         entityPrivileges = {
512e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
513e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
514e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
515e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
516e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
517e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
5181da66f75SEd Tanous     }
5191da66f75SEd Tanous 
5201da66f75SEd Tanous   private:
5211da66f75SEd Tanous     /**
5221da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
5231da66f75SEd Tanous      */
5241da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
5251da66f75SEd Tanous                const std::vector<std::string> &params) override
5261da66f75SEd Tanous     {
527e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
5281da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
5291da66f75SEd Tanous         // it has a duplicate entry for members
530e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
531e1f26343SJason M. Bills                                          const boost::system::error_code ec,
5321da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
5331da66f75SEd Tanous             if (ec)
5341da66f75SEd Tanous             {
5351da66f75SEd Tanous                 if (ec.value() !=
5361da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
5371da66f75SEd Tanous                 {
5381da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
5391da66f75SEd Tanous                                      << ec.message();
540f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
5411da66f75SEd Tanous                     return;
5421da66f75SEd Tanous                 }
5431da66f75SEd Tanous             }
544e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
5451da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
546e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
5471da66f75SEd Tanous                 "/redfish/v1/"
5481da66f75SEd Tanous                 "$metadata#LogEntryCollection.LogEntryCollection";
549e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.id"] =
550e1f26343SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
551e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
552e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
553e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
554e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
555e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
5561da66f75SEd Tanous             for (const std::string &objpath : resp)
5571da66f75SEd Tanous             {
5581da66f75SEd Tanous                 // Don't list the immediate log
5594ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
5601da66f75SEd Tanous                 {
5611da66f75SEd Tanous                     continue;
5621da66f75SEd Tanous                 }
5634ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
5644ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
5651da66f75SEd Tanous                 {
566e1f26343SJason M. Bills                     logEntryArray.push_back(
567e1f26343SJason M. Bills                         {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
568e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
5694ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
5701da66f75SEd Tanous                 }
5711da66f75SEd Tanous             }
572e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
573e1f26343SJason M. Bills                 logEntryArray.size();
5741da66f75SEd Tanous         };
5751da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
5761da66f75SEd Tanous             std::move(getLogEntriesCallback),
5771da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
5781da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
5791da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
5804ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
5811da66f75SEd Tanous     }
5821da66f75SEd Tanous };
5831da66f75SEd Tanous 
5841da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
5851da66f75SEd Tanous {
5861da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
5871da66f75SEd Tanous     if (metaIt != cpuLog.end())
5881da66f75SEd Tanous     {
5891da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
5901da66f75SEd Tanous         if (tsIt != metaIt->end())
5911da66f75SEd Tanous         {
5921da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
5931da66f75SEd Tanous             if (logTime != nullptr)
5941da66f75SEd Tanous             {
5951da66f75SEd Tanous                 return *logTime;
5961da66f75SEd Tanous             }
5971da66f75SEd Tanous         }
5981da66f75SEd Tanous     }
5991da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
6001da66f75SEd Tanous 
6011da66f75SEd Tanous     return std::string();
6021da66f75SEd Tanous }
6031da66f75SEd Tanous 
604e1f26343SJason M. Bills class CPULogEntry : public Node
6051da66f75SEd Tanous {
6061da66f75SEd Tanous   public:
607e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
6084ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
6091da66f75SEd Tanous              std::string())
6101da66f75SEd Tanous     {
6111da66f75SEd Tanous         entityPrivileges = {
612e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
613e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
614e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
615e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
616e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
617e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6181da66f75SEd Tanous     }
6191da66f75SEd Tanous 
6201da66f75SEd Tanous   private:
6211da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
6221da66f75SEd Tanous                const std::vector<std::string> &params) override
6231da66f75SEd Tanous     {
624e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6251da66f75SEd Tanous         if (params.size() != 1)
6261da66f75SEd Tanous         {
627f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
6281da66f75SEd Tanous             return;
6291da66f75SEd Tanous         }
6304ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
631e1f26343SJason M. Bills         auto getStoredLogCallback =
632e1f26343SJason M. Bills             [asyncResp,
6334ed77cd5SEd Tanous              logId](const boost::system::error_code ec,
634e1f26343SJason M. Bills                     const sdbusplus::message::variant<std::string> &resp) {
6351da66f75SEd Tanous                 if (ec)
6361da66f75SEd Tanous                 {
637e1f26343SJason M. Bills                     BMCWEB_LOG_DEBUG << "failed to get log ec: "
638e1f26343SJason M. Bills                                      << ec.message();
639f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
6401da66f75SEd Tanous                     return;
6411da66f75SEd Tanous                 }
642e1f26343SJason M. Bills                 const std::string *log =
643e1f26343SJason M. Bills                     mapbox::getPtr<const std::string>(resp);
6441da66f75SEd Tanous                 if (log == nullptr)
6451da66f75SEd Tanous                 {
646f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
6471da66f75SEd Tanous                     return;
6481da66f75SEd Tanous                 }
6491da66f75SEd Tanous                 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
6501da66f75SEd Tanous                 if (j.is_discarded())
6511da66f75SEd Tanous                 {
652f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
6531da66f75SEd Tanous                     return;
6541da66f75SEd Tanous                 }
6551da66f75SEd Tanous                 std::string t = getLogCreatedTime(j);
656e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {
6571da66f75SEd Tanous                     {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
658e1f26343SJason M. Bills                     {"@odata.context",
659e1f26343SJason M. Bills                      "/redfish/v1/$metadata#LogEntry.LogEntry"},
6601da66f75SEd Tanous                     {"@odata.id",
6614ed77cd5SEd Tanous                      "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
6624ed77cd5SEd Tanous                          std::to_string(logId)},
6631da66f75SEd Tanous                     {"Name", "CPU Debug Log"},
6644ed77cd5SEd Tanous                     {"Id", logId},
6651da66f75SEd Tanous                     {"EntryType", "Oem"},
6661da66f75SEd Tanous                     {"OemRecordFormat", "Intel CPU Log"},
6671da66f75SEd Tanous                     {"Oem", {{"Intel", std::move(j)}}},
6681da66f75SEd Tanous                     {"Created", std::move(t)}};
6691da66f75SEd Tanous             };
6701da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
6714ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
6724ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
6734ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
6741da66f75SEd Tanous     }
6751da66f75SEd Tanous };
6761da66f75SEd Tanous 
677e1f26343SJason M. Bills class ImmediateCPULog : public Node
6781da66f75SEd Tanous {
6791da66f75SEd Tanous   public:
680e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
6814ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
682e1f26343SJason M. Bills                   "CpuLog.Immediate/")
6831da66f75SEd Tanous     {
6841da66f75SEd Tanous         entityPrivileges = {
685e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
686e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
687e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
688e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
689e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
690e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6911da66f75SEd Tanous     }
6921da66f75SEd Tanous 
6931da66f75SEd Tanous   private:
6941da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
6951da66f75SEd Tanous                 const std::vector<std::string> &params) override
6961da66f75SEd Tanous     {
697e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6981da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
6991da66f75SEd Tanous             immediateLogMatcher;
7001da66f75SEd Tanous 
7011da66f75SEd Tanous         // Only allow one Immediate Log request at a time
7021da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
7031da66f75SEd Tanous         {
704e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
705f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
7061da66f75SEd Tanous             return;
7071da66f75SEd Tanous         }
7081da66f75SEd Tanous         // Make this static so it survives outside this method
7091da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
7101da66f75SEd Tanous 
7111da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
712e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
7131da66f75SEd Tanous             immediateLogMatcher = nullptr;
7141da66f75SEd Tanous             if (ec)
7151da66f75SEd Tanous             {
7161da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
7171da66f75SEd Tanous                 // completion.
7181da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
7191da66f75SEd Tanous                 {
7201da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
7211da66f75SEd Tanous                 }
7221da66f75SEd Tanous                 return;
7231da66f75SEd Tanous             }
7241da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
7251da66f75SEd Tanous 
726f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
7271da66f75SEd Tanous         });
7281da66f75SEd Tanous 
729e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
7301da66f75SEd Tanous                                                sdbusplus::message::message &m) {
7311da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
7321da66f75SEd Tanous             boost::system::error_code ec;
7331da66f75SEd Tanous             timeout.cancel(ec);
7341da66f75SEd Tanous             if (ec)
7351da66f75SEd Tanous             {
7361da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
7371da66f75SEd Tanous             }
7384ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
7391da66f75SEd Tanous             boost::container::flat_map<
7401da66f75SEd Tanous                 std::string,
7411da66f75SEd Tanous                 boost::container::flat_map<
7421da66f75SEd Tanous                     std::string, sdbusplus::message::variant<std::string>>>
7434ed77cd5SEd Tanous                 interfacesAdded;
7444ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
7451da66f75SEd Tanous             const std::string *log = mapbox::getPtr<const std::string>(
7464ed77cd5SEd Tanous                 interfacesAdded[cpuLogInterface]["Log"]);
7471da66f75SEd Tanous             if (log == nullptr)
7481da66f75SEd Tanous             {
749f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
7501da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
7511da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
7521da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
7531da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
7541da66f75SEd Tanous                 // be the last thing done.
7551da66f75SEd Tanous                 immediateLogMatcher = nullptr;
7561da66f75SEd Tanous                 return;
7571da66f75SEd Tanous             }
7581da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
7591da66f75SEd Tanous             if (j.is_discarded())
7601da66f75SEd Tanous             {
761f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
7621da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
7631da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
7641da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
7651da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
7661da66f75SEd Tanous                 // be the last thing done.
7671da66f75SEd Tanous                 immediateLogMatcher = nullptr;
7681da66f75SEd Tanous                 return;
7691da66f75SEd Tanous             }
7701da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
771e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
7721da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
7731da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
7741da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
7751da66f75SEd Tanous                 {"EntryType", "Oem"},
7761da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
7771da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
7781da66f75SEd Tanous                 {"Created", std::move(t)}};
7791da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
7801da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
7811da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
7821da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
7831da66f75SEd Tanous             // thing done.
7841da66f75SEd Tanous             immediateLogMatcher = nullptr;
7851da66f75SEd Tanous         };
7861da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
7871da66f75SEd Tanous             *crow::connections::systemBus,
7881da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
7894ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
7901da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
7911da66f75SEd Tanous 
7921da66f75SEd Tanous         auto generateImmediateLogCallback =
793e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
7941da66f75SEd Tanous                         const std::string &resp) {
7951da66f75SEd Tanous                 if (ec)
7961da66f75SEd Tanous                 {
7971da66f75SEd Tanous                     if (ec.value() ==
7981da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
7991da66f75SEd Tanous                     {
800f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
8011da66f75SEd Tanous                     }
8021da66f75SEd Tanous                     else
8031da66f75SEd Tanous                     {
804f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
8051da66f75SEd Tanous                     }
8061da66f75SEd Tanous                     boost::system::error_code timeoutec;
8071da66f75SEd Tanous                     timeout.cancel(timeoutec);
8081da66f75SEd Tanous                     if (timeoutec)
8091da66f75SEd Tanous                     {
8101da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
8111da66f75SEd Tanous                                          << timeoutec;
8121da66f75SEd Tanous                     }
8131da66f75SEd Tanous                     immediateLogMatcher = nullptr;
8141da66f75SEd Tanous                     return;
8151da66f75SEd Tanous                 }
8161da66f75SEd Tanous             };
8171da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
8184ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
8194ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
8201da66f75SEd Tanous     }
8211da66f75SEd Tanous };
8221da66f75SEd Tanous 
823e1f26343SJason M. Bills class SendRawPECI : public Node
8241da66f75SEd Tanous {
8251da66f75SEd Tanous   public:
826e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
8274ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
828e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
8291da66f75SEd Tanous     {
8301da66f75SEd Tanous         entityPrivileges = {
8311da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
8321da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
8331da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
8341da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
8351da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
8361da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
8371da66f75SEd Tanous     }
8381da66f75SEd Tanous 
8391da66f75SEd Tanous   private:
8401da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
8411da66f75SEd Tanous                 const std::vector<std::string> &params) override
8421da66f75SEd Tanous     {
843e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
8441da66f75SEd Tanous         // Get the Raw PECI command from the request
845e1f26343SJason M. Bills         nlohmann::json rawPECICmd;
846e1f26343SJason M. Bills         if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
8471da66f75SEd Tanous         {
8481da66f75SEd Tanous             return;
8491da66f75SEd Tanous         }
8501da66f75SEd Tanous         // Get the Client Address from the request
851e1f26343SJason M. Bills         nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
852e1f26343SJason M. Bills         if (caIt == rawPECICmd.end())
8531da66f75SEd Tanous         {
854f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "ClientAddress",
855f12894f8SJason M. Bills                                       "/ClientAddress");
8561da66f75SEd Tanous             return;
8571da66f75SEd Tanous         }
8581da66f75SEd Tanous         const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
8591da66f75SEd Tanous         if (ca == nullptr)
8601da66f75SEd Tanous         {
861f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, caIt->dump(),
862f12894f8SJason M. Bills                                              "ClientAddress", "/ClientAddress");
8631da66f75SEd Tanous             return;
8641da66f75SEd Tanous         }
8651da66f75SEd Tanous         // Get the Read Length from the request
8661da66f75SEd Tanous         const uint8_t clientAddress = static_cast<uint8_t>(*ca);
867e1f26343SJason M. Bills         nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
868e1f26343SJason M. Bills         if (rlIt == rawPECICmd.end())
8691da66f75SEd Tanous         {
870f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "ReadLength",
8711da66f75SEd Tanous                                       "/ReadLength");
8721da66f75SEd Tanous             return;
8731da66f75SEd Tanous         }
8741da66f75SEd Tanous         const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
8751da66f75SEd Tanous         if (rl == nullptr)
8761da66f75SEd Tanous         {
877f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, rlIt->dump(),
878f12894f8SJason M. Bills                                              "ReadLength", "/ReadLength");
8791da66f75SEd Tanous             return;
8801da66f75SEd Tanous         }
8811da66f75SEd Tanous         // Get the PECI Command from the request
8821da66f75SEd Tanous         const uint32_t readLength = static_cast<uint32_t>(*rl);
883e1f26343SJason M. Bills         nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
884e1f26343SJason M. Bills         if (pcIt == rawPECICmd.end())
8851da66f75SEd Tanous         {
886f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "PECICommand",
8871da66f75SEd Tanous                                       "/PECICommand");
8881da66f75SEd Tanous             return;
8891da66f75SEd Tanous         }
8901da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
8911da66f75SEd Tanous         for (auto pc : *pcIt)
8921da66f75SEd Tanous         {
8931da66f75SEd Tanous             const uint64_t *val = pc.get_ptr<const uint64_t *>();
8941da66f75SEd Tanous             if (val == nullptr)
8951da66f75SEd Tanous             {
8961da66f75SEd Tanous                 messages::propertyValueTypeError(
897f12894f8SJason M. Bills                     asyncResp->res, pc.dump(),
898f12894f8SJason M. Bills                     "PECICommand/" + std::to_string(peciCommand.size()),
8991da66f75SEd Tanous                     "/PECICommand");
9001da66f75SEd Tanous                 return;
9011da66f75SEd Tanous             }
9021da66f75SEd Tanous             peciCommand.push_back(static_cast<uint8_t>(*val));
9031da66f75SEd Tanous         }
9041da66f75SEd Tanous         // Callback to return the Raw PECI response
905e1f26343SJason M. Bills         auto sendRawPECICallback =
906e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
9071da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
9081da66f75SEd Tanous                 if (ec)
9091da66f75SEd Tanous                 {
9101da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
9111da66f75SEd Tanous                                      << ec.message();
912f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
9131da66f75SEd Tanous                     return;
9141da66f75SEd Tanous                 }
915e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
9161da66f75SEd Tanous                                             {"PECIResponse", resp}};
9171da66f75SEd Tanous             };
9181da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
9191da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
920e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
921e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
9224ed77cd5SEd Tanous             peciCommand);
9231da66f75SEd Tanous     }
9241da66f75SEd Tanous };
9251da66f75SEd Tanous 
9261da66f75SEd Tanous } // namespace redfish
927