xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision f12894f8bd7fc26ffa16e5a89072e6c19095f866)
11da66f75SEd Tanous /*
21da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation
31da66f75SEd Tanous //
41da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License");
51da66f75SEd Tanous // you may not use this file except in compliance with the License.
61da66f75SEd Tanous // You may obtain a copy of the License at
71da66f75SEd Tanous //
81da66f75SEd Tanous //      http://www.apache.org/licenses/LICENSE-2.0
91da66f75SEd Tanous //
101da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software
111da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS,
121da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131da66f75SEd Tanous // See the License for the specific language governing permissions and
141da66f75SEd Tanous // limitations under the License.
151da66f75SEd Tanous */
161da66f75SEd Tanous #pragma once
171da66f75SEd Tanous 
181da66f75SEd Tanous #include "node.hpp"
191da66f75SEd Tanous 
20e1f26343SJason M. Bills #include <systemd/sd-journal.h>
21e1f26343SJason M. Bills 
221da66f75SEd Tanous #include <boost/container/flat_map.hpp>
23e1f26343SJason M. Bills #include <boost/utility/string_view.hpp>
241da66f75SEd Tanous #include <experimental/filesystem>
251da66f75SEd Tanous 
261da66f75SEd Tanous namespace redfish
271da66f75SEd Tanous {
281da66f75SEd Tanous 
294ed77cd5SEd Tanous constexpr char const *cpuLogObject = "com.intel.CpuDebugLog";
304ed77cd5SEd Tanous constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog";
314ed77cd5SEd Tanous constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate";
324ed77cd5SEd Tanous constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
334ed77cd5SEd Tanous constexpr char const *cpuLogImmediateInterface =
341da66f75SEd Tanous     "com.intel.CpuDebugLog.Immediate";
35e1f26343SJason M. Bills constexpr char const *cpuLogRawPECIInterface =
361da66f75SEd Tanous     "com.intel.CpuDebugLog.SendRawPeci";
371da66f75SEd Tanous 
381da66f75SEd Tanous namespace fs = std::experimental::filesystem;
391da66f75SEd Tanous 
401da66f75SEd Tanous class LogServiceCollection : public Node
411da66f75SEd Tanous {
421da66f75SEd Tanous   public:
431da66f75SEd Tanous     template <typename CrowApp>
441da66f75SEd Tanous     LogServiceCollection(CrowApp &app) :
454ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
461da66f75SEd Tanous     {
471da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
481da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
494ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices";
501da66f75SEd Tanous         entityPrivileges = {
51e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
52e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
53e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
54e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
55e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
56e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
571da66f75SEd Tanous     }
581da66f75SEd Tanous 
591da66f75SEd Tanous   private:
601da66f75SEd Tanous     /**
611da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
621da66f75SEd Tanous      */
631da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
641da66f75SEd Tanous                const std::vector<std::string> &params) override
651da66f75SEd Tanous     {
66e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
671da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
681da66f75SEd Tanous         // it has a duplicate entry for members
69e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
701da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
71e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
721da66f75SEd Tanous             "/redfish/v1/"
731da66f75SEd Tanous             "$metadata#LogServiceCollection.LogServiceCollection";
74e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
75e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
76e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
77e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
781da66f75SEd Tanous             "Collection of LogServices for this Manager";
79e1f26343SJason M. Bills         nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"];
801da66f75SEd Tanous         logserviceArray = nlohmann::json::array();
81e1f26343SJason M. Bills         logserviceArray.push_back(
82e1f26343SJason M. Bills             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}});
831da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
841da66f75SEd Tanous         logserviceArray.push_back(
854ed77cd5SEd Tanous             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
861da66f75SEd Tanous #endif
87e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
88e1f26343SJason M. Bills             logserviceArray.size();
891da66f75SEd Tanous     }
901da66f75SEd Tanous };
911da66f75SEd Tanous 
92e1f26343SJason M. Bills class BMCLogService : public Node
931da66f75SEd Tanous {
941da66f75SEd Tanous   public:
951da66f75SEd Tanous     template <typename CrowApp>
96e1f26343SJason M. Bills     BMCLogService(CrowApp &app) :
97e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/")
98e1f26343SJason M. Bills     {
99e1f26343SJason M. Bills         // Set the id for SubRoute
100e1f26343SJason M. Bills         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog";
101e1f26343SJason M. Bills         entityPrivileges = {
102e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
103e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
104e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
105e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
106e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
107e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
108e1f26343SJason M. Bills     }
109e1f26343SJason M. Bills 
110e1f26343SJason M. Bills   private:
111e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
112e1f26343SJason M. Bills                const std::vector<std::string> &params) override
113e1f26343SJason M. Bills     {
114e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
115e1f26343SJason M. Bills         // Copy over the static data to include the entries added by SubRoute
116e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
117e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
118e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
119e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
120e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
121e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Service";
122e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Log Service";
123e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Log";
124e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
125e1f26343SJason M. Bills     }
126e1f26343SJason M. Bills };
127e1f26343SJason M. Bills 
128e1f26343SJason M. Bills static int fillBMCLogEntryJson(const std::string &bmcLogEntryID,
129e1f26343SJason M. Bills                                sd_journal *journal,
130e1f26343SJason M. Bills                                nlohmann::json &bmcLogEntryJson)
131e1f26343SJason M. Bills {
132e1f26343SJason M. Bills     // Get the Log Entry contents
133e1f26343SJason M. Bills     int ret = 0;
134e1f26343SJason M. Bills     const char *data = nullptr;
135e1f26343SJason M. Bills     size_t length = 0;
136e1f26343SJason M. Bills 
137e1f26343SJason M. Bills     ret =
138e1f26343SJason M. Bills         sd_journal_get_data(journal, "MESSAGE", (const void **)&data, &length);
139e1f26343SJason M. Bills     if (ret < 0)
140e1f26343SJason M. Bills     {
141e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
142e1f26343SJason M. Bills         return 1;
143e1f26343SJason M. Bills     }
144e1f26343SJason M. Bills     boost::string_view msg;
145e1f26343SJason M. Bills     msg = boost::string_view(data, length);
146e1f26343SJason M. Bills     // Only use the content after the "=" character.
147e1f26343SJason M. Bills     msg.remove_prefix(std::min(msg.find("=") + 1, msg.size()));
148e1f26343SJason M. Bills 
149e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
150e1f26343SJason M. Bills     boost::string_view priority;
151e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
152e1f26343SJason M. Bills     ret =
153e1f26343SJason M. Bills         sd_journal_get_data(journal, "PRIORITY", (const void **)&data, &length);
154e1f26343SJason M. Bills     if (ret < 0)
155e1f26343SJason M. Bills     {
156e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
157e1f26343SJason M. Bills         return 1;
158e1f26343SJason M. Bills     }
159e1f26343SJason M. Bills     priority = boost::string_view(data, length);
160e1f26343SJason M. Bills     // Check length for sanity. Must be a single digit in the form
161e1f26343SJason M. Bills     // "PRIORITY=[0-7]"
162e1f26343SJason M. Bills     if (priority.size() > sizeof("PRIORITY=0"))
163e1f26343SJason M. Bills     {
164e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Invalid PRIORITY field length";
165e1f26343SJason M. Bills         return 1;
166e1f26343SJason M. Bills     }
167e1f26343SJason M. Bills     // Only use the content after the "=" character.
168e1f26343SJason M. Bills     priority.remove_prefix(std::min(priority.find("=") + 1, priority.size()));
169e1f26343SJason M. Bills     severity = strtol(priority.data(), nullptr, 10);
170e1f26343SJason M. Bills 
171e1f26343SJason M. Bills     // Get the Created time from the timestamp
172e1f26343SJason M. Bills     // Get the entry timestamp
173e1f26343SJason M. Bills     uint64_t timestamp = 0;
174e1f26343SJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
175e1f26343SJason M. Bills     if (ret < 0)
176e1f26343SJason M. Bills     {
177e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
178e1f26343SJason M. Bills                          << strerror(-ret);
179e1f26343SJason M. Bills     }
180e1f26343SJason M. Bills     time_t t =
181e1f26343SJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
182e1f26343SJason M. Bills     struct tm *loctime = localtime(&t);
183e1f26343SJason M. Bills     char entryTime[64] = {};
184e1f26343SJason M. Bills     if (NULL != loctime)
185e1f26343SJason M. Bills     {
186e1f26343SJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
187e1f26343SJason M. Bills     }
188e1f26343SJason M. Bills     // Insert the ':' into the timezone
189e1f26343SJason M. Bills     boost::string_view t1(entryTime);
190e1f26343SJason M. Bills     boost::string_view t2(entryTime);
191e1f26343SJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
192e1f26343SJason M. Bills     {
193e1f26343SJason M. Bills         t1.remove_suffix(2);
194e1f26343SJason M. Bills         t2.remove_prefix(t2.size() - 2);
195e1f26343SJason M. Bills     }
196e1f26343SJason M. Bills     const std::string entryTimeStr(t1.to_string() + ":" + t2.to_string());
197e1f26343SJason M. Bills 
198e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
199e1f26343SJason M. Bills     bmcLogEntryJson = {
200e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
201e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
202e1f26343SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" +
203e1f26343SJason M. Bills                           bmcLogEntryID},
204e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
205e1f26343SJason M. Bills         {"Id", bmcLogEntryID},
206e1f26343SJason M. Bills         {"Message", msg.to_string()},
207e1f26343SJason M. Bills         {"EntryType", "Oem"},
208e1f26343SJason M. Bills         {"Severity",
209e1f26343SJason M. Bills          severity <= 2 ? "Critical"
210e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
211e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
212e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
213e1f26343SJason M. Bills     return 0;
214e1f26343SJason M. Bills }
215e1f26343SJason M. Bills 
216e1f26343SJason M. Bills class BMCLogEntryCollection : public Node
217e1f26343SJason M. Bills {
218e1f26343SJason M. Bills   public:
219e1f26343SJason M. Bills     template <typename CrowApp>
220e1f26343SJason M. Bills     BMCLogEntryCollection(CrowApp &app) :
221e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/")
222e1f26343SJason M. Bills     {
223e1f26343SJason M. Bills         // Collections use static ID for SubRoute to add to its parent, but only
224e1f26343SJason M. Bills         // load dynamic data so the duplicate static members don't get displayed
225e1f26343SJason M. Bills         Node::json["@odata.id"] =
226e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
227e1f26343SJason M. Bills         entityPrivileges = {
228e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
229e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
230e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
231e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
232e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
233e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
234e1f26343SJason M. Bills     }
235e1f26343SJason M. Bills 
236e1f26343SJason M. Bills   private:
237e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
238e1f26343SJason M. Bills                const std::vector<std::string> &params) override
239e1f26343SJason M. Bills     {
240e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
241e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
242e1f26343SJason M. Bills         // it has a duplicate entry for members
243e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
244e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
245e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
246e1f26343SJason M. Bills             "/redfish/v1/"
247e1f26343SJason M. Bills             "$metadata#LogEntryCollection.LogEntryCollection";
248e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
249e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
250e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
251e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
252e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
253e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
254e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
255e1f26343SJason M. Bills 
256e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
257e1f26343SJason M. Bills         // for each entry
258e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
259e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
260e1f26343SJason M. Bills         if (ret < 0)
261e1f26343SJason M. Bills         {
262e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
263*f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
264e1f26343SJason M. Bills             return;
265e1f26343SJason M. Bills         }
266e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
267e1f26343SJason M. Bills             journalTmp, sd_journal_close);
268e1f26343SJason M. Bills         journalTmp = nullptr;
269e1f26343SJason M. Bills         uint64_t prevTs = 0;
270e1f26343SJason M. Bills         int index = 0;
271e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
272e1f26343SJason M. Bills         {
273e1f26343SJason M. Bills             // Get the entry timestamp
274e1f26343SJason M. Bills             uint64_t curTs = 0;
275e1f26343SJason M. Bills             ret = sd_journal_get_realtime_usec(journal.get(), &curTs);
276e1f26343SJason M. Bills             if (ret < 0)
277e1f26343SJason M. Bills             {
278e1f26343SJason M. Bills                 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
279e1f26343SJason M. Bills                                  << strerror(-ret);
280e1f26343SJason M. Bills                 continue;
281e1f26343SJason M. Bills             }
282e1f26343SJason M. Bills             // If the timestamp isn't unique, increment the index
283e1f26343SJason M. Bills             if (curTs == prevTs)
284e1f26343SJason M. Bills             {
285e1f26343SJason M. Bills                 index++;
286e1f26343SJason M. Bills             }
287e1f26343SJason M. Bills             else
288e1f26343SJason M. Bills             {
289e1f26343SJason M. Bills                 // Otherwise, reset it
290e1f26343SJason M. Bills                 index = 0;
291e1f26343SJason M. Bills             }
292e1f26343SJason M. Bills             // Save the timestamp
293e1f26343SJason M. Bills             prevTs = curTs;
294e1f26343SJason M. Bills 
295e1f26343SJason M. Bills             std::string idStr(std::to_string(curTs));
296e1f26343SJason M. Bills             if (index > 0)
297e1f26343SJason M. Bills             {
298e1f26343SJason M. Bills                 idStr += "_" + std::to_string(index);
299e1f26343SJason M. Bills             }
300e1f26343SJason M. Bills             logEntryArray.push_back({});
301e1f26343SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
302e1f26343SJason M. Bills             if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
303e1f26343SJason M. Bills             {
304*f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
305e1f26343SJason M. Bills                 return;
306e1f26343SJason M. Bills             }
307e1f26343SJason M. Bills         }
308e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = logEntryArray.size();
309e1f26343SJason M. Bills     }
310e1f26343SJason M. Bills };
311e1f26343SJason M. Bills 
312e1f26343SJason M. Bills class BMCLogEntry : public Node
313e1f26343SJason M. Bills {
314e1f26343SJason M. Bills   public:
315e1f26343SJason M. Bills     BMCLogEntry(CrowApp &app) :
316e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
317e1f26343SJason M. Bills              std::string())
318e1f26343SJason M. Bills     {
319e1f26343SJason M. Bills         entityPrivileges = {
320e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
321e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
322e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
323e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
324e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
325e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
326e1f26343SJason M. Bills     }
327e1f26343SJason M. Bills 
328e1f26343SJason M. Bills   private:
329e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
330e1f26343SJason M. Bills                const std::vector<std::string> &params) override
331e1f26343SJason M. Bills     {
332e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
333e1f26343SJason M. Bills         if (params.size() != 1)
334e1f26343SJason M. Bills         {
335*f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
336e1f26343SJason M. Bills             return;
337e1f26343SJason M. Bills         }
338e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
339e1f26343SJason M. Bills         boost::string_view tsStr(params[0]);
340e1f26343SJason M. Bills         boost::string_view indexStr(params[0]);
341e1f26343SJason M. Bills         uint64_t ts = 0;
342e1f26343SJason M. Bills         uint16_t index = 0;
343e1f26343SJason M. Bills         auto underscorePos = tsStr.find("_");
344e1f26343SJason M. Bills         if (underscorePos == tsStr.npos)
345e1f26343SJason M. Bills         {
346e1f26343SJason M. Bills             // Timestamp has no index
347e1f26343SJason M. Bills             ts = strtoull(tsStr.data(), nullptr, 10);
348e1f26343SJason M. Bills         }
349e1f26343SJason M. Bills         else
350e1f26343SJason M. Bills         {
351e1f26343SJason M. Bills             // Timestamp has an index
352e1f26343SJason M. Bills             tsStr.remove_suffix(tsStr.size() - underscorePos + 1);
353e1f26343SJason M. Bills             ts = strtoull(tsStr.data(), nullptr, 10);
354e1f26343SJason M. Bills             indexStr.remove_prefix(underscorePos + 1);
355e1f26343SJason M. Bills             index =
356e1f26343SJason M. Bills                 static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10));
357e1f26343SJason M. Bills         }
358e1f26343SJason M. Bills 
359e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
360e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
361e1f26343SJason M. Bills         if (ret < 0)
362e1f26343SJason M. Bills         {
363e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
364*f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
365e1f26343SJason M. Bills             return;
366e1f26343SJason M. Bills         }
367e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
368e1f26343SJason M. Bills             journalTmp, sd_journal_close);
369e1f26343SJason M. Bills         journalTmp = nullptr;
370e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
371e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
372e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
373e1f26343SJason M. Bills         {
374e1f26343SJason M. Bills             sd_journal_next(journal.get());
375e1f26343SJason M. Bills         }
376e1f26343SJason M. Bills         if (fillBMCLogEntryJson(params[0], journal.get(),
377e1f26343SJason M. Bills                                 asyncResp->res.jsonValue) != 0)
378e1f26343SJason M. Bills         {
379*f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
380e1f26343SJason M. Bills             return;
381e1f26343SJason M. Bills         }
382e1f26343SJason M. Bills     }
383e1f26343SJason M. Bills };
384e1f26343SJason M. Bills 
385e1f26343SJason M. Bills class CPULogService : public Node
386e1f26343SJason M. Bills {
387e1f26343SJason M. Bills   public:
388e1f26343SJason M. Bills     template <typename CrowApp>
389e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
390e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
3911da66f75SEd Tanous     {
3921da66f75SEd Tanous         // Set the id for SubRoute
3934ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
3941da66f75SEd Tanous         entityPrivileges = {
395e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
396e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
397e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
398e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
399e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
400e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
4011da66f75SEd Tanous     }
4021da66f75SEd Tanous 
4031da66f75SEd Tanous   private:
4041da66f75SEd Tanous     /**
4051da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
4061da66f75SEd Tanous      */
4071da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
4081da66f75SEd Tanous                const std::vector<std::string> &params) override
4091da66f75SEd Tanous     {
410e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
4111da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
412e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
413e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
414e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
415e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
416e1f26343SJason M. Bills             "/redfish/v1/"
4171da66f75SEd Tanous             "$metadata#LogService.LogService";
418e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
419e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
420e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
421e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
422e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
423e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
4241da66f75SEd Tanous             {"Oem",
4251da66f75SEd Tanous              {{"#CpuLog.Immediate",
4261da66f75SEd Tanous                {{"target",
4274ed77cd5SEd Tanous                  "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
4281da66f75SEd Tanous                  "CpuLog.Immediate"}}}}}};
4291da66f75SEd Tanous 
4301da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
431e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
4321da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
4331da66f75SEd Tanous              {{"target",
4344ed77cd5SEd Tanous                "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
4351da66f75SEd Tanous                "CpuLog.SendRawPeci"}}});
4361da66f75SEd Tanous #endif
4371da66f75SEd Tanous     }
4381da66f75SEd Tanous };
4391da66f75SEd Tanous 
440e1f26343SJason M. Bills class CPULogEntryCollection : public Node
4411da66f75SEd Tanous {
4421da66f75SEd Tanous   public:
4431da66f75SEd Tanous     template <typename CrowApp>
444e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
445e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
4461da66f75SEd Tanous     {
4471da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
4481da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
4491da66f75SEd Tanous         Node::json["@odata.id"] =
4504ed77cd5SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
4511da66f75SEd Tanous         entityPrivileges = {
452e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
453e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
454e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
455e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
456e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
457e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
4581da66f75SEd Tanous     }
4591da66f75SEd Tanous 
4601da66f75SEd Tanous   private:
4611da66f75SEd Tanous     /**
4621da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
4631da66f75SEd Tanous      */
4641da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
4651da66f75SEd Tanous                const std::vector<std::string> &params) override
4661da66f75SEd Tanous     {
467e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
4681da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
4691da66f75SEd Tanous         // it has a duplicate entry for members
470e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
471e1f26343SJason M. Bills                                          const boost::system::error_code ec,
4721da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
4731da66f75SEd Tanous             if (ec)
4741da66f75SEd Tanous             {
4751da66f75SEd Tanous                 if (ec.value() !=
4761da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
4771da66f75SEd Tanous                 {
4781da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
4791da66f75SEd Tanous                                      << ec.message();
480*f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
4811da66f75SEd Tanous                     return;
4821da66f75SEd Tanous                 }
4831da66f75SEd Tanous             }
484e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
4851da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
486e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
4871da66f75SEd Tanous                 "/redfish/v1/"
4881da66f75SEd Tanous                 "$metadata#LogEntryCollection.LogEntryCollection";
489e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.id"] =
490e1f26343SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
491e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
492e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
493e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
494e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
495e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
4961da66f75SEd Tanous             for (const std::string &objpath : resp)
4971da66f75SEd Tanous             {
4981da66f75SEd Tanous                 // Don't list the immediate log
4994ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
5001da66f75SEd Tanous                 {
5011da66f75SEd Tanous                     continue;
5021da66f75SEd Tanous                 }
5034ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
5044ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
5051da66f75SEd Tanous                 {
506e1f26343SJason M. Bills                     logEntryArray.push_back(
507e1f26343SJason M. Bills                         {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
508e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
5094ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
5101da66f75SEd Tanous                 }
5111da66f75SEd Tanous             }
512e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
513e1f26343SJason M. Bills                 logEntryArray.size();
5141da66f75SEd Tanous         };
5151da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
5161da66f75SEd Tanous             std::move(getLogEntriesCallback),
5171da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
5181da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
5191da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
5204ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
5211da66f75SEd Tanous     }
5221da66f75SEd Tanous };
5231da66f75SEd Tanous 
5241da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
5251da66f75SEd Tanous {
5261da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
5271da66f75SEd Tanous     if (metaIt != cpuLog.end())
5281da66f75SEd Tanous     {
5291da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
5301da66f75SEd Tanous         if (tsIt != metaIt->end())
5311da66f75SEd Tanous         {
5321da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
5331da66f75SEd Tanous             if (logTime != nullptr)
5341da66f75SEd Tanous             {
5351da66f75SEd Tanous                 return *logTime;
5361da66f75SEd Tanous             }
5371da66f75SEd Tanous         }
5381da66f75SEd Tanous     }
5391da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
5401da66f75SEd Tanous 
5411da66f75SEd Tanous     return std::string();
5421da66f75SEd Tanous }
5431da66f75SEd Tanous 
544e1f26343SJason M. Bills class CPULogEntry : public Node
5451da66f75SEd Tanous {
5461da66f75SEd Tanous   public:
547e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
5484ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
5491da66f75SEd Tanous              std::string())
5501da66f75SEd Tanous     {
5511da66f75SEd Tanous         entityPrivileges = {
552e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
553e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
554e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
555e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
556e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
557e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
5581da66f75SEd Tanous     }
5591da66f75SEd Tanous 
5601da66f75SEd Tanous   private:
5611da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
5621da66f75SEd Tanous                const std::vector<std::string> &params) override
5631da66f75SEd Tanous     {
564e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
5651da66f75SEd Tanous         if (params.size() != 1)
5661da66f75SEd Tanous         {
567*f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
5681da66f75SEd Tanous             return;
5691da66f75SEd Tanous         }
5704ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
571e1f26343SJason M. Bills         auto getStoredLogCallback =
572e1f26343SJason M. Bills             [asyncResp,
5734ed77cd5SEd Tanous              logId](const boost::system::error_code ec,
574e1f26343SJason M. Bills                     const sdbusplus::message::variant<std::string> &resp) {
5751da66f75SEd Tanous                 if (ec)
5761da66f75SEd Tanous                 {
577e1f26343SJason M. Bills                     BMCWEB_LOG_DEBUG << "failed to get log ec: "
578e1f26343SJason M. Bills                                      << ec.message();
579*f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
5801da66f75SEd Tanous                     return;
5811da66f75SEd Tanous                 }
582e1f26343SJason M. Bills                 const std::string *log =
583e1f26343SJason M. Bills                     mapbox::getPtr<const std::string>(resp);
5841da66f75SEd Tanous                 if (log == nullptr)
5851da66f75SEd Tanous                 {
586*f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
5871da66f75SEd Tanous                     return;
5881da66f75SEd Tanous                 }
5891da66f75SEd Tanous                 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
5901da66f75SEd Tanous                 if (j.is_discarded())
5911da66f75SEd Tanous                 {
592*f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
5931da66f75SEd Tanous                     return;
5941da66f75SEd Tanous                 }
5951da66f75SEd Tanous                 std::string t = getLogCreatedTime(j);
596e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {
5971da66f75SEd Tanous                     {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
598e1f26343SJason M. Bills                     {"@odata.context",
599e1f26343SJason M. Bills                      "/redfish/v1/$metadata#LogEntry.LogEntry"},
6001da66f75SEd Tanous                     {"@odata.id",
6014ed77cd5SEd Tanous                      "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
6024ed77cd5SEd Tanous                          std::to_string(logId)},
6031da66f75SEd Tanous                     {"Name", "CPU Debug Log"},
6044ed77cd5SEd Tanous                     {"Id", logId},
6051da66f75SEd Tanous                     {"EntryType", "Oem"},
6061da66f75SEd Tanous                     {"OemRecordFormat", "Intel CPU Log"},
6071da66f75SEd Tanous                     {"Oem", {{"Intel", std::move(j)}}},
6081da66f75SEd Tanous                     {"Created", std::move(t)}};
6091da66f75SEd Tanous             };
6101da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
6114ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
6124ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
6134ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
6141da66f75SEd Tanous     }
6151da66f75SEd Tanous };
6161da66f75SEd Tanous 
617e1f26343SJason M. Bills class ImmediateCPULog : public Node
6181da66f75SEd Tanous {
6191da66f75SEd Tanous   public:
620e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
6214ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
622e1f26343SJason M. Bills                   "CpuLog.Immediate/")
6231da66f75SEd Tanous     {
6241da66f75SEd Tanous         entityPrivileges = {
625e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
626e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
627e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
628e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
629e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
630e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6311da66f75SEd Tanous     }
6321da66f75SEd Tanous 
6331da66f75SEd Tanous   private:
6341da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
6351da66f75SEd Tanous                 const std::vector<std::string> &params) override
6361da66f75SEd Tanous     {
637e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6381da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
6391da66f75SEd Tanous             immediateLogMatcher;
6401da66f75SEd Tanous 
6411da66f75SEd Tanous         // Only allow one Immediate Log request at a time
6421da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
6431da66f75SEd Tanous         {
644e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
645*f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
6461da66f75SEd Tanous             return;
6471da66f75SEd Tanous         }
6481da66f75SEd Tanous         // Make this static so it survives outside this method
6491da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
6501da66f75SEd Tanous 
6511da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
652e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
6531da66f75SEd Tanous             immediateLogMatcher = nullptr;
6541da66f75SEd Tanous             if (ec)
6551da66f75SEd Tanous             {
6561da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
6571da66f75SEd Tanous                 // completion.
6581da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
6591da66f75SEd Tanous                 {
6601da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
6611da66f75SEd Tanous                 }
6621da66f75SEd Tanous                 return;
6631da66f75SEd Tanous             }
6641da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
6651da66f75SEd Tanous 
666*f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
6671da66f75SEd Tanous         });
6681da66f75SEd Tanous 
669e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
6701da66f75SEd Tanous                                                sdbusplus::message::message &m) {
6711da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
6721da66f75SEd Tanous             boost::system::error_code ec;
6731da66f75SEd Tanous             timeout.cancel(ec);
6741da66f75SEd Tanous             if (ec)
6751da66f75SEd Tanous             {
6761da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
6771da66f75SEd Tanous             }
6784ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
6791da66f75SEd Tanous             boost::container::flat_map<
6801da66f75SEd Tanous                 std::string,
6811da66f75SEd Tanous                 boost::container::flat_map<
6821da66f75SEd Tanous                     std::string, sdbusplus::message::variant<std::string>>>
6834ed77cd5SEd Tanous                 interfacesAdded;
6844ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
6851da66f75SEd Tanous             const std::string *log = mapbox::getPtr<const std::string>(
6864ed77cd5SEd Tanous                 interfacesAdded[cpuLogInterface]["Log"]);
6871da66f75SEd Tanous             if (log == nullptr)
6881da66f75SEd Tanous             {
689*f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
6901da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
6911da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
6921da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
6931da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
6941da66f75SEd Tanous                 // be the last thing done.
6951da66f75SEd Tanous                 immediateLogMatcher = nullptr;
6961da66f75SEd Tanous                 return;
6971da66f75SEd Tanous             }
6981da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
6991da66f75SEd Tanous             if (j.is_discarded())
7001da66f75SEd Tanous             {
701*f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
7021da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
7031da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
7041da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
7051da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
7061da66f75SEd Tanous                 // be the last thing done.
7071da66f75SEd Tanous                 immediateLogMatcher = nullptr;
7081da66f75SEd Tanous                 return;
7091da66f75SEd Tanous             }
7101da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
711e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
7121da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
7131da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
7141da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
7151da66f75SEd Tanous                 {"EntryType", "Oem"},
7161da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
7171da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
7181da66f75SEd Tanous                 {"Created", std::move(t)}};
7191da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
7201da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
7211da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
7221da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
7231da66f75SEd Tanous             // thing done.
7241da66f75SEd Tanous             immediateLogMatcher = nullptr;
7251da66f75SEd Tanous         };
7261da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
7271da66f75SEd Tanous             *crow::connections::systemBus,
7281da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
7294ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
7301da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
7311da66f75SEd Tanous 
7321da66f75SEd Tanous         auto generateImmediateLogCallback =
733e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
7341da66f75SEd Tanous                         const std::string &resp) {
7351da66f75SEd Tanous                 if (ec)
7361da66f75SEd Tanous                 {
7371da66f75SEd Tanous                     if (ec.value() ==
7381da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
7391da66f75SEd Tanous                     {
740*f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
7411da66f75SEd Tanous                     }
7421da66f75SEd Tanous                     else
7431da66f75SEd Tanous                     {
744*f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
7451da66f75SEd Tanous                     }
7461da66f75SEd Tanous                     boost::system::error_code timeoutec;
7471da66f75SEd Tanous                     timeout.cancel(timeoutec);
7481da66f75SEd Tanous                     if (timeoutec)
7491da66f75SEd Tanous                     {
7501da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
7511da66f75SEd Tanous                                          << timeoutec;
7521da66f75SEd Tanous                     }
7531da66f75SEd Tanous                     immediateLogMatcher = nullptr;
7541da66f75SEd Tanous                     return;
7551da66f75SEd Tanous                 }
7561da66f75SEd Tanous             };
7571da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
7584ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
7594ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
7601da66f75SEd Tanous     }
7611da66f75SEd Tanous };
7621da66f75SEd Tanous 
763e1f26343SJason M. Bills class SendRawPECI : public Node
7641da66f75SEd Tanous {
7651da66f75SEd Tanous   public:
766e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
7674ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
768e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
7691da66f75SEd Tanous     {
7701da66f75SEd Tanous         entityPrivileges = {
7711da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
7721da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
7731da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
7741da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
7751da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
7761da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
7771da66f75SEd Tanous     }
7781da66f75SEd Tanous 
7791da66f75SEd Tanous   private:
7801da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
7811da66f75SEd Tanous                 const std::vector<std::string> &params) override
7821da66f75SEd Tanous     {
783e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
7841da66f75SEd Tanous         // Get the Raw PECI command from the request
785e1f26343SJason M. Bills         nlohmann::json rawPECICmd;
786e1f26343SJason M. Bills         if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
7871da66f75SEd Tanous         {
7881da66f75SEd Tanous             return;
7891da66f75SEd Tanous         }
7901da66f75SEd Tanous         // Get the Client Address from the request
791e1f26343SJason M. Bills         nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
792e1f26343SJason M. Bills         if (caIt == rawPECICmd.end())
7931da66f75SEd Tanous         {
794*f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "ClientAddress",
795*f12894f8SJason M. Bills                                       "/ClientAddress");
7961da66f75SEd Tanous             return;
7971da66f75SEd Tanous         }
7981da66f75SEd Tanous         const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
7991da66f75SEd Tanous         if (ca == nullptr)
8001da66f75SEd Tanous         {
801*f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, caIt->dump(),
802*f12894f8SJason M. Bills                                              "ClientAddress", "/ClientAddress");
8031da66f75SEd Tanous             return;
8041da66f75SEd Tanous         }
8051da66f75SEd Tanous         // Get the Read Length from the request
8061da66f75SEd Tanous         const uint8_t clientAddress = static_cast<uint8_t>(*ca);
807e1f26343SJason M. Bills         nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
808e1f26343SJason M. Bills         if (rlIt == rawPECICmd.end())
8091da66f75SEd Tanous         {
810*f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "ReadLength",
8111da66f75SEd Tanous                                       "/ReadLength");
8121da66f75SEd Tanous             return;
8131da66f75SEd Tanous         }
8141da66f75SEd Tanous         const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
8151da66f75SEd Tanous         if (rl == nullptr)
8161da66f75SEd Tanous         {
817*f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, rlIt->dump(),
818*f12894f8SJason M. Bills                                              "ReadLength", "/ReadLength");
8191da66f75SEd Tanous             return;
8201da66f75SEd Tanous         }
8211da66f75SEd Tanous         // Get the PECI Command from the request
8221da66f75SEd Tanous         const uint32_t readLength = static_cast<uint32_t>(*rl);
823e1f26343SJason M. Bills         nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
824e1f26343SJason M. Bills         if (pcIt == rawPECICmd.end())
8251da66f75SEd Tanous         {
826*f12894f8SJason M. Bills             messages::propertyMissing(asyncResp->res, "PECICommand",
8271da66f75SEd Tanous                                       "/PECICommand");
8281da66f75SEd Tanous             return;
8291da66f75SEd Tanous         }
8301da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
8311da66f75SEd Tanous         for (auto pc : *pcIt)
8321da66f75SEd Tanous         {
8331da66f75SEd Tanous             const uint64_t *val = pc.get_ptr<const uint64_t *>();
8341da66f75SEd Tanous             if (val == nullptr)
8351da66f75SEd Tanous             {
8361da66f75SEd Tanous                 messages::propertyValueTypeError(
837*f12894f8SJason M. Bills                     asyncResp->res, pc.dump(),
838*f12894f8SJason M. Bills                     "PECICommand/" + std::to_string(peciCommand.size()),
8391da66f75SEd Tanous                     "/PECICommand");
8401da66f75SEd Tanous                 return;
8411da66f75SEd Tanous             }
8421da66f75SEd Tanous             peciCommand.push_back(static_cast<uint8_t>(*val));
8431da66f75SEd Tanous         }
8441da66f75SEd Tanous         // Callback to return the Raw PECI response
845e1f26343SJason M. Bills         auto sendRawPECICallback =
846e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
8471da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
8481da66f75SEd Tanous                 if (ec)
8491da66f75SEd Tanous                 {
8501da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
8511da66f75SEd Tanous                                      << ec.message();
852*f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
8531da66f75SEd Tanous                     return;
8541da66f75SEd Tanous                 }
855e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
8561da66f75SEd Tanous                                             {"PECIResponse", resp}};
8571da66f75SEd Tanous             };
8581da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
8591da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
860e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
861e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
8624ed77cd5SEd Tanous             peciCommand);
8631da66f75SEd Tanous     }
8641da66f75SEd Tanous };
8651da66f75SEd Tanous 
8661da66f75SEd Tanous } // namespace redfish
867