xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 1da66f7501332d622f64963136f1cba38829fb40)
1*1da66f75SEd Tanous /*
2*1da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation
3*1da66f75SEd Tanous //
4*1da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License");
5*1da66f75SEd Tanous // you may not use this file except in compliance with the License.
6*1da66f75SEd Tanous // You may obtain a copy of the License at
7*1da66f75SEd Tanous //
8*1da66f75SEd Tanous //      http://www.apache.org/licenses/LICENSE-2.0
9*1da66f75SEd Tanous //
10*1da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software
11*1da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS,
12*1da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*1da66f75SEd Tanous // See the License for the specific language governing permissions and
14*1da66f75SEd Tanous // limitations under the License.
15*1da66f75SEd Tanous */
16*1da66f75SEd Tanous #pragma once
17*1da66f75SEd Tanous 
18*1da66f75SEd Tanous #include "node.hpp"
19*1da66f75SEd Tanous 
20*1da66f75SEd Tanous #include <boost/container/flat_map.hpp>
21*1da66f75SEd Tanous #include <experimental/filesystem>
22*1da66f75SEd Tanous 
23*1da66f75SEd Tanous namespace redfish
24*1da66f75SEd Tanous {
25*1da66f75SEd Tanous 
26*1da66f75SEd Tanous constexpr char const *CPU_LOG_OBJECT = "com.intel.CpuDebugLog";
27*1da66f75SEd Tanous constexpr char const *CPU_LOG_PATH = "/com/intel/CpuDebugLog";
28*1da66f75SEd Tanous constexpr char const *CPU_LOG_IMMEDIATE_PATH =
29*1da66f75SEd Tanous     "/com/intel/CpuDebugLog/Immediate";
30*1da66f75SEd Tanous constexpr char const *CPU_LOG_INTERFACE = "com.intel.CpuDebugLog";
31*1da66f75SEd Tanous constexpr char const *CPU_LOG_IMMEDIATE_INTERFACE =
32*1da66f75SEd Tanous     "com.intel.CpuDebugLog.Immediate";
33*1da66f75SEd Tanous constexpr char const *CPU_LOG_RAW_PECI_INTERFACE =
34*1da66f75SEd Tanous     "com.intel.CpuDebugLog.SendRawPeci";
35*1da66f75SEd Tanous 
36*1da66f75SEd Tanous namespace fs = std::experimental::filesystem;
37*1da66f75SEd Tanous 
38*1da66f75SEd Tanous class LogServiceCollection : public Node
39*1da66f75SEd Tanous {
40*1da66f75SEd Tanous   public:
41*1da66f75SEd Tanous     template <typename CrowApp>
42*1da66f75SEd Tanous     LogServiceCollection(CrowApp &app) :
43*1da66f75SEd Tanous         Node(app, "/redfish/v1/Managers/openbmc/LogServices/")
44*1da66f75SEd Tanous     {
45*1da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
46*1da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
47*1da66f75SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/LogServices";
48*1da66f75SEd Tanous         entityPrivileges = {
49*1da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
50*1da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
51*1da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
52*1da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
53*1da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
54*1da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
55*1da66f75SEd Tanous     }
56*1da66f75SEd Tanous 
57*1da66f75SEd Tanous   private:
58*1da66f75SEd Tanous     /**
59*1da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
60*1da66f75SEd Tanous      */
61*1da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
62*1da66f75SEd Tanous                const std::vector<std::string> &params) override
63*1da66f75SEd Tanous     {
64*1da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
65*1da66f75SEd Tanous         // it has a duplicate entry for members
66*1da66f75SEd Tanous         res.jsonValue["@odata.type"] =
67*1da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
68*1da66f75SEd Tanous         res.jsonValue["@odata.context"] =
69*1da66f75SEd Tanous             "/redfish/v1/"
70*1da66f75SEd Tanous             "$metadata#LogServiceCollection.LogServiceCollection";
71*1da66f75SEd Tanous         res.jsonValue["@odata.id"] = "/redfish/v1/Managers/openbmc/LogServices";
72*1da66f75SEd Tanous         res.jsonValue["Name"] = "Open BMC Log Services Collection";
73*1da66f75SEd Tanous         res.jsonValue["Description"] =
74*1da66f75SEd Tanous             "Collection of LogServices for this Manager";
75*1da66f75SEd Tanous         nlohmann::json &logserviceArray = res.jsonValue["Members"];
76*1da66f75SEd Tanous         logserviceArray = nlohmann::json::array();
77*1da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
78*1da66f75SEd Tanous         logserviceArray.push_back(
79*1da66f75SEd Tanous             {{"@odata.id", "/redfish/v1/Managers/openbmc/LogServices/CpuLog"}});
80*1da66f75SEd Tanous #endif
81*1da66f75SEd Tanous         res.jsonValue["Members@odata.count"] = logserviceArray.size();
82*1da66f75SEd Tanous         res.end();
83*1da66f75SEd Tanous     }
84*1da66f75SEd Tanous };
85*1da66f75SEd Tanous 
86*1da66f75SEd Tanous class CpuLogService : public Node
87*1da66f75SEd Tanous {
88*1da66f75SEd Tanous   public:
89*1da66f75SEd Tanous     template <typename CrowApp>
90*1da66f75SEd Tanous     CpuLogService(CrowApp &app) :
91*1da66f75SEd Tanous         Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog")
92*1da66f75SEd Tanous     {
93*1da66f75SEd Tanous         // Set the id for SubRoute
94*1da66f75SEd Tanous         Node::json["@odata.id"] =
95*1da66f75SEd Tanous             "/redfish/v1/Managers/openbmc/LogServices/CpuLog";
96*1da66f75SEd Tanous         entityPrivileges = {
97*1da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
98*1da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
99*1da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
100*1da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
101*1da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
102*1da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
103*1da66f75SEd Tanous     }
104*1da66f75SEd Tanous 
105*1da66f75SEd Tanous   private:
106*1da66f75SEd Tanous     /**
107*1da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
108*1da66f75SEd Tanous      */
109*1da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
110*1da66f75SEd Tanous                const std::vector<std::string> &params) override
111*1da66f75SEd Tanous     {
112*1da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
113*1da66f75SEd Tanous         res.jsonValue = Node::json;
114*1da66f75SEd Tanous         res.jsonValue["@odata.type"] = "#LogService.v1_1_0.LogService";
115*1da66f75SEd Tanous         res.jsonValue["@odata.context"] = "/redfish/v1/"
116*1da66f75SEd Tanous                                           "$metadata#LogService.LogService";
117*1da66f75SEd Tanous         res.jsonValue["Name"] = "Open BMC CPU Log Service";
118*1da66f75SEd Tanous         res.jsonValue["Description"] = "CPU Log Service";
119*1da66f75SEd Tanous         res.jsonValue["Id"] = "CPU Log";
120*1da66f75SEd Tanous         res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
121*1da66f75SEd Tanous         res.jsonValue["MaxNumberOfRecords"] = 3;
122*1da66f75SEd Tanous         res.jsonValue["Actions"] = {
123*1da66f75SEd Tanous             {"Oem",
124*1da66f75SEd Tanous              {{"#CpuLog.Immediate",
125*1da66f75SEd Tanous                {{"target",
126*1da66f75SEd Tanous                  "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/"
127*1da66f75SEd Tanous                  "CpuLog.Immediate"}}}}}};
128*1da66f75SEd Tanous 
129*1da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
130*1da66f75SEd Tanous         res.jsonValue["Actions"]["Oem"].push_back(
131*1da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
132*1da66f75SEd Tanous              {{"target",
133*1da66f75SEd Tanous                "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/"
134*1da66f75SEd Tanous                "CpuLog.SendRawPeci"}}});
135*1da66f75SEd Tanous #endif
136*1da66f75SEd Tanous         res.end();
137*1da66f75SEd Tanous     }
138*1da66f75SEd Tanous };
139*1da66f75SEd Tanous 
140*1da66f75SEd Tanous class CpuLogEntryCollection : public Node
141*1da66f75SEd Tanous {
142*1da66f75SEd Tanous   public:
143*1da66f75SEd Tanous     template <typename CrowApp>
144*1da66f75SEd Tanous     CpuLogEntryCollection(CrowApp &app) :
145*1da66f75SEd Tanous         Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries")
146*1da66f75SEd Tanous     {
147*1da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
148*1da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
149*1da66f75SEd Tanous         Node::json["@odata.id"] =
150*1da66f75SEd Tanous             "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries";
151*1da66f75SEd Tanous         entityPrivileges = {
152*1da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
153*1da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
154*1da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
155*1da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
156*1da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
157*1da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
158*1da66f75SEd Tanous     }
159*1da66f75SEd Tanous 
160*1da66f75SEd Tanous   private:
161*1da66f75SEd Tanous     /**
162*1da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
163*1da66f75SEd Tanous      */
164*1da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
165*1da66f75SEd Tanous                const std::vector<std::string> &params) override
166*1da66f75SEd Tanous     {
167*1da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
168*1da66f75SEd Tanous         // it has a duplicate entry for members
169*1da66f75SEd Tanous         auto getLogEntriesCallback =
170*1da66f75SEd Tanous             [&res](const boost::system::error_code ec,
171*1da66f75SEd Tanous                    const std::vector<std::string> &resp) {
172*1da66f75SEd Tanous                 if (ec)
173*1da66f75SEd Tanous                 {
174*1da66f75SEd Tanous                     if (ec.value() !=
175*1da66f75SEd Tanous                         boost::system::errc::no_such_file_or_directory)
176*1da66f75SEd Tanous                     {
177*1da66f75SEd Tanous                         BMCWEB_LOG_DEBUG << "failed to get entries ec: "
178*1da66f75SEd Tanous                                          << ec.message();
179*1da66f75SEd Tanous                         res.result(
180*1da66f75SEd Tanous                             boost::beast::http::status::internal_server_error);
181*1da66f75SEd Tanous                         res.end();
182*1da66f75SEd Tanous                         return;
183*1da66f75SEd Tanous                     }
184*1da66f75SEd Tanous                 }
185*1da66f75SEd Tanous                 res.jsonValue["@odata.type"] =
186*1da66f75SEd Tanous                     "#LogEntryCollection.LogEntryCollection";
187*1da66f75SEd Tanous                 res.jsonValue["@odata.context"] =
188*1da66f75SEd Tanous                     "/redfish/v1/"
189*1da66f75SEd Tanous                     "$metadata#LogEntryCollection.LogEntryCollection";
190*1da66f75SEd Tanous                 res.jsonValue["Name"] = "Open BMC CPU Log Entries";
191*1da66f75SEd Tanous                 res.jsonValue["Description"] = "Collection of CPU Log Entries";
192*1da66f75SEd Tanous                 nlohmann::json &logentry_array = res.jsonValue["Members"];
193*1da66f75SEd Tanous                 logentry_array = nlohmann::json::array();
194*1da66f75SEd Tanous                 for (const std::string &objpath : resp)
195*1da66f75SEd Tanous                 {
196*1da66f75SEd Tanous                     // Don't list the immediate log
197*1da66f75SEd Tanous                     if (objpath.compare(CPU_LOG_IMMEDIATE_PATH) == 0)
198*1da66f75SEd Tanous                     {
199*1da66f75SEd Tanous                         continue;
200*1da66f75SEd Tanous                     }
201*1da66f75SEd Tanous                     std::size_t last_pos = objpath.rfind("/");
202*1da66f75SEd Tanous                     if (last_pos != std::string::npos)
203*1da66f75SEd Tanous                     {
204*1da66f75SEd Tanous                         logentry_array.push_back(
205*1da66f75SEd Tanous                             {{"@odata.id", "/redfish/v1/Managers/openbmc/"
206*1da66f75SEd Tanous                                            "LogServices/CpuLog/Entries/" +
207*1da66f75SEd Tanous                                                objpath.substr(last_pos + 1)}});
208*1da66f75SEd Tanous                     }
209*1da66f75SEd Tanous                 }
210*1da66f75SEd Tanous                 res.jsonValue["Members@odata.count"] = logentry_array.size();
211*1da66f75SEd Tanous                 res.end();
212*1da66f75SEd Tanous             };
213*1da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
214*1da66f75SEd Tanous             std::move(getLogEntriesCallback),
215*1da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
216*1da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
217*1da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
218*1da66f75SEd Tanous             std::array<const char *, 1>{CPU_LOG_INTERFACE});
219*1da66f75SEd Tanous     }
220*1da66f75SEd Tanous };
221*1da66f75SEd Tanous 
222*1da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
223*1da66f75SEd Tanous {
224*1da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
225*1da66f75SEd Tanous     if (metaIt != cpuLog.end())
226*1da66f75SEd Tanous     {
227*1da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
228*1da66f75SEd Tanous         if (tsIt != metaIt->end())
229*1da66f75SEd Tanous         {
230*1da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
231*1da66f75SEd Tanous             if (logTime != nullptr)
232*1da66f75SEd Tanous             {
233*1da66f75SEd Tanous                 return *logTime;
234*1da66f75SEd Tanous             }
235*1da66f75SEd Tanous         }
236*1da66f75SEd Tanous     }
237*1da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
238*1da66f75SEd Tanous 
239*1da66f75SEd Tanous     return std::string();
240*1da66f75SEd Tanous }
241*1da66f75SEd Tanous 
242*1da66f75SEd Tanous class CpuLogEntry : public Node
243*1da66f75SEd Tanous {
244*1da66f75SEd Tanous   public:
245*1da66f75SEd Tanous     CpuLogEntry(CrowApp &app) :
246*1da66f75SEd Tanous         Node(app,
247*1da66f75SEd Tanous              "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries/<str>/",
248*1da66f75SEd Tanous              std::string())
249*1da66f75SEd Tanous     {
250*1da66f75SEd Tanous         entityPrivileges = {
251*1da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
252*1da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
253*1da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
254*1da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
255*1da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
256*1da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
257*1da66f75SEd Tanous     }
258*1da66f75SEd Tanous 
259*1da66f75SEd Tanous   private:
260*1da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
261*1da66f75SEd Tanous                const std::vector<std::string> &params) override
262*1da66f75SEd Tanous     {
263*1da66f75SEd Tanous         if (params.size() != 1)
264*1da66f75SEd Tanous         {
265*1da66f75SEd Tanous             res.result(boost::beast::http::status::internal_server_error);
266*1da66f75SEd Tanous             res.end();
267*1da66f75SEd Tanous             return;
268*1da66f75SEd Tanous         }
269*1da66f75SEd Tanous         const uint8_t log_id = std::atoi(params[0].c_str());
270*1da66f75SEd Tanous         auto getStoredLogCallback = [&res,
271*1da66f75SEd Tanous                                      log_id](const boost::system::error_code ec,
272*1da66f75SEd Tanous                                              const sdbusplus::message::variant<
273*1da66f75SEd Tanous                                                  std::string> &resp) {
274*1da66f75SEd Tanous             if (ec)
275*1da66f75SEd Tanous             {
276*1da66f75SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
277*1da66f75SEd Tanous                 res.result(boost::beast::http::status::internal_server_error);
278*1da66f75SEd Tanous                 res.end();
279*1da66f75SEd Tanous                 return;
280*1da66f75SEd Tanous             }
281*1da66f75SEd Tanous             const std::string *log = mapbox::getPtr<const std::string>(resp);
282*1da66f75SEd Tanous             if (log == nullptr)
283*1da66f75SEd Tanous             {
284*1da66f75SEd Tanous                 res.result(boost::beast::http::status::internal_server_error);
285*1da66f75SEd Tanous                 res.end();
286*1da66f75SEd Tanous                 return;
287*1da66f75SEd Tanous             }
288*1da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
289*1da66f75SEd Tanous             if (j.is_discarded())
290*1da66f75SEd Tanous             {
291*1da66f75SEd Tanous                 res.result(boost::beast::http::status::internal_server_error);
292*1da66f75SEd Tanous                 res.end();
293*1da66f75SEd Tanous                 return;
294*1da66f75SEd Tanous             }
295*1da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
296*1da66f75SEd Tanous             res.jsonValue = {
297*1da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
298*1da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
299*1da66f75SEd Tanous                 {"@odata.id",
300*1da66f75SEd Tanous                  "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries/" +
301*1da66f75SEd Tanous                      std::to_string(log_id)},
302*1da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
303*1da66f75SEd Tanous                 {"Id", log_id},
304*1da66f75SEd Tanous                 {"EntryType", "Oem"},
305*1da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
306*1da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
307*1da66f75SEd Tanous                 {"Created", std::move(t)}};
308*1da66f75SEd Tanous             res.end();
309*1da66f75SEd Tanous         };
310*1da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
311*1da66f75SEd Tanous             std::move(getStoredLogCallback), CPU_LOG_OBJECT,
312*1da66f75SEd Tanous             CPU_LOG_PATH + std::string("/") + std::to_string(log_id),
313*1da66f75SEd Tanous             "org.freedesktop.DBus.Properties", "Get", CPU_LOG_INTERFACE, "Log");
314*1da66f75SEd Tanous     }
315*1da66f75SEd Tanous };
316*1da66f75SEd Tanous 
317*1da66f75SEd Tanous class ImmediateCpuLog : public Node
318*1da66f75SEd Tanous {
319*1da66f75SEd Tanous   public:
320*1da66f75SEd Tanous     ImmediateCpuLog(CrowApp &app) :
321*1da66f75SEd Tanous         Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/"
322*1da66f75SEd Tanous                   "CpuLog.Immediate")
323*1da66f75SEd Tanous     {
324*1da66f75SEd Tanous         entityPrivileges = {
325*1da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
326*1da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
327*1da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
328*1da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
329*1da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
330*1da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
331*1da66f75SEd Tanous     }
332*1da66f75SEd Tanous 
333*1da66f75SEd Tanous   private:
334*1da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
335*1da66f75SEd Tanous                 const std::vector<std::string> &params) override
336*1da66f75SEd Tanous     {
337*1da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
338*1da66f75SEd Tanous             immediateLogMatcher;
339*1da66f75SEd Tanous 
340*1da66f75SEd Tanous         // Only allow one Immediate Log request at a time
341*1da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
342*1da66f75SEd Tanous         {
343*1da66f75SEd Tanous             res.addHeader("Retry-After", "30");
344*1da66f75SEd Tanous             res.result(boost::beast::http::status::service_unavailable);
345*1da66f75SEd Tanous             messages::addMessageToJson(
346*1da66f75SEd Tanous                 res.jsonValue, messages::serviceTemporarilyUnavailable("30"),
347*1da66f75SEd Tanous                 "/CpuLog.Immediate");
348*1da66f75SEd Tanous             res.end();
349*1da66f75SEd Tanous             return;
350*1da66f75SEd Tanous         }
351*1da66f75SEd Tanous         // Make this static so it survives outside this method
352*1da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
353*1da66f75SEd Tanous 
354*1da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
355*1da66f75SEd Tanous         timeout.async_wait([&res](const boost::system::error_code &ec) {
356*1da66f75SEd Tanous             immediateLogMatcher = nullptr;
357*1da66f75SEd Tanous             if (ec)
358*1da66f75SEd Tanous             {
359*1da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
360*1da66f75SEd Tanous                 // completion.
361*1da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
362*1da66f75SEd Tanous                 {
363*1da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
364*1da66f75SEd Tanous                 }
365*1da66f75SEd Tanous                 return;
366*1da66f75SEd Tanous             }
367*1da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
368*1da66f75SEd Tanous 
369*1da66f75SEd Tanous             res.result(boost::beast::http::status::internal_server_error);
370*1da66f75SEd Tanous             res.end();
371*1da66f75SEd Tanous         });
372*1da66f75SEd Tanous 
373*1da66f75SEd Tanous         auto immediateLogMatcherCallback = [&res](
374*1da66f75SEd Tanous                                                sdbusplus::message::message &m) {
375*1da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
376*1da66f75SEd Tanous             boost::system::error_code ec;
377*1da66f75SEd Tanous             timeout.cancel(ec);
378*1da66f75SEd Tanous             if (ec)
379*1da66f75SEd Tanous             {
380*1da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
381*1da66f75SEd Tanous             }
382*1da66f75SEd Tanous             sdbusplus::message::object_path obj_path;
383*1da66f75SEd Tanous             boost::container::flat_map<
384*1da66f75SEd Tanous                 std::string,
385*1da66f75SEd Tanous                 boost::container::flat_map<
386*1da66f75SEd Tanous                     std::string, sdbusplus::message::variant<std::string>>>
387*1da66f75SEd Tanous                 interfaces_added;
388*1da66f75SEd Tanous             m.read(obj_path, interfaces_added);
389*1da66f75SEd Tanous             const std::string *log = mapbox::getPtr<const std::string>(
390*1da66f75SEd Tanous                 interfaces_added[CPU_LOG_INTERFACE]["Log"]);
391*1da66f75SEd Tanous             if (log == nullptr)
392*1da66f75SEd Tanous             {
393*1da66f75SEd Tanous                 res.result(boost::beast::http::status::internal_server_error);
394*1da66f75SEd Tanous                 res.end();
395*1da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
396*1da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
397*1da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
398*1da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
399*1da66f75SEd Tanous                 // be the last thing done.
400*1da66f75SEd Tanous                 immediateLogMatcher = nullptr;
401*1da66f75SEd Tanous                 return;
402*1da66f75SEd Tanous             }
403*1da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
404*1da66f75SEd Tanous             if (j.is_discarded())
405*1da66f75SEd Tanous             {
406*1da66f75SEd Tanous                 res.result(boost::beast::http::status::internal_server_error);
407*1da66f75SEd Tanous                 res.end();
408*1da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
409*1da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
410*1da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
411*1da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
412*1da66f75SEd Tanous                 // be the last thing done.
413*1da66f75SEd Tanous                 immediateLogMatcher = nullptr;
414*1da66f75SEd Tanous                 return;
415*1da66f75SEd Tanous             }
416*1da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
417*1da66f75SEd Tanous             res.jsonValue = {
418*1da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
419*1da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
420*1da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
421*1da66f75SEd Tanous                 {"EntryType", "Oem"},
422*1da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
423*1da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
424*1da66f75SEd Tanous                 {"Created", std::move(t)}};
425*1da66f75SEd Tanous             res.end();
426*1da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
427*1da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
428*1da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
429*1da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
430*1da66f75SEd Tanous             // thing done.
431*1da66f75SEd Tanous             immediateLogMatcher = nullptr;
432*1da66f75SEd Tanous         };
433*1da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
434*1da66f75SEd Tanous             *crow::connections::systemBus,
435*1da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
436*1da66f75SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0,
437*1da66f75SEd Tanous                                                        CPU_LOG_IMMEDIATE_PATH),
438*1da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
439*1da66f75SEd Tanous 
440*1da66f75SEd Tanous         auto generateImmediateLogCallback =
441*1da66f75SEd Tanous             [&res](const boost::system::error_code ec,
442*1da66f75SEd Tanous                    const std::string &resp) {
443*1da66f75SEd Tanous                 if (ec)
444*1da66f75SEd Tanous                 {
445*1da66f75SEd Tanous                     if (ec.value() ==
446*1da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
447*1da66f75SEd Tanous                     {
448*1da66f75SEd Tanous                         messages::addMessageToJson(
449*1da66f75SEd Tanous                             res.jsonValue, messages::resourceInStandby(),
450*1da66f75SEd Tanous                             "/CpuLog.Immediate");
451*1da66f75SEd Tanous                         res.result(
452*1da66f75SEd Tanous                             boost::beast::http::status::service_unavailable);
453*1da66f75SEd Tanous                     }
454*1da66f75SEd Tanous                     else
455*1da66f75SEd Tanous                     {
456*1da66f75SEd Tanous                         res.result(
457*1da66f75SEd Tanous                             boost::beast::http::status::internal_server_error);
458*1da66f75SEd Tanous                     }
459*1da66f75SEd Tanous                     res.end();
460*1da66f75SEd Tanous                     boost::system::error_code timeoutec;
461*1da66f75SEd Tanous                     timeout.cancel(timeoutec);
462*1da66f75SEd Tanous                     if (timeoutec)
463*1da66f75SEd Tanous                     {
464*1da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
465*1da66f75SEd Tanous                                          << timeoutec;
466*1da66f75SEd Tanous                     }
467*1da66f75SEd Tanous                     immediateLogMatcher = nullptr;
468*1da66f75SEd Tanous                     return;
469*1da66f75SEd Tanous                 }
470*1da66f75SEd Tanous             };
471*1da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
472*1da66f75SEd Tanous             std::move(generateImmediateLogCallback), CPU_LOG_OBJECT,
473*1da66f75SEd Tanous             CPU_LOG_PATH, CPU_LOG_IMMEDIATE_INTERFACE, "GenerateImmediateLog");
474*1da66f75SEd Tanous     }
475*1da66f75SEd Tanous };
476*1da66f75SEd Tanous 
477*1da66f75SEd Tanous class SendRawPeci : public Node
478*1da66f75SEd Tanous {
479*1da66f75SEd Tanous   public:
480*1da66f75SEd Tanous     SendRawPeci(CrowApp &app) :
481*1da66f75SEd Tanous         Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/"
482*1da66f75SEd Tanous                   "CpuLog.SendRawPeci")
483*1da66f75SEd Tanous     {
484*1da66f75SEd Tanous         entityPrivileges = {
485*1da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
486*1da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
487*1da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
488*1da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
489*1da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
490*1da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
491*1da66f75SEd Tanous     }
492*1da66f75SEd Tanous 
493*1da66f75SEd Tanous   private:
494*1da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
495*1da66f75SEd Tanous                 const std::vector<std::string> &params) override
496*1da66f75SEd Tanous     {
497*1da66f75SEd Tanous         // Get the Raw PECI command from the request
498*1da66f75SEd Tanous         nlohmann::json rawPeciCmd;
499*1da66f75SEd Tanous         if (!json_util::processJsonFromRequest(res, req, rawPeciCmd))
500*1da66f75SEd Tanous         {
501*1da66f75SEd Tanous             return;
502*1da66f75SEd Tanous         }
503*1da66f75SEd Tanous         // Get the Client Address from the request
504*1da66f75SEd Tanous         nlohmann::json::const_iterator caIt = rawPeciCmd.find("ClientAddress");
505*1da66f75SEd Tanous         if (caIt == rawPeciCmd.end())
506*1da66f75SEd Tanous         {
507*1da66f75SEd Tanous             messages::addMessageToJson(
508*1da66f75SEd Tanous                 res.jsonValue, messages::propertyMissing("ClientAddress"),
509*1da66f75SEd Tanous                 "/ClientAddress");
510*1da66f75SEd Tanous             res.result(boost::beast::http::status::bad_request);
511*1da66f75SEd Tanous             res.end();
512*1da66f75SEd Tanous             return;
513*1da66f75SEd Tanous         }
514*1da66f75SEd Tanous         const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
515*1da66f75SEd Tanous         if (ca == nullptr)
516*1da66f75SEd Tanous         {
517*1da66f75SEd Tanous             messages::addMessageToJson(
518*1da66f75SEd Tanous                 res.jsonValue,
519*1da66f75SEd Tanous                 messages::propertyValueTypeError(caIt->dump(), "ClientAddress"),
520*1da66f75SEd Tanous                 "/ClientAddress");
521*1da66f75SEd Tanous             res.result(boost::beast::http::status::bad_request);
522*1da66f75SEd Tanous             res.end();
523*1da66f75SEd Tanous             return;
524*1da66f75SEd Tanous         }
525*1da66f75SEd Tanous         // Get the Read Length from the request
526*1da66f75SEd Tanous         const uint8_t clientAddress = static_cast<uint8_t>(*ca);
527*1da66f75SEd Tanous         nlohmann::json::const_iterator rlIt = rawPeciCmd.find("ReadLength");
528*1da66f75SEd Tanous         if (rlIt == rawPeciCmd.end())
529*1da66f75SEd Tanous         {
530*1da66f75SEd Tanous             messages::addMessageToJson(res.jsonValue,
531*1da66f75SEd Tanous                                        messages::propertyMissing("ReadLength"),
532*1da66f75SEd Tanous                                        "/ReadLength");
533*1da66f75SEd Tanous             res.result(boost::beast::http::status::bad_request);
534*1da66f75SEd Tanous             res.end();
535*1da66f75SEd Tanous             return;
536*1da66f75SEd Tanous         }
537*1da66f75SEd Tanous         const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
538*1da66f75SEd Tanous         if (rl == nullptr)
539*1da66f75SEd Tanous         {
540*1da66f75SEd Tanous             messages::addMessageToJson(
541*1da66f75SEd Tanous                 res.jsonValue,
542*1da66f75SEd Tanous                 messages::propertyValueTypeError(rlIt->dump(), "ReadLength"),
543*1da66f75SEd Tanous                 "/ReadLength");
544*1da66f75SEd Tanous             res.result(boost::beast::http::status::bad_request);
545*1da66f75SEd Tanous             res.end();
546*1da66f75SEd Tanous             return;
547*1da66f75SEd Tanous         }
548*1da66f75SEd Tanous         // Get the PECI Command from the request
549*1da66f75SEd Tanous         const uint32_t readLength = static_cast<uint32_t>(*rl);
550*1da66f75SEd Tanous         nlohmann::json::const_iterator pcIt = rawPeciCmd.find("PECICommand");
551*1da66f75SEd Tanous         if (pcIt == rawPeciCmd.end())
552*1da66f75SEd Tanous         {
553*1da66f75SEd Tanous             messages::addMessageToJson(res.jsonValue,
554*1da66f75SEd Tanous                                        messages::propertyMissing("PECICommand"),
555*1da66f75SEd Tanous                                        "/PECICommand");
556*1da66f75SEd Tanous             res.result(boost::beast::http::status::bad_request);
557*1da66f75SEd Tanous             res.end();
558*1da66f75SEd Tanous             return;
559*1da66f75SEd Tanous         }
560*1da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
561*1da66f75SEd Tanous         for (auto pc : *pcIt)
562*1da66f75SEd Tanous         {
563*1da66f75SEd Tanous             const uint64_t *val = pc.get_ptr<const uint64_t *>();
564*1da66f75SEd Tanous             if (val == nullptr)
565*1da66f75SEd Tanous             {
566*1da66f75SEd Tanous                 messages::addMessageToJson(
567*1da66f75SEd Tanous                     res.jsonValue,
568*1da66f75SEd Tanous                     messages::propertyValueTypeError(
569*1da66f75SEd Tanous                         pc.dump(),
570*1da66f75SEd Tanous                         "PECICommand/" + std::to_string(peciCommand.size())),
571*1da66f75SEd Tanous                     "/PECICommand");
572*1da66f75SEd Tanous                 res.result(boost::beast::http::status::bad_request);
573*1da66f75SEd Tanous                 res.end();
574*1da66f75SEd Tanous                 return;
575*1da66f75SEd Tanous             }
576*1da66f75SEd Tanous             peciCommand.push_back(static_cast<uint8_t>(*val));
577*1da66f75SEd Tanous         }
578*1da66f75SEd Tanous         // Callback to return the Raw PECI response
579*1da66f75SEd Tanous         auto sendRawPeciCallback = [&res](const boost::system::error_code ec,
580*1da66f75SEd Tanous                                           const std::vector<uint8_t> &resp) {
581*1da66f75SEd Tanous             if (ec)
582*1da66f75SEd Tanous             {
583*1da66f75SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
584*1da66f75SEd Tanous                                  << ec.message();
585*1da66f75SEd Tanous                 res.result(boost::beast::http::status::internal_server_error);
586*1da66f75SEd Tanous                 res.end();
587*1da66f75SEd Tanous                 return;
588*1da66f75SEd Tanous             }
589*1da66f75SEd Tanous             res.jsonValue = {{"Name", "PECI Command Response"},
590*1da66f75SEd Tanous                              {"PECIResponse", resp}};
591*1da66f75SEd Tanous             res.end();
592*1da66f75SEd Tanous         };
593*1da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
594*1da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
595*1da66f75SEd Tanous             std::move(sendRawPeciCallback), CPU_LOG_OBJECT, CPU_LOG_PATH,
596*1da66f75SEd Tanous             CPU_LOG_RAW_PECI_INTERFACE, "SendRawPeci", clientAddress,
597*1da66f75SEd Tanous             readLength, peciCommand);
598*1da66f75SEd Tanous     }
599*1da66f75SEd Tanous };
600*1da66f75SEd Tanous 
601*1da66f75SEd Tanous } // namespace redfish
602