xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision e1f26343d52c4ea8b4c491f4323744d72b34e6ba)
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 
20*e1f26343SJason M. Bills #include <systemd/sd-journal.h>
21*e1f26343SJason M. Bills 
221da66f75SEd Tanous #include <boost/container/flat_map.hpp>
23*e1f26343SJason 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";
35*e1f26343SJason 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 = {
51*e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
52*e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
53*e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
54*e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
55*e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
56*e1f26343SJason 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     {
66*e1f26343SJason 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
69*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
701da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
71*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
721da66f75SEd Tanous             "/redfish/v1/"
731da66f75SEd Tanous             "$metadata#LogServiceCollection.LogServiceCollection";
74*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
75*e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
76*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
77*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
781da66f75SEd Tanous             "Collection of LogServices for this Manager";
79*e1f26343SJason M. Bills         nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"];
801da66f75SEd Tanous         logserviceArray = nlohmann::json::array();
81*e1f26343SJason M. Bills         logserviceArray.push_back(
82*e1f26343SJason 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
87*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
88*e1f26343SJason M. Bills             logserviceArray.size();
891da66f75SEd Tanous     }
901da66f75SEd Tanous };
911da66f75SEd Tanous 
92*e1f26343SJason M. Bills class BMCLogService : public Node
931da66f75SEd Tanous {
941da66f75SEd Tanous   public:
951da66f75SEd Tanous     template <typename CrowApp>
96*e1f26343SJason M. Bills     BMCLogService(CrowApp &app) :
97*e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/")
98*e1f26343SJason M. Bills     {
99*e1f26343SJason M. Bills         // Set the id for SubRoute
100*e1f26343SJason M. Bills         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog";
101*e1f26343SJason M. Bills         entityPrivileges = {
102*e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
103*e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
104*e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
105*e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
106*e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
107*e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
108*e1f26343SJason M. Bills     }
109*e1f26343SJason M. Bills 
110*e1f26343SJason M. Bills   private:
111*e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
112*e1f26343SJason M. Bills                const std::vector<std::string> &params) override
113*e1f26343SJason M. Bills     {
114*e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
115*e1f26343SJason M. Bills         // Copy over the static data to include the entries added by SubRoute
116*e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
117*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
118*e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
119*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
120*e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
121*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Service";
122*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Log Service";
123*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Log";
124*e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
125*e1f26343SJason M. Bills     }
126*e1f26343SJason M. Bills };
127*e1f26343SJason M. Bills 
128*e1f26343SJason M. Bills static int fillBMCLogEntryJson(const std::string &bmcLogEntryID,
129*e1f26343SJason M. Bills                                sd_journal *journal,
130*e1f26343SJason M. Bills                                nlohmann::json &bmcLogEntryJson)
131*e1f26343SJason M. Bills {
132*e1f26343SJason M. Bills     // Get the Log Entry contents
133*e1f26343SJason M. Bills     int ret = 0;
134*e1f26343SJason M. Bills     const char *data = nullptr;
135*e1f26343SJason M. Bills     size_t length = 0;
136*e1f26343SJason M. Bills 
137*e1f26343SJason M. Bills     ret =
138*e1f26343SJason M. Bills         sd_journal_get_data(journal, "MESSAGE", (const void **)&data, &length);
139*e1f26343SJason M. Bills     if (ret < 0)
140*e1f26343SJason M. Bills     {
141*e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
142*e1f26343SJason M. Bills         return 1;
143*e1f26343SJason M. Bills     }
144*e1f26343SJason M. Bills     boost::string_view msg;
145*e1f26343SJason M. Bills     msg = boost::string_view(data, length);
146*e1f26343SJason M. Bills     // Only use the content after the "=" character.
147*e1f26343SJason M. Bills     msg.remove_prefix(std::min(msg.find("=") + 1, msg.size()));
148*e1f26343SJason M. Bills 
149*e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
150*e1f26343SJason M. Bills     boost::string_view priority;
151*e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
152*e1f26343SJason M. Bills     ret =
153*e1f26343SJason M. Bills         sd_journal_get_data(journal, "PRIORITY", (const void **)&data, &length);
154*e1f26343SJason M. Bills     if (ret < 0)
155*e1f26343SJason M. Bills     {
156*e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
157*e1f26343SJason M. Bills         return 1;
158*e1f26343SJason M. Bills     }
159*e1f26343SJason M. Bills     priority = boost::string_view(data, length);
160*e1f26343SJason M. Bills     // Check length for sanity. Must be a single digit in the form
161*e1f26343SJason M. Bills     // "PRIORITY=[0-7]"
162*e1f26343SJason M. Bills     if (priority.size() > sizeof("PRIORITY=0"))
163*e1f26343SJason M. Bills     {
164*e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Invalid PRIORITY field length";
165*e1f26343SJason M. Bills         return 1;
166*e1f26343SJason M. Bills     }
167*e1f26343SJason M. Bills     // Only use the content after the "=" character.
168*e1f26343SJason M. Bills     priority.remove_prefix(std::min(priority.find("=") + 1, priority.size()));
169*e1f26343SJason M. Bills     severity = strtol(priority.data(), nullptr, 10);
170*e1f26343SJason M. Bills 
171*e1f26343SJason M. Bills     // Get the Created time from the timestamp
172*e1f26343SJason M. Bills     // Get the entry timestamp
173*e1f26343SJason M. Bills     uint64_t timestamp = 0;
174*e1f26343SJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
175*e1f26343SJason M. Bills     if (ret < 0)
176*e1f26343SJason M. Bills     {
177*e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
178*e1f26343SJason M. Bills                          << strerror(-ret);
179*e1f26343SJason M. Bills     }
180*e1f26343SJason M. Bills     time_t t =
181*e1f26343SJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
182*e1f26343SJason M. Bills     struct tm *loctime = localtime(&t);
183*e1f26343SJason M. Bills     char entryTime[64] = {};
184*e1f26343SJason M. Bills     if (NULL != loctime)
185*e1f26343SJason M. Bills     {
186*e1f26343SJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
187*e1f26343SJason M. Bills     }
188*e1f26343SJason M. Bills     // Insert the ':' into the timezone
189*e1f26343SJason M. Bills     boost::string_view t1(entryTime);
190*e1f26343SJason M. Bills     boost::string_view t2(entryTime);
191*e1f26343SJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
192*e1f26343SJason M. Bills     {
193*e1f26343SJason M. Bills         t1.remove_suffix(2);
194*e1f26343SJason M. Bills         t2.remove_prefix(t2.size() - 2);
195*e1f26343SJason M. Bills     }
196*e1f26343SJason M. Bills     const std::string entryTimeStr(t1.to_string() + ":" + t2.to_string());
197*e1f26343SJason M. Bills 
198*e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
199*e1f26343SJason M. Bills     bmcLogEntryJson = {
200*e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
201*e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
202*e1f26343SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" +
203*e1f26343SJason M. Bills                           bmcLogEntryID},
204*e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
205*e1f26343SJason M. Bills         {"Id", bmcLogEntryID},
206*e1f26343SJason M. Bills         {"Message", msg.to_string()},
207*e1f26343SJason M. Bills         {"EntryType", "Oem"},
208*e1f26343SJason M. Bills         {"Severity",
209*e1f26343SJason M. Bills          severity <= 2 ? "Critical"
210*e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
211*e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
212*e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
213*e1f26343SJason M. Bills     return 0;
214*e1f26343SJason M. Bills }
215*e1f26343SJason M. Bills 
216*e1f26343SJason M. Bills class BMCLogEntryCollection : public Node
217*e1f26343SJason M. Bills {
218*e1f26343SJason M. Bills   public:
219*e1f26343SJason M. Bills     template <typename CrowApp>
220*e1f26343SJason M. Bills     BMCLogEntryCollection(CrowApp &app) :
221*e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/")
222*e1f26343SJason M. Bills     {
223*e1f26343SJason M. Bills         // Collections use static ID for SubRoute to add to its parent, but only
224*e1f26343SJason M. Bills         // load dynamic data so the duplicate static members don't get displayed
225*e1f26343SJason M. Bills         Node::json["@odata.id"] =
226*e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
227*e1f26343SJason M. Bills         entityPrivileges = {
228*e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
229*e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
230*e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
231*e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
232*e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
233*e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
234*e1f26343SJason M. Bills     }
235*e1f26343SJason M. Bills 
236*e1f26343SJason M. Bills   private:
237*e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
238*e1f26343SJason M. Bills                const std::vector<std::string> &params) override
239*e1f26343SJason M. Bills     {
240*e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
241*e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
242*e1f26343SJason M. Bills         // it has a duplicate entry for members
243*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
244*e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
245*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
246*e1f26343SJason M. Bills             "/redfish/v1/"
247*e1f26343SJason M. Bills             "$metadata#LogEntryCollection.LogEntryCollection";
248*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
249*e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
250*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
251*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
252*e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
253*e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
254*e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
255*e1f26343SJason M. Bills 
256*e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
257*e1f26343SJason M. Bills         // for each entry
258*e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
259*e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
260*e1f26343SJason M. Bills         if (ret < 0)
261*e1f26343SJason M. Bills         {
262*e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
263*e1f26343SJason M. Bills             asyncResp->res.result(
264*e1f26343SJason M. Bills                 boost::beast::http::status::internal_server_error);
265*e1f26343SJason M. Bills             return;
266*e1f26343SJason M. Bills         }
267*e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
268*e1f26343SJason M. Bills             journalTmp, sd_journal_close);
269*e1f26343SJason M. Bills         journalTmp = nullptr;
270*e1f26343SJason M. Bills         uint64_t prevTs = 0;
271*e1f26343SJason M. Bills         int index = 0;
272*e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
273*e1f26343SJason M. Bills         {
274*e1f26343SJason M. Bills             // Get the entry timestamp
275*e1f26343SJason M. Bills             uint64_t curTs = 0;
276*e1f26343SJason M. Bills             ret = sd_journal_get_realtime_usec(journal.get(), &curTs);
277*e1f26343SJason M. Bills             if (ret < 0)
278*e1f26343SJason M. Bills             {
279*e1f26343SJason M. Bills                 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
280*e1f26343SJason M. Bills                                  << strerror(-ret);
281*e1f26343SJason M. Bills                 continue;
282*e1f26343SJason M. Bills             }
283*e1f26343SJason M. Bills             // If the timestamp isn't unique, increment the index
284*e1f26343SJason M. Bills             if (curTs == prevTs)
285*e1f26343SJason M. Bills             {
286*e1f26343SJason M. Bills                 index++;
287*e1f26343SJason M. Bills             }
288*e1f26343SJason M. Bills             else
289*e1f26343SJason M. Bills             {
290*e1f26343SJason M. Bills                 // Otherwise, reset it
291*e1f26343SJason M. Bills                 index = 0;
292*e1f26343SJason M. Bills             }
293*e1f26343SJason M. Bills             // Save the timestamp
294*e1f26343SJason M. Bills             prevTs = curTs;
295*e1f26343SJason M. Bills 
296*e1f26343SJason M. Bills             std::string idStr(std::to_string(curTs));
297*e1f26343SJason M. Bills             if (index > 0)
298*e1f26343SJason M. Bills             {
299*e1f26343SJason M. Bills                 idStr += "_" + std::to_string(index);
300*e1f26343SJason M. Bills             }
301*e1f26343SJason M. Bills             logEntryArray.push_back({});
302*e1f26343SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
303*e1f26343SJason M. Bills             if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
304*e1f26343SJason M. Bills             {
305*e1f26343SJason M. Bills                 asyncResp->res.result(
306*e1f26343SJason M. Bills                     boost::beast::http::status::internal_server_error);
307*e1f26343SJason M. Bills                 return;
308*e1f26343SJason M. Bills             }
309*e1f26343SJason M. Bills         }
310*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = logEntryArray.size();
311*e1f26343SJason M. Bills     }
312*e1f26343SJason M. Bills };
313*e1f26343SJason M. Bills 
314*e1f26343SJason M. Bills class BMCLogEntry : public Node
315*e1f26343SJason M. Bills {
316*e1f26343SJason M. Bills   public:
317*e1f26343SJason M. Bills     BMCLogEntry(CrowApp &app) :
318*e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
319*e1f26343SJason M. Bills              std::string())
320*e1f26343SJason M. Bills     {
321*e1f26343SJason M. Bills         entityPrivileges = {
322*e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
323*e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
324*e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
325*e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
326*e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
327*e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
328*e1f26343SJason M. Bills     }
329*e1f26343SJason M. Bills 
330*e1f26343SJason M. Bills   private:
331*e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
332*e1f26343SJason M. Bills                const std::vector<std::string> &params) override
333*e1f26343SJason M. Bills     {
334*e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
335*e1f26343SJason M. Bills         if (params.size() != 1)
336*e1f26343SJason M. Bills         {
337*e1f26343SJason M. Bills             asyncResp->res.result(
338*e1f26343SJason M. Bills                 boost::beast::http::status::internal_server_error);
339*e1f26343SJason M. Bills             return;
340*e1f26343SJason M. Bills         }
341*e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
342*e1f26343SJason M. Bills         boost::string_view tsStr(params[0]);
343*e1f26343SJason M. Bills         boost::string_view indexStr(params[0]);
344*e1f26343SJason M. Bills         uint64_t ts = 0;
345*e1f26343SJason M. Bills         uint16_t index = 0;
346*e1f26343SJason M. Bills         auto underscorePos = tsStr.find("_");
347*e1f26343SJason M. Bills         if (underscorePos == tsStr.npos)
348*e1f26343SJason M. Bills         {
349*e1f26343SJason M. Bills             // Timestamp has no index
350*e1f26343SJason M. Bills             ts = strtoull(tsStr.data(), nullptr, 10);
351*e1f26343SJason M. Bills         }
352*e1f26343SJason M. Bills         else
353*e1f26343SJason M. Bills         {
354*e1f26343SJason M. Bills             // Timestamp has an index
355*e1f26343SJason M. Bills             tsStr.remove_suffix(tsStr.size() - underscorePos + 1);
356*e1f26343SJason M. Bills             ts = strtoull(tsStr.data(), nullptr, 10);
357*e1f26343SJason M. Bills             indexStr.remove_prefix(underscorePos + 1);
358*e1f26343SJason M. Bills             index =
359*e1f26343SJason M. Bills                 static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10));
360*e1f26343SJason M. Bills         }
361*e1f26343SJason M. Bills 
362*e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
363*e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
364*e1f26343SJason M. Bills         if (ret < 0)
365*e1f26343SJason M. Bills         {
366*e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
367*e1f26343SJason M. Bills             asyncResp->res.result(
368*e1f26343SJason M. Bills                 boost::beast::http::status::internal_server_error);
369*e1f26343SJason M. Bills             return;
370*e1f26343SJason M. Bills         }
371*e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
372*e1f26343SJason M. Bills             journalTmp, sd_journal_close);
373*e1f26343SJason M. Bills         journalTmp = nullptr;
374*e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
375*e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
376*e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
377*e1f26343SJason M. Bills         {
378*e1f26343SJason M. Bills             sd_journal_next(journal.get());
379*e1f26343SJason M. Bills         }
380*e1f26343SJason M. Bills         if (fillBMCLogEntryJson(params[0], journal.get(),
381*e1f26343SJason M. Bills                                 asyncResp->res.jsonValue) != 0)
382*e1f26343SJason M. Bills         {
383*e1f26343SJason M. Bills             asyncResp->res.result(
384*e1f26343SJason M. Bills                 boost::beast::http::status::internal_server_error);
385*e1f26343SJason M. Bills             return;
386*e1f26343SJason M. Bills         }
387*e1f26343SJason M. Bills     }
388*e1f26343SJason M. Bills };
389*e1f26343SJason M. Bills 
390*e1f26343SJason M. Bills class CPULogService : public Node
391*e1f26343SJason M. Bills {
392*e1f26343SJason M. Bills   public:
393*e1f26343SJason M. Bills     template <typename CrowApp>
394*e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
395*e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
3961da66f75SEd Tanous     {
3971da66f75SEd Tanous         // Set the id for SubRoute
3984ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
3991da66f75SEd Tanous         entityPrivileges = {
400*e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
401*e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
402*e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
403*e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
404*e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
405*e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
4061da66f75SEd Tanous     }
4071da66f75SEd Tanous 
4081da66f75SEd Tanous   private:
4091da66f75SEd Tanous     /**
4101da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
4111da66f75SEd Tanous      */
4121da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
4131da66f75SEd Tanous                const std::vector<std::string> &params) override
4141da66f75SEd Tanous     {
415*e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
4161da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
417*e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
418*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
419*e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
420*e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
421*e1f26343SJason M. Bills             "/redfish/v1/"
4221da66f75SEd Tanous             "$metadata#LogService.LogService";
423*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
424*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
425*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
426*e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
427*e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
428*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
4291da66f75SEd Tanous             {"Oem",
4301da66f75SEd Tanous              {{"#CpuLog.Immediate",
4311da66f75SEd Tanous                {{"target",
4324ed77cd5SEd Tanous                  "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
4331da66f75SEd Tanous                  "CpuLog.Immediate"}}}}}};
4341da66f75SEd Tanous 
4351da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
436*e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
4371da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
4381da66f75SEd Tanous              {{"target",
4394ed77cd5SEd Tanous                "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
4401da66f75SEd Tanous                "CpuLog.SendRawPeci"}}});
4411da66f75SEd Tanous #endif
4421da66f75SEd Tanous     }
4431da66f75SEd Tanous };
4441da66f75SEd Tanous 
445*e1f26343SJason M. Bills class CPULogEntryCollection : public Node
4461da66f75SEd Tanous {
4471da66f75SEd Tanous   public:
4481da66f75SEd Tanous     template <typename CrowApp>
449*e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
450*e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
4511da66f75SEd Tanous     {
4521da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
4531da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
4541da66f75SEd Tanous         Node::json["@odata.id"] =
4554ed77cd5SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
4561da66f75SEd Tanous         entityPrivileges = {
457*e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
458*e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
459*e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
460*e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
461*e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
462*e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
4631da66f75SEd Tanous     }
4641da66f75SEd Tanous 
4651da66f75SEd Tanous   private:
4661da66f75SEd Tanous     /**
4671da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
4681da66f75SEd Tanous      */
4691da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
4701da66f75SEd Tanous                const std::vector<std::string> &params) override
4711da66f75SEd Tanous     {
472*e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
4731da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
4741da66f75SEd Tanous         // it has a duplicate entry for members
475*e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
476*e1f26343SJason M. Bills                                          const boost::system::error_code ec,
4771da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
4781da66f75SEd Tanous             if (ec)
4791da66f75SEd Tanous             {
4801da66f75SEd Tanous                 if (ec.value() !=
4811da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
4821da66f75SEd Tanous                 {
4831da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
4841da66f75SEd Tanous                                      << ec.message();
485*e1f26343SJason M. Bills                     asyncResp->res.result(
4861da66f75SEd Tanous                         boost::beast::http::status::internal_server_error);
4871da66f75SEd Tanous                     return;
4881da66f75SEd Tanous                 }
4891da66f75SEd Tanous             }
490*e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
4911da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
492*e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
4931da66f75SEd Tanous                 "/redfish/v1/"
4941da66f75SEd Tanous                 "$metadata#LogEntryCollection.LogEntryCollection";
495*e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.id"] =
496*e1f26343SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
497*e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
498*e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
499*e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
500*e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
501*e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
5021da66f75SEd Tanous             for (const std::string &objpath : resp)
5031da66f75SEd Tanous             {
5041da66f75SEd Tanous                 // Don't list the immediate log
5054ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
5061da66f75SEd Tanous                 {
5071da66f75SEd Tanous                     continue;
5081da66f75SEd Tanous                 }
5094ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
5104ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
5111da66f75SEd Tanous                 {
512*e1f26343SJason M. Bills                     logEntryArray.push_back(
513*e1f26343SJason M. Bills                         {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
514*e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
5154ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
5161da66f75SEd Tanous                 }
5171da66f75SEd Tanous             }
518*e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
519*e1f26343SJason M. Bills                 logEntryArray.size();
5201da66f75SEd Tanous         };
5211da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
5221da66f75SEd Tanous             std::move(getLogEntriesCallback),
5231da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
5241da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
5251da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
5264ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
5271da66f75SEd Tanous     }
5281da66f75SEd Tanous };
5291da66f75SEd Tanous 
5301da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
5311da66f75SEd Tanous {
5321da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
5331da66f75SEd Tanous     if (metaIt != cpuLog.end())
5341da66f75SEd Tanous     {
5351da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
5361da66f75SEd Tanous         if (tsIt != metaIt->end())
5371da66f75SEd Tanous         {
5381da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
5391da66f75SEd Tanous             if (logTime != nullptr)
5401da66f75SEd Tanous             {
5411da66f75SEd Tanous                 return *logTime;
5421da66f75SEd Tanous             }
5431da66f75SEd Tanous         }
5441da66f75SEd Tanous     }
5451da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
5461da66f75SEd Tanous 
5471da66f75SEd Tanous     return std::string();
5481da66f75SEd Tanous }
5491da66f75SEd Tanous 
550*e1f26343SJason M. Bills class CPULogEntry : public Node
5511da66f75SEd Tanous {
5521da66f75SEd Tanous   public:
553*e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
5544ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
5551da66f75SEd Tanous              std::string())
5561da66f75SEd Tanous     {
5571da66f75SEd Tanous         entityPrivileges = {
558*e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
559*e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
560*e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
561*e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
562*e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
563*e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
5641da66f75SEd Tanous     }
5651da66f75SEd Tanous 
5661da66f75SEd Tanous   private:
5671da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
5681da66f75SEd Tanous                const std::vector<std::string> &params) override
5691da66f75SEd Tanous     {
570*e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
5711da66f75SEd Tanous         if (params.size() != 1)
5721da66f75SEd Tanous         {
573*e1f26343SJason M. Bills             asyncResp->res.result(
574*e1f26343SJason M. Bills                 boost::beast::http::status::internal_server_error);
5751da66f75SEd Tanous             return;
5761da66f75SEd Tanous         }
5774ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
578*e1f26343SJason M. Bills         auto getStoredLogCallback =
579*e1f26343SJason M. Bills             [asyncResp,
5804ed77cd5SEd Tanous              logId](const boost::system::error_code ec,
581*e1f26343SJason M. Bills                     const sdbusplus::message::variant<std::string> &resp) {
5821da66f75SEd Tanous                 if (ec)
5831da66f75SEd Tanous                 {
584*e1f26343SJason M. Bills                     BMCWEB_LOG_DEBUG << "failed to get log ec: "
585*e1f26343SJason M. Bills                                      << ec.message();
586*e1f26343SJason M. Bills                     asyncResp->res.result(
587*e1f26343SJason M. Bills                         boost::beast::http::status::internal_server_error);
5881da66f75SEd Tanous                     return;
5891da66f75SEd Tanous                 }
590*e1f26343SJason M. Bills                 const std::string *log =
591*e1f26343SJason M. Bills                     mapbox::getPtr<const std::string>(resp);
5921da66f75SEd Tanous                 if (log == nullptr)
5931da66f75SEd Tanous                 {
594*e1f26343SJason M. Bills                     asyncResp->res.result(
595*e1f26343SJason M. Bills                         boost::beast::http::status::internal_server_error);
5961da66f75SEd Tanous                     return;
5971da66f75SEd Tanous                 }
5981da66f75SEd Tanous                 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
5991da66f75SEd Tanous                 if (j.is_discarded())
6001da66f75SEd Tanous                 {
601*e1f26343SJason M. Bills                     asyncResp->res.result(
602*e1f26343SJason M. Bills                         boost::beast::http::status::internal_server_error);
6031da66f75SEd Tanous                     return;
6041da66f75SEd Tanous                 }
6051da66f75SEd Tanous                 std::string t = getLogCreatedTime(j);
606*e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {
6071da66f75SEd Tanous                     {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
608*e1f26343SJason M. Bills                     {"@odata.context",
609*e1f26343SJason M. Bills                      "/redfish/v1/$metadata#LogEntry.LogEntry"},
6101da66f75SEd Tanous                     {"@odata.id",
6114ed77cd5SEd Tanous                      "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
6124ed77cd5SEd Tanous                          std::to_string(logId)},
6131da66f75SEd Tanous                     {"Name", "CPU Debug Log"},
6144ed77cd5SEd Tanous                     {"Id", logId},
6151da66f75SEd Tanous                     {"EntryType", "Oem"},
6161da66f75SEd Tanous                     {"OemRecordFormat", "Intel CPU Log"},
6171da66f75SEd Tanous                     {"Oem", {{"Intel", std::move(j)}}},
6181da66f75SEd Tanous                     {"Created", std::move(t)}};
6191da66f75SEd Tanous             };
6201da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
6214ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
6224ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
6234ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
6241da66f75SEd Tanous     }
6251da66f75SEd Tanous };
6261da66f75SEd Tanous 
627*e1f26343SJason M. Bills class ImmediateCPULog : public Node
6281da66f75SEd Tanous {
6291da66f75SEd Tanous   public:
630*e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
6314ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
632*e1f26343SJason M. Bills                   "CpuLog.Immediate/")
6331da66f75SEd Tanous     {
6341da66f75SEd Tanous         entityPrivileges = {
635*e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
636*e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
637*e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
638*e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
639*e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
640*e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6411da66f75SEd Tanous     }
6421da66f75SEd Tanous 
6431da66f75SEd Tanous   private:
6441da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
6451da66f75SEd Tanous                 const std::vector<std::string> &params) override
6461da66f75SEd Tanous     {
647*e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6481da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
6491da66f75SEd Tanous             immediateLogMatcher;
6501da66f75SEd Tanous 
6511da66f75SEd Tanous         // Only allow one Immediate Log request at a time
6521da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
6531da66f75SEd Tanous         {
654*e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
655*e1f26343SJason M. Bills             asyncResp->res.result(
656*e1f26343SJason M. Bills                 boost::beast::http::status::service_unavailable);
6571da66f75SEd Tanous             messages::addMessageToJson(
658*e1f26343SJason M. Bills                 asyncResp->res.jsonValue,
659*e1f26343SJason M. Bills                 messages::serviceTemporarilyUnavailable("30"),
6601da66f75SEd Tanous                 "/CpuLog.Immediate");
6611da66f75SEd Tanous             return;
6621da66f75SEd Tanous         }
6631da66f75SEd Tanous         // Make this static so it survives outside this method
6641da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
6651da66f75SEd Tanous 
6661da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
667*e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
6681da66f75SEd Tanous             immediateLogMatcher = nullptr;
6691da66f75SEd Tanous             if (ec)
6701da66f75SEd Tanous             {
6711da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
6721da66f75SEd Tanous                 // completion.
6731da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
6741da66f75SEd Tanous                 {
6751da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
6761da66f75SEd Tanous                 }
6771da66f75SEd Tanous                 return;
6781da66f75SEd Tanous             }
6791da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
6801da66f75SEd Tanous 
681*e1f26343SJason M. Bills             asyncResp->res.result(
682*e1f26343SJason M. Bills                 boost::beast::http::status::internal_server_error);
6831da66f75SEd Tanous         });
6841da66f75SEd Tanous 
685*e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
6861da66f75SEd Tanous                                                sdbusplus::message::message &m) {
6871da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
6881da66f75SEd Tanous             boost::system::error_code ec;
6891da66f75SEd Tanous             timeout.cancel(ec);
6901da66f75SEd Tanous             if (ec)
6911da66f75SEd Tanous             {
6921da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
6931da66f75SEd Tanous             }
6944ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
6951da66f75SEd Tanous             boost::container::flat_map<
6961da66f75SEd Tanous                 std::string,
6971da66f75SEd Tanous                 boost::container::flat_map<
6981da66f75SEd Tanous                     std::string, sdbusplus::message::variant<std::string>>>
6994ed77cd5SEd Tanous                 interfacesAdded;
7004ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
7011da66f75SEd Tanous             const std::string *log = mapbox::getPtr<const std::string>(
7024ed77cd5SEd Tanous                 interfacesAdded[cpuLogInterface]["Log"]);
7031da66f75SEd Tanous             if (log == nullptr)
7041da66f75SEd Tanous             {
705*e1f26343SJason M. Bills                 asyncResp->res.result(
706*e1f26343SJason M. Bills                     boost::beast::http::status::internal_server_error);
7071da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
7081da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
7091da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
7101da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
7111da66f75SEd Tanous                 // be the last thing done.
7121da66f75SEd Tanous                 immediateLogMatcher = nullptr;
7131da66f75SEd Tanous                 return;
7141da66f75SEd Tanous             }
7151da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
7161da66f75SEd Tanous             if (j.is_discarded())
7171da66f75SEd Tanous             {
718*e1f26343SJason M. Bills                 asyncResp->res.result(
719*e1f26343SJason M. Bills                     boost::beast::http::status::internal_server_error);
7201da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
7211da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
7221da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
7231da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
7241da66f75SEd Tanous                 // be the last thing done.
7251da66f75SEd Tanous                 immediateLogMatcher = nullptr;
7261da66f75SEd Tanous                 return;
7271da66f75SEd Tanous             }
7281da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
729*e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
7301da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
7311da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
7321da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
7331da66f75SEd Tanous                 {"EntryType", "Oem"},
7341da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
7351da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
7361da66f75SEd Tanous                 {"Created", std::move(t)}};
7371da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
7381da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
7391da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
7401da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
7411da66f75SEd Tanous             // thing done.
7421da66f75SEd Tanous             immediateLogMatcher = nullptr;
7431da66f75SEd Tanous         };
7441da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
7451da66f75SEd Tanous             *crow::connections::systemBus,
7461da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
7474ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
7481da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
7491da66f75SEd Tanous 
7501da66f75SEd Tanous         auto generateImmediateLogCallback =
751*e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
7521da66f75SEd Tanous                         const std::string &resp) {
7531da66f75SEd Tanous                 if (ec)
7541da66f75SEd Tanous                 {
7551da66f75SEd Tanous                     if (ec.value() ==
7561da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
7571da66f75SEd Tanous                     {
7581da66f75SEd Tanous                         messages::addMessageToJson(
759*e1f26343SJason M. Bills                             asyncResp->res.jsonValue,
760*e1f26343SJason M. Bills                             messages::resourceInStandby(), "/CpuLog.Immediate");
761*e1f26343SJason M. Bills                         asyncResp->res.result(
7621da66f75SEd Tanous                             boost::beast::http::status::service_unavailable);
7631da66f75SEd Tanous                     }
7641da66f75SEd Tanous                     else
7651da66f75SEd Tanous                     {
766*e1f26343SJason M. Bills                         asyncResp->res.result(
7671da66f75SEd Tanous                             boost::beast::http::status::internal_server_error);
7681da66f75SEd Tanous                     }
7691da66f75SEd Tanous                     boost::system::error_code timeoutec;
7701da66f75SEd Tanous                     timeout.cancel(timeoutec);
7711da66f75SEd Tanous                     if (timeoutec)
7721da66f75SEd Tanous                     {
7731da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
7741da66f75SEd Tanous                                          << timeoutec;
7751da66f75SEd Tanous                     }
7761da66f75SEd Tanous                     immediateLogMatcher = nullptr;
7771da66f75SEd Tanous                     return;
7781da66f75SEd Tanous                 }
7791da66f75SEd Tanous             };
7801da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
7814ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
7824ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
7831da66f75SEd Tanous     }
7841da66f75SEd Tanous };
7851da66f75SEd Tanous 
786*e1f26343SJason M. Bills class SendRawPECI : public Node
7871da66f75SEd Tanous {
7881da66f75SEd Tanous   public:
789*e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
7904ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
791*e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
7921da66f75SEd Tanous     {
7931da66f75SEd Tanous         entityPrivileges = {
7941da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
7951da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
7961da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
7971da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
7981da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
7991da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
8001da66f75SEd Tanous     }
8011da66f75SEd Tanous 
8021da66f75SEd Tanous   private:
8031da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
8041da66f75SEd Tanous                 const std::vector<std::string> &params) override
8051da66f75SEd Tanous     {
806*e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
8071da66f75SEd Tanous         // Get the Raw PECI command from the request
808*e1f26343SJason M. Bills         nlohmann::json rawPECICmd;
809*e1f26343SJason M. Bills         if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
8101da66f75SEd Tanous         {
8111da66f75SEd Tanous             return;
8121da66f75SEd Tanous         }
8131da66f75SEd Tanous         // Get the Client Address from the request
814*e1f26343SJason M. Bills         nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
815*e1f26343SJason M. Bills         if (caIt == rawPECICmd.end())
8161da66f75SEd Tanous         {
8171da66f75SEd Tanous             messages::addMessageToJson(
818*e1f26343SJason M. Bills                 asyncResp->res.jsonValue,
819*e1f26343SJason M. Bills                 messages::propertyMissing("ClientAddress"), "/ClientAddress");
820*e1f26343SJason M. Bills             asyncResp->res.result(boost::beast::http::status::bad_request);
8211da66f75SEd Tanous             return;
8221da66f75SEd Tanous         }
8231da66f75SEd Tanous         const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
8241da66f75SEd Tanous         if (ca == nullptr)
8251da66f75SEd Tanous         {
8261da66f75SEd Tanous             messages::addMessageToJson(
827*e1f26343SJason M. Bills                 asyncResp->res.jsonValue,
8281da66f75SEd Tanous                 messages::propertyValueTypeError(caIt->dump(), "ClientAddress"),
8291da66f75SEd Tanous                 "/ClientAddress");
830*e1f26343SJason M. Bills             asyncResp->res.result(boost::beast::http::status::bad_request);
8311da66f75SEd Tanous             return;
8321da66f75SEd Tanous         }
8331da66f75SEd Tanous         // Get the Read Length from the request
8341da66f75SEd Tanous         const uint8_t clientAddress = static_cast<uint8_t>(*ca);
835*e1f26343SJason M. Bills         nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
836*e1f26343SJason M. Bills         if (rlIt == rawPECICmd.end())
8371da66f75SEd Tanous         {
838*e1f26343SJason M. Bills             messages::addMessageToJson(asyncResp->res.jsonValue,
8391da66f75SEd Tanous                                        messages::propertyMissing("ReadLength"),
8401da66f75SEd Tanous                                        "/ReadLength");
841*e1f26343SJason M. Bills             asyncResp->res.result(boost::beast::http::status::bad_request);
8421da66f75SEd Tanous             return;
8431da66f75SEd Tanous         }
8441da66f75SEd Tanous         const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
8451da66f75SEd Tanous         if (rl == nullptr)
8461da66f75SEd Tanous         {
8471da66f75SEd Tanous             messages::addMessageToJson(
848*e1f26343SJason M. Bills                 asyncResp->res.jsonValue,
8491da66f75SEd Tanous                 messages::propertyValueTypeError(rlIt->dump(), "ReadLength"),
8501da66f75SEd Tanous                 "/ReadLength");
851*e1f26343SJason M. Bills             asyncResp->res.result(boost::beast::http::status::bad_request);
8521da66f75SEd Tanous             return;
8531da66f75SEd Tanous         }
8541da66f75SEd Tanous         // Get the PECI Command from the request
8551da66f75SEd Tanous         const uint32_t readLength = static_cast<uint32_t>(*rl);
856*e1f26343SJason M. Bills         nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
857*e1f26343SJason M. Bills         if (pcIt == rawPECICmd.end())
8581da66f75SEd Tanous         {
859*e1f26343SJason M. Bills             messages::addMessageToJson(asyncResp->res.jsonValue,
8601da66f75SEd Tanous                                        messages::propertyMissing("PECICommand"),
8611da66f75SEd Tanous                                        "/PECICommand");
862*e1f26343SJason M. Bills             asyncResp->res.result(boost::beast::http::status::bad_request);
8631da66f75SEd Tanous             return;
8641da66f75SEd Tanous         }
8651da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
8661da66f75SEd Tanous         for (auto pc : *pcIt)
8671da66f75SEd Tanous         {
8681da66f75SEd Tanous             const uint64_t *val = pc.get_ptr<const uint64_t *>();
8691da66f75SEd Tanous             if (val == nullptr)
8701da66f75SEd Tanous             {
8711da66f75SEd Tanous                 messages::addMessageToJson(
872*e1f26343SJason M. Bills                     asyncResp->res.jsonValue,
8731da66f75SEd Tanous                     messages::propertyValueTypeError(
8741da66f75SEd Tanous                         pc.dump(),
8751da66f75SEd Tanous                         "PECICommand/" + std::to_string(peciCommand.size())),
8761da66f75SEd Tanous                     "/PECICommand");
877*e1f26343SJason M. Bills                 asyncResp->res.result(boost::beast::http::status::bad_request);
8781da66f75SEd Tanous                 return;
8791da66f75SEd Tanous             }
8801da66f75SEd Tanous             peciCommand.push_back(static_cast<uint8_t>(*val));
8811da66f75SEd Tanous         }
8821da66f75SEd Tanous         // Callback to return the Raw PECI response
883*e1f26343SJason M. Bills         auto sendRawPECICallback =
884*e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
8851da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
8861da66f75SEd Tanous                 if (ec)
8871da66f75SEd Tanous                 {
8881da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
8891da66f75SEd Tanous                                      << ec.message();
890*e1f26343SJason M. Bills                     asyncResp->res.result(
891*e1f26343SJason M. Bills                         boost::beast::http::status::internal_server_error);
8921da66f75SEd Tanous                     return;
8931da66f75SEd Tanous                 }
894*e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
8951da66f75SEd Tanous                                             {"PECIResponse", resp}};
8961da66f75SEd Tanous             };
8971da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
8981da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
899*e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
900*e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
9014ed77cd5SEd Tanous             peciCommand);
9021da66f75SEd Tanous     }
9031da66f75SEd Tanous };
9041da66f75SEd Tanous 
9051da66f75SEd Tanous } // namespace redfish
906