xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 16428a1a312ac17209d71593298e14f833a03c2c)
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 
40*16428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
41*16428a1aSJason M. Bills                               const boost::string_view &field,
42*16428a1aSJason M. Bills                               boost::string_view &contents)
43*16428a1aSJason M. Bills {
44*16428a1aSJason M. Bills     const char *data = nullptr;
45*16428a1aSJason M. Bills     size_t length = 0;
46*16428a1aSJason M. Bills     int ret = 0;
47*16428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
48*16428a1aSJason M. Bills     ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
49*16428a1aSJason M. Bills                               &length);
50*16428a1aSJason M. Bills     if (ret < 0)
51*16428a1aSJason M. Bills     {
52*16428a1aSJason M. Bills         return ret;
53*16428a1aSJason M. Bills     }
54*16428a1aSJason M. Bills     contents = boost::string_view(data, length);
55*16428a1aSJason M. Bills     // Only use the content after the "=" character.
56*16428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
57*16428a1aSJason M. Bills     return ret;
58*16428a1aSJason M. Bills }
59*16428a1aSJason M. Bills 
60*16428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
61*16428a1aSJason M. Bills                               const boost::string_view &field, const int &base,
62*16428a1aSJason M. Bills                               int &contents)
63*16428a1aSJason M. Bills {
64*16428a1aSJason M. Bills     int ret = 0;
65*16428a1aSJason M. Bills     boost::string_view metadata;
66*16428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
67*16428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
68*16428a1aSJason M. Bills     if (ret < 0)
69*16428a1aSJason M. Bills     {
70*16428a1aSJason M. Bills         return ret;
71*16428a1aSJason M. Bills     }
72*16428a1aSJason M. Bills     contents = strtol(metadata.data(), nullptr, base);
73*16428a1aSJason M. Bills     return ret;
74*16428a1aSJason M. Bills }
75*16428a1aSJason M. Bills 
76*16428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
77*16428a1aSJason M. Bills {
78*16428a1aSJason M. Bills     int ret = 0;
79*16428a1aSJason M. Bills     uint64_t timestamp = 0;
80*16428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
81*16428a1aSJason M. Bills     if (ret < 0)
82*16428a1aSJason M. Bills     {
83*16428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
84*16428a1aSJason M. Bills                          << strerror(-ret);
85*16428a1aSJason M. Bills         return false;
86*16428a1aSJason M. Bills     }
87*16428a1aSJason M. Bills     time_t t =
88*16428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
89*16428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
90*16428a1aSJason M. Bills     char entryTime[64] = {};
91*16428a1aSJason M. Bills     if (NULL != loctime)
92*16428a1aSJason M. Bills     {
93*16428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
94*16428a1aSJason M. Bills     }
95*16428a1aSJason M. Bills     // Insert the ':' into the timezone
96*16428a1aSJason M. Bills     boost::string_view t1(entryTime);
97*16428a1aSJason M. Bills     boost::string_view t2(entryTime);
98*16428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
99*16428a1aSJason M. Bills     {
100*16428a1aSJason M. Bills         t1.remove_suffix(2);
101*16428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
102*16428a1aSJason M. Bills     }
103*16428a1aSJason M. Bills     entryTimestamp = t1.to_string() + ":" + t2.to_string();
104*16428a1aSJason M. Bills     return true;
105*16428a1aSJason M. Bills }
106*16428a1aSJason M. Bills 
107*16428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
108*16428a1aSJason M. Bills                          long &skip)
109*16428a1aSJason M. Bills {
110*16428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
111*16428a1aSJason M. Bills     if (skipParam != nullptr)
112*16428a1aSJason M. Bills     {
113*16428a1aSJason M. Bills         char *ptr = nullptr;
114*16428a1aSJason M. Bills         skip = std::strtol(skipParam, &ptr, 10);
115*16428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
116*16428a1aSJason M. Bills         {
117*16428a1aSJason M. Bills 
118*16428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
119*16428a1aSJason M. Bills                                                    "$skip");
120*16428a1aSJason M. Bills             return false;
121*16428a1aSJason M. Bills         }
122*16428a1aSJason M. Bills         if (skip < 0)
123*16428a1aSJason M. Bills         {
124*16428a1aSJason M. Bills 
125*16428a1aSJason M. Bills             messages::queryParameterOutOfRange(res, std::to_string(skip),
126*16428a1aSJason M. Bills                                                "$skip", "greater than 0");
127*16428a1aSJason M. Bills             return false;
128*16428a1aSJason M. Bills         }
129*16428a1aSJason M. Bills     }
130*16428a1aSJason M. Bills     return true;
131*16428a1aSJason M. Bills }
132*16428a1aSJason M. Bills 
133*16428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000;
134*16428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
135*16428a1aSJason M. Bills                         long &top)
136*16428a1aSJason M. Bills {
137*16428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
138*16428a1aSJason M. Bills     if (topParam != nullptr)
139*16428a1aSJason M. Bills     {
140*16428a1aSJason M. Bills         char *ptr = nullptr;
141*16428a1aSJason M. Bills         top = std::strtol(topParam, &ptr, 10);
142*16428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
143*16428a1aSJason M. Bills         {
144*16428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
145*16428a1aSJason M. Bills                                                    "$top");
146*16428a1aSJason M. Bills             return false;
147*16428a1aSJason M. Bills         }
148*16428a1aSJason M. Bills         if (top < 1 || top > maxEntriesPerPage)
149*16428a1aSJason M. Bills         {
150*16428a1aSJason M. Bills 
151*16428a1aSJason M. Bills             messages::queryParameterOutOfRange(
152*16428a1aSJason M. Bills                 res, std::to_string(top), "$top",
153*16428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
154*16428a1aSJason M. Bills             return false;
155*16428a1aSJason M. Bills         }
156*16428a1aSJason M. Bills     }
157*16428a1aSJason M. Bills     return true;
158*16428a1aSJason M. Bills }
159*16428a1aSJason M. Bills 
160*16428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
161*16428a1aSJason M. Bills {
162*16428a1aSJason M. Bills     int ret = 0;
163*16428a1aSJason M. Bills     static uint64_t prevTs = 0;
164*16428a1aSJason M. Bills     static int index = 0;
165*16428a1aSJason M. Bills     // Get the entry timestamp
166*16428a1aSJason M. Bills     uint64_t curTs = 0;
167*16428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &curTs);
168*16428a1aSJason M. Bills     if (ret < 0)
169*16428a1aSJason M. Bills     {
170*16428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
171*16428a1aSJason M. Bills                          << strerror(-ret);
172*16428a1aSJason M. Bills         return false;
173*16428a1aSJason M. Bills     }
174*16428a1aSJason M. Bills     // If the timestamp isn't unique, increment the index
175*16428a1aSJason M. Bills     if (curTs == prevTs)
176*16428a1aSJason M. Bills     {
177*16428a1aSJason M. Bills         index++;
178*16428a1aSJason M. Bills     }
179*16428a1aSJason M. Bills     else
180*16428a1aSJason M. Bills     {
181*16428a1aSJason M. Bills         // Otherwise, reset it
182*16428a1aSJason M. Bills         index = 0;
183*16428a1aSJason M. Bills     }
184*16428a1aSJason M. Bills     // Save the timestamp
185*16428a1aSJason M. Bills     prevTs = curTs;
186*16428a1aSJason M. Bills 
187*16428a1aSJason M. Bills     entryID = std::to_string(curTs);
188*16428a1aSJason M. Bills     if (index > 0)
189*16428a1aSJason M. Bills     {
190*16428a1aSJason M. Bills         entryID += "_" + std::to_string(index);
191*16428a1aSJason M. Bills     }
192*16428a1aSJason M. Bills     return true;
193*16428a1aSJason M. Bills }
194*16428a1aSJason M. Bills 
195*16428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
196*16428a1aSJason M. Bills                                uint64_t &timestamp, uint16_t &index)
197*16428a1aSJason M. Bills {
198*16428a1aSJason M. Bills     if (entryID.empty())
199*16428a1aSJason M. Bills     {
200*16428a1aSJason M. Bills         return false;
201*16428a1aSJason M. Bills     }
202*16428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
203*16428a1aSJason M. Bills     boost::string_view tsStr(entryID);
204*16428a1aSJason M. Bills 
205*16428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
206*16428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
207*16428a1aSJason M. Bills     {
208*16428a1aSJason M. Bills         // Timestamp has an index
209*16428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
210*16428a1aSJason M. Bills         boost::string_view indexStr(entryID);
211*16428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
212*16428a1aSJason M. Bills         std::size_t pos;
213*16428a1aSJason M. Bills         try
214*16428a1aSJason M. Bills         {
215*16428a1aSJason M. Bills             index = std::stoul(indexStr.to_string(), &pos);
216*16428a1aSJason M. Bills         }
217*16428a1aSJason M. Bills         catch (std::invalid_argument)
218*16428a1aSJason M. Bills         {
219*16428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
220*16428a1aSJason M. Bills             return false;
221*16428a1aSJason M. Bills         }
222*16428a1aSJason M. Bills         catch (std::out_of_range)
223*16428a1aSJason M. Bills         {
224*16428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
225*16428a1aSJason M. Bills             return false;
226*16428a1aSJason M. Bills         }
227*16428a1aSJason M. Bills         if (pos != indexStr.size())
228*16428a1aSJason M. Bills         {
229*16428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
230*16428a1aSJason M. Bills             return false;
231*16428a1aSJason M. Bills         }
232*16428a1aSJason M. Bills     }
233*16428a1aSJason M. Bills     // Timestamp has no index
234*16428a1aSJason M. Bills     std::size_t pos;
235*16428a1aSJason M. Bills     try
236*16428a1aSJason M. Bills     {
237*16428a1aSJason M. Bills         timestamp = std::stoull(tsStr.to_string(), &pos);
238*16428a1aSJason M. Bills     }
239*16428a1aSJason M. Bills     catch (std::invalid_argument)
240*16428a1aSJason M. Bills     {
241*16428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
242*16428a1aSJason M. Bills         return false;
243*16428a1aSJason M. Bills     }
244*16428a1aSJason M. Bills     catch (std::out_of_range)
245*16428a1aSJason M. Bills     {
246*16428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
247*16428a1aSJason M. Bills         return false;
248*16428a1aSJason M. Bills     }
249*16428a1aSJason M. Bills     if (pos != tsStr.size())
250*16428a1aSJason M. Bills     {
251*16428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
252*16428a1aSJason M. Bills         return false;
253*16428a1aSJason M. Bills     }
254*16428a1aSJason M. Bills     return true;
255*16428a1aSJason M. Bills }
256*16428a1aSJason M. Bills 
2571da66f75SEd Tanous class LogServiceCollection : public Node
2581da66f75SEd Tanous {
2591da66f75SEd Tanous   public:
2601da66f75SEd Tanous     template <typename CrowApp>
2611da66f75SEd Tanous     LogServiceCollection(CrowApp &app) :
2624ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
2631da66f75SEd Tanous     {
2641da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
2651da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
2664ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices";
2671da66f75SEd Tanous         entityPrivileges = {
268e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
269e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
270e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
271e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
272e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
273e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2741da66f75SEd Tanous     }
2751da66f75SEd Tanous 
2761da66f75SEd Tanous   private:
2771da66f75SEd Tanous     /**
2781da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
2791da66f75SEd Tanous      */
2801da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
2811da66f75SEd Tanous                const std::vector<std::string> &params) override
2821da66f75SEd Tanous     {
283e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2841da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
2851da66f75SEd Tanous         // it has a duplicate entry for members
286e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
2871da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
288e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
2891da66f75SEd Tanous             "/redfish/v1/"
2901da66f75SEd Tanous             "$metadata#LogServiceCollection.LogServiceCollection";
291e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
292e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
293e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
294e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
2951da66f75SEd Tanous             "Collection of LogServices for this Manager";
296e1f26343SJason M. Bills         nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"];
2971da66f75SEd Tanous         logserviceArray = nlohmann::json::array();
298e1f26343SJason M. Bills         logserviceArray.push_back(
299e1f26343SJason M. Bills             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}});
3001da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
3011da66f75SEd Tanous         logserviceArray.push_back(
3024ed77cd5SEd Tanous             {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
3031da66f75SEd Tanous #endif
304e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
305e1f26343SJason M. Bills             logserviceArray.size();
3061da66f75SEd Tanous     }
3071da66f75SEd Tanous };
3081da66f75SEd Tanous 
309e1f26343SJason M. Bills class BMCLogService : public Node
3101da66f75SEd Tanous {
3111da66f75SEd Tanous   public:
3121da66f75SEd Tanous     template <typename CrowApp>
313e1f26343SJason M. Bills     BMCLogService(CrowApp &app) :
314e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/")
315e1f26343SJason M. Bills     {
316e1f26343SJason M. Bills         // Set the id for SubRoute
317e1f26343SJason M. Bills         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog";
318e1f26343SJason M. Bills         entityPrivileges = {
319e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
320e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
321e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
322e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
323e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
324e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
325e1f26343SJason M. Bills     }
326e1f26343SJason M. Bills 
327e1f26343SJason M. Bills   private:
328e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
329e1f26343SJason M. Bills                const std::vector<std::string> &params) override
330e1f26343SJason M. Bills     {
331e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
332e1f26343SJason M. Bills         // Copy over the static data to include the entries added by SubRoute
333e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
334e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
335e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
336e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
337e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
338e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Service";
339e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Log Service";
340e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Log";
341e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
342e1f26343SJason M. Bills     }
343e1f26343SJason M. Bills };
344e1f26343SJason M. Bills 
345e1f26343SJason M. Bills static int fillBMCLogEntryJson(const std::string &bmcLogEntryID,
346e1f26343SJason M. Bills                                sd_journal *journal,
347e1f26343SJason M. Bills                                nlohmann::json &bmcLogEntryJson)
348e1f26343SJason M. Bills {
349e1f26343SJason M. Bills     // Get the Log Entry contents
350e1f26343SJason M. Bills     int ret = 0;
351e1f26343SJason M. Bills 
352*16428a1aSJason M. Bills     boost::string_view msg;
353*16428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
354e1f26343SJason M. Bills     if (ret < 0)
355e1f26343SJason M. Bills     {
356e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
357e1f26343SJason M. Bills         return 1;
358e1f26343SJason M. Bills     }
359e1f26343SJason M. Bills 
360e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
361e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
362*16428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
363e1f26343SJason M. Bills     if (ret < 0)
364e1f26343SJason M. Bills     {
365e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
366e1f26343SJason M. Bills         return 1;
367e1f26343SJason M. Bills     }
368e1f26343SJason M. Bills 
369e1f26343SJason M. Bills     // Get the Created time from the timestamp
370*16428a1aSJason M. Bills     std::string entryTimeStr;
371*16428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
372e1f26343SJason M. Bills     {
373*16428a1aSJason M. Bills         return 1;
374e1f26343SJason M. Bills     }
375e1f26343SJason M. Bills 
376e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
377e1f26343SJason M. Bills     bmcLogEntryJson = {
378e1f26343SJason M. Bills         {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
379e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
380e1f26343SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" +
381e1f26343SJason M. Bills                           bmcLogEntryID},
382e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
383e1f26343SJason M. Bills         {"Id", bmcLogEntryID},
384*16428a1aSJason M. Bills         {"Message", msg},
385e1f26343SJason M. Bills         {"EntryType", "Oem"},
386e1f26343SJason M. Bills         {"Severity",
387e1f26343SJason M. Bills          severity <= 2 ? "Critical"
388e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
389e1f26343SJason M. Bills         {"OemRecordFormat", "Intel BMC Journal Entry"},
390e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
391e1f26343SJason M. Bills     return 0;
392e1f26343SJason M. Bills }
393e1f26343SJason M. Bills 
394e1f26343SJason M. Bills class BMCLogEntryCollection : public Node
395e1f26343SJason M. Bills {
396e1f26343SJason M. Bills   public:
397e1f26343SJason M. Bills     template <typename CrowApp>
398e1f26343SJason M. Bills     BMCLogEntryCollection(CrowApp &app) :
399e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/")
400e1f26343SJason M. Bills     {
401e1f26343SJason M. Bills         // Collections use static ID for SubRoute to add to its parent, but only
402e1f26343SJason M. Bills         // load dynamic data so the duplicate static members don't get displayed
403e1f26343SJason M. Bills         Node::json["@odata.id"] =
404e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
405e1f26343SJason M. Bills         entityPrivileges = {
406e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
407e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
408e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
409e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
410e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
411e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
412e1f26343SJason M. Bills     }
413e1f26343SJason M. Bills 
414e1f26343SJason M. Bills   private:
415e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
416e1f26343SJason M. Bills                const std::vector<std::string> &params) override
417e1f26343SJason M. Bills     {
418e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
419193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
420193ad2faSJason M. Bills         long skip = 0;
421193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
422*16428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
423193ad2faSJason M. Bills         {
424193ad2faSJason M. Bills             return;
425193ad2faSJason M. Bills         }
426*16428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
427193ad2faSJason M. Bills         {
428193ad2faSJason M. Bills             return;
429193ad2faSJason M. Bills         }
430e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
431e1f26343SJason M. Bills         // it has a duplicate entry for members
432e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
433e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
434e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
435e1f26343SJason M. Bills             "/redfish/v1/"
436e1f26343SJason M. Bills             "$metadata#LogEntryCollection.LogEntryCollection";
437e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
438e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
439e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
440e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
441e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
442e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
443e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
444e1f26343SJason M. Bills 
445e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
446e1f26343SJason M. Bills         // for each entry
447e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
448e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
449e1f26343SJason M. Bills         if (ret < 0)
450e1f26343SJason M. Bills         {
451e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
452f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
453e1f26343SJason M. Bills             return;
454e1f26343SJason M. Bills         }
455e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
456e1f26343SJason M. Bills             journalTmp, sd_journal_close);
457e1f26343SJason M. Bills         journalTmp = nullptr;
458193ad2faSJason M. Bills         uint64_t entryCount = 0;
459e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
460e1f26343SJason M. Bills         {
461193ad2faSJason M. Bills             entryCount++;
462193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
463193ad2faSJason M. Bills             // start) and top (number of entries to display)
464193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
465193ad2faSJason M. Bills             {
466193ad2faSJason M. Bills                 continue;
467193ad2faSJason M. Bills             }
468193ad2faSJason M. Bills 
469*16428a1aSJason M. Bills             std::string idStr;
470*16428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
471e1f26343SJason M. Bills             {
472e1f26343SJason M. Bills                 continue;
473e1f26343SJason M. Bills             }
474e1f26343SJason M. Bills 
475e1f26343SJason M. Bills             logEntryArray.push_back({});
476e1f26343SJason M. Bills             nlohmann::json &bmcLogEntry = logEntryArray.back();
477e1f26343SJason M. Bills             if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
478e1f26343SJason M. Bills             {
479f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
480e1f26343SJason M. Bills                 return;
481e1f26343SJason M. Bills             }
482e1f26343SJason M. Bills         }
483193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
484193ad2faSJason M. Bills         if (skip + top < entryCount)
485193ad2faSJason M. Bills         {
486193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
487193ad2faSJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
488193ad2faSJason M. Bills                 std::to_string(skip + top);
489193ad2faSJason M. Bills         }
490e1f26343SJason M. Bills     }
491e1f26343SJason M. Bills };
492e1f26343SJason M. Bills 
493e1f26343SJason M. Bills class BMCLogEntry : public Node
494e1f26343SJason M. Bills {
495e1f26343SJason M. Bills   public:
496e1f26343SJason M. Bills     BMCLogEntry(CrowApp &app) :
497e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
498e1f26343SJason M. Bills              std::string())
499e1f26343SJason M. Bills     {
500e1f26343SJason M. Bills         entityPrivileges = {
501e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
502e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
503e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
504e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
505e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
506e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
507e1f26343SJason M. Bills     }
508e1f26343SJason M. Bills 
509e1f26343SJason M. Bills   private:
510e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
511e1f26343SJason M. Bills                const std::vector<std::string> &params) override
512e1f26343SJason M. Bills     {
513e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
514e1f26343SJason M. Bills         if (params.size() != 1)
515e1f26343SJason M. Bills         {
516f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
517e1f26343SJason M. Bills             return;
518e1f26343SJason M. Bills         }
519*16428a1aSJason M. Bills         const std::string &entryID = params[0];
520e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
521e1f26343SJason M. Bills         uint64_t ts = 0;
522e1f26343SJason M. Bills         uint16_t index = 0;
523*16428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
524e1f26343SJason M. Bills         {
525*16428a1aSJason M. Bills             return;
526e1f26343SJason M. Bills         }
527e1f26343SJason M. Bills 
528e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
529e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
530e1f26343SJason M. Bills         if (ret < 0)
531e1f26343SJason M. Bills         {
532e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
533f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
534e1f26343SJason M. Bills             return;
535e1f26343SJason M. Bills         }
536e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
537e1f26343SJason M. Bills             journalTmp, sd_journal_close);
538e1f26343SJason M. Bills         journalTmp = nullptr;
539e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
540e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
541e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
542e1f26343SJason M. Bills         {
543e1f26343SJason M. Bills             sd_journal_next(journal.get());
544e1f26343SJason M. Bills         }
545e1f26343SJason M. Bills         if (fillBMCLogEntryJson(params[0], journal.get(),
546e1f26343SJason M. Bills                                 asyncResp->res.jsonValue) != 0)
547e1f26343SJason M. Bills         {
548f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
549e1f26343SJason M. Bills             return;
550e1f26343SJason M. Bills         }
551e1f26343SJason M. Bills     }
552e1f26343SJason M. Bills };
553e1f26343SJason M. Bills 
554e1f26343SJason M. Bills class CPULogService : public Node
555e1f26343SJason M. Bills {
556e1f26343SJason M. Bills   public:
557e1f26343SJason M. Bills     template <typename CrowApp>
558e1f26343SJason M. Bills     CPULogService(CrowApp &app) :
559e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
5601da66f75SEd Tanous     {
5611da66f75SEd Tanous         // Set the id for SubRoute
5624ed77cd5SEd Tanous         Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
5631da66f75SEd Tanous         entityPrivileges = {
564e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
565e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
566e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
567e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
568e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
569e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
5701da66f75SEd Tanous     }
5711da66f75SEd Tanous 
5721da66f75SEd Tanous   private:
5731da66f75SEd Tanous     /**
5741da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
5751da66f75SEd Tanous      */
5761da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
5771da66f75SEd Tanous                const std::vector<std::string> &params) override
5781da66f75SEd Tanous     {
579e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
5801da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
581e1f26343SJason M. Bills         asyncResp->res.jsonValue = Node::json;
582e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
583e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
584e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
585e1f26343SJason M. Bills             "/redfish/v1/"
5861da66f75SEd Tanous             "$metadata#LogService.LogService";
587e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
588e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] = "CPU Log Service";
589e1f26343SJason M. Bills         asyncResp->res.jsonValue["Id"] = "CPU Log";
590e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
591e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
592e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
5931da66f75SEd Tanous             {"Oem",
5941da66f75SEd Tanous              {{"#CpuLog.Immediate",
5951da66f75SEd Tanous                {{"target",
5964ed77cd5SEd Tanous                  "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
5971da66f75SEd Tanous                  "CpuLog.Immediate"}}}}}};
5981da66f75SEd Tanous 
5991da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
600e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
6011da66f75SEd Tanous             {"#CpuLog.SendRawPeci",
6021da66f75SEd Tanous              {{"target",
6034ed77cd5SEd Tanous                "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
6041da66f75SEd Tanous                "CpuLog.SendRawPeci"}}});
6051da66f75SEd Tanous #endif
6061da66f75SEd Tanous     }
6071da66f75SEd Tanous };
6081da66f75SEd Tanous 
609e1f26343SJason M. Bills class CPULogEntryCollection : public Node
6101da66f75SEd Tanous {
6111da66f75SEd Tanous   public:
6121da66f75SEd Tanous     template <typename CrowApp>
613e1f26343SJason M. Bills     CPULogEntryCollection(CrowApp &app) :
614e1f26343SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
6151da66f75SEd Tanous     {
6161da66f75SEd Tanous         // Collections use static ID for SubRoute to add to its parent, but only
6171da66f75SEd Tanous         // load dynamic data so the duplicate static members don't get displayed
6181da66f75SEd Tanous         Node::json["@odata.id"] =
6194ed77cd5SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
6201da66f75SEd Tanous         entityPrivileges = {
621e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
622e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
623e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
624e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
625e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
626e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
6271da66f75SEd Tanous     }
6281da66f75SEd Tanous 
6291da66f75SEd Tanous   private:
6301da66f75SEd Tanous     /**
6311da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
6321da66f75SEd Tanous      */
6331da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
6341da66f75SEd Tanous                const std::vector<std::string> &params) override
6351da66f75SEd Tanous     {
636e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
6371da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
6381da66f75SEd Tanous         // it has a duplicate entry for members
639e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
640e1f26343SJason M. Bills                                          const boost::system::error_code ec,
6411da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
6421da66f75SEd Tanous             if (ec)
6431da66f75SEd Tanous             {
6441da66f75SEd Tanous                 if (ec.value() !=
6451da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
6461da66f75SEd Tanous                 {
6471da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
6481da66f75SEd Tanous                                      << ec.message();
649f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
6501da66f75SEd Tanous                     return;
6511da66f75SEd Tanous                 }
6521da66f75SEd Tanous             }
653e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
6541da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
655e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
6561da66f75SEd Tanous                 "/redfish/v1/"
6571da66f75SEd Tanous                 "$metadata#LogEntryCollection.LogEntryCollection";
658e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.id"] =
659e1f26343SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
660e1f26343SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
661e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
662e1f26343SJason M. Bills                 "Collection of CPU Log Entries";
663e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
664e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
6651da66f75SEd Tanous             for (const std::string &objpath : resp)
6661da66f75SEd Tanous             {
6671da66f75SEd Tanous                 // Don't list the immediate log
6684ed77cd5SEd Tanous                 if (objpath.compare(cpuLogImmediatePath) == 0)
6691da66f75SEd Tanous                 {
6701da66f75SEd Tanous                     continue;
6711da66f75SEd Tanous                 }
6724ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
6734ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
6741da66f75SEd Tanous                 {
675e1f26343SJason M. Bills                     logEntryArray.push_back(
676e1f26343SJason M. Bills                         {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
677e1f26343SJason M. Bills                                        "CpuLog/Entries/" +
6784ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
6791da66f75SEd Tanous                 }
6801da66f75SEd Tanous             }
681e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
682e1f26343SJason M. Bills                 logEntryArray.size();
6831da66f75SEd Tanous         };
6841da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
6851da66f75SEd Tanous             std::move(getLogEntriesCallback),
6861da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
6871da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
6881da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
6894ed77cd5SEd Tanous             std::array<const char *, 1>{cpuLogInterface});
6901da66f75SEd Tanous     }
6911da66f75SEd Tanous };
6921da66f75SEd Tanous 
6931da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog)
6941da66f75SEd Tanous {
6951da66f75SEd Tanous     nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
6961da66f75SEd Tanous     if (metaIt != cpuLog.end())
6971da66f75SEd Tanous     {
6981da66f75SEd Tanous         nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
6991da66f75SEd Tanous         if (tsIt != metaIt->end())
7001da66f75SEd Tanous         {
7011da66f75SEd Tanous             const std::string *logTime = tsIt->get_ptr<const std::string *>();
7021da66f75SEd Tanous             if (logTime != nullptr)
7031da66f75SEd Tanous             {
7041da66f75SEd Tanous                 return *logTime;
7051da66f75SEd Tanous             }
7061da66f75SEd Tanous         }
7071da66f75SEd Tanous     }
7081da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
7091da66f75SEd Tanous 
7101da66f75SEd Tanous     return std::string();
7111da66f75SEd Tanous }
7121da66f75SEd Tanous 
713e1f26343SJason M. Bills class CPULogEntry : public Node
7141da66f75SEd Tanous {
7151da66f75SEd Tanous   public:
716e1f26343SJason M. Bills     CPULogEntry(CrowApp &app) :
7174ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
7181da66f75SEd Tanous              std::string())
7191da66f75SEd Tanous     {
7201da66f75SEd Tanous         entityPrivileges = {
721e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
722e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
723e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
724e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
725e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
726e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
7271da66f75SEd Tanous     }
7281da66f75SEd Tanous 
7291da66f75SEd Tanous   private:
7301da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
7311da66f75SEd Tanous                const std::vector<std::string> &params) override
7321da66f75SEd Tanous     {
733e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
7341da66f75SEd Tanous         if (params.size() != 1)
7351da66f75SEd Tanous         {
736f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
7371da66f75SEd Tanous             return;
7381da66f75SEd Tanous         }
7394ed77cd5SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
740e1f26343SJason M. Bills         auto getStoredLogCallback =
741e1f26343SJason M. Bills             [asyncResp,
7424ed77cd5SEd Tanous              logId](const boost::system::error_code ec,
743e1f26343SJason M. Bills                     const sdbusplus::message::variant<std::string> &resp) {
7441da66f75SEd Tanous                 if (ec)
7451da66f75SEd Tanous                 {
746e1f26343SJason M. Bills                     BMCWEB_LOG_DEBUG << "failed to get log ec: "
747e1f26343SJason M. Bills                                      << ec.message();
748f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
7491da66f75SEd Tanous                     return;
7501da66f75SEd Tanous                 }
751e1f26343SJason M. Bills                 const std::string *log =
752e1f26343SJason M. Bills                     mapbox::getPtr<const std::string>(resp);
7531da66f75SEd Tanous                 if (log == nullptr)
7541da66f75SEd Tanous                 {
755f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
7561da66f75SEd Tanous                     return;
7571da66f75SEd Tanous                 }
7581da66f75SEd Tanous                 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
7591da66f75SEd Tanous                 if (j.is_discarded())
7601da66f75SEd Tanous                 {
761f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
7621da66f75SEd Tanous                     return;
7631da66f75SEd Tanous                 }
7641da66f75SEd Tanous                 std::string t = getLogCreatedTime(j);
765e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {
7661da66f75SEd Tanous                     {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
767e1f26343SJason M. Bills                     {"@odata.context",
768e1f26343SJason M. Bills                      "/redfish/v1/$metadata#LogEntry.LogEntry"},
7691da66f75SEd Tanous                     {"@odata.id",
7704ed77cd5SEd Tanous                      "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
7714ed77cd5SEd Tanous                          std::to_string(logId)},
7721da66f75SEd Tanous                     {"Name", "CPU Debug Log"},
7734ed77cd5SEd Tanous                     {"Id", logId},
7741da66f75SEd Tanous                     {"EntryType", "Oem"},
7751da66f75SEd Tanous                     {"OemRecordFormat", "Intel CPU Log"},
7761da66f75SEd Tanous                     {"Oem", {{"Intel", std::move(j)}}},
7771da66f75SEd Tanous                     {"Created", std::move(t)}};
7781da66f75SEd Tanous             };
7791da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
7804ed77cd5SEd Tanous             std::move(getStoredLogCallback), cpuLogObject,
7814ed77cd5SEd Tanous             cpuLogPath + std::string("/") + std::to_string(logId),
7824ed77cd5SEd Tanous             "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
7831da66f75SEd Tanous     }
7841da66f75SEd Tanous };
7851da66f75SEd Tanous 
786e1f26343SJason M. Bills class ImmediateCPULog : public Node
7871da66f75SEd Tanous {
7881da66f75SEd Tanous   public:
789e1f26343SJason M. Bills     ImmediateCPULog(CrowApp &app) :
7904ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
791e1f26343SJason M. Bills                   "CpuLog.Immediate/")
7921da66f75SEd Tanous     {
7931da66f75SEd Tanous         entityPrivileges = {
794e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
795e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
796e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
797e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
798e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
799e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
8001da66f75SEd Tanous     }
8011da66f75SEd Tanous 
8021da66f75SEd Tanous   private:
8031da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
8041da66f75SEd Tanous                 const std::vector<std::string> &params) override
8051da66f75SEd Tanous     {
806e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
8071da66f75SEd Tanous         static std::unique_ptr<sdbusplus::bus::match::match>
8081da66f75SEd Tanous             immediateLogMatcher;
8091da66f75SEd Tanous 
8101da66f75SEd Tanous         // Only allow one Immediate Log request at a time
8111da66f75SEd Tanous         if (immediateLogMatcher != nullptr)
8121da66f75SEd Tanous         {
813e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
814f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
8151da66f75SEd Tanous             return;
8161da66f75SEd Tanous         }
8171da66f75SEd Tanous         // Make this static so it survives outside this method
8181da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
8191da66f75SEd Tanous 
8201da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
821e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
8221da66f75SEd Tanous             immediateLogMatcher = nullptr;
8231da66f75SEd Tanous             if (ec)
8241da66f75SEd Tanous             {
8251da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
8261da66f75SEd Tanous                 // completion.
8271da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
8281da66f75SEd Tanous                 {
8291da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
8301da66f75SEd Tanous                 }
8311da66f75SEd Tanous                 return;
8321da66f75SEd Tanous             }
8331da66f75SEd Tanous             BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
8341da66f75SEd Tanous 
835f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
8361da66f75SEd Tanous         });
8371da66f75SEd Tanous 
838e1f26343SJason M. Bills         auto immediateLogMatcherCallback = [asyncResp](
8391da66f75SEd Tanous                                                sdbusplus::message::message &m) {
8401da66f75SEd Tanous             BMCWEB_LOG_DEBUG << "Immediate log available match fired";
8411da66f75SEd Tanous             boost::system::error_code ec;
8421da66f75SEd Tanous             timeout.cancel(ec);
8431da66f75SEd Tanous             if (ec)
8441da66f75SEd Tanous             {
8451da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
8461da66f75SEd Tanous             }
8474ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
8481da66f75SEd Tanous             boost::container::flat_map<
8491da66f75SEd Tanous                 std::string,
8501da66f75SEd Tanous                 boost::container::flat_map<
8511da66f75SEd Tanous                     std::string, sdbusplus::message::variant<std::string>>>
8524ed77cd5SEd Tanous                 interfacesAdded;
8534ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
8541da66f75SEd Tanous             const std::string *log = mapbox::getPtr<const std::string>(
8554ed77cd5SEd Tanous                 interfacesAdded[cpuLogInterface]["Log"]);
8561da66f75SEd Tanous             if (log == nullptr)
8571da66f75SEd Tanous             {
858f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
8591da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
8601da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
8611da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
8621da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
8631da66f75SEd Tanous                 // be the last thing done.
8641da66f75SEd Tanous                 immediateLogMatcher = nullptr;
8651da66f75SEd Tanous                 return;
8661da66f75SEd Tanous             }
8671da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
8681da66f75SEd Tanous             if (j.is_discarded())
8691da66f75SEd Tanous             {
870f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
8711da66f75SEd Tanous                 // Careful with immediateLogMatcher.  It is a unique_ptr to the
8721da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
8731da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
8741da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
8751da66f75SEd Tanous                 // be the last thing done.
8761da66f75SEd Tanous                 immediateLogMatcher = nullptr;
8771da66f75SEd Tanous                 return;
8781da66f75SEd Tanous             }
8791da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
880e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
8811da66f75SEd Tanous                 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
8821da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
8831da66f75SEd Tanous                 {"Name", "CPU Debug Log"},
8841da66f75SEd Tanous                 {"EntryType", "Oem"},
8851da66f75SEd Tanous                 {"OemRecordFormat", "Intel CPU Log"},
8861da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
8871da66f75SEd Tanous                 {"Created", std::move(t)}};
8881da66f75SEd Tanous             // Careful with immediateLogMatcher.  It is a unique_ptr to the
8891da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
8901da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
8911da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
8921da66f75SEd Tanous             // thing done.
8931da66f75SEd Tanous             immediateLogMatcher = nullptr;
8941da66f75SEd Tanous         };
8951da66f75SEd Tanous         immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
8961da66f75SEd Tanous             *crow::connections::systemBus,
8971da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
8984ed77cd5SEd Tanous                 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
8991da66f75SEd Tanous             std::move(immediateLogMatcherCallback));
9001da66f75SEd Tanous 
9011da66f75SEd Tanous         auto generateImmediateLogCallback =
902e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
9031da66f75SEd Tanous                         const std::string &resp) {
9041da66f75SEd Tanous                 if (ec)
9051da66f75SEd Tanous                 {
9061da66f75SEd Tanous                     if (ec.value() ==
9071da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
9081da66f75SEd Tanous                     {
909f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
9101da66f75SEd Tanous                     }
9111da66f75SEd Tanous                     else
9121da66f75SEd Tanous                     {
913f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
9141da66f75SEd Tanous                     }
9151da66f75SEd Tanous                     boost::system::error_code timeoutec;
9161da66f75SEd Tanous                     timeout.cancel(timeoutec);
9171da66f75SEd Tanous                     if (timeoutec)
9181da66f75SEd Tanous                     {
9191da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
9201da66f75SEd Tanous                                          << timeoutec;
9211da66f75SEd Tanous                     }
9221da66f75SEd Tanous                     immediateLogMatcher = nullptr;
9231da66f75SEd Tanous                     return;
9241da66f75SEd Tanous                 }
9251da66f75SEd Tanous             };
9261da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
9274ed77cd5SEd Tanous             std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
9284ed77cd5SEd Tanous             cpuLogImmediateInterface, "GenerateImmediateLog");
9291da66f75SEd Tanous     }
9301da66f75SEd Tanous };
9311da66f75SEd Tanous 
932e1f26343SJason M. Bills class SendRawPECI : public Node
9331da66f75SEd Tanous {
9341da66f75SEd Tanous   public:
935e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
9364ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
937e1f26343SJason M. Bills                   "CpuLog.SendRawPeci/")
9381da66f75SEd Tanous     {
9391da66f75SEd Tanous         entityPrivileges = {
9401da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
9411da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
9421da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
9431da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
9441da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
9451da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
9461da66f75SEd Tanous     }
9471da66f75SEd Tanous 
9481da66f75SEd Tanous   private:
9491da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
9501da66f75SEd Tanous                 const std::vector<std::string> &params) override
9511da66f75SEd Tanous     {
952e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9531da66f75SEd Tanous         // Get the Raw PECI command from the request
954e1f26343SJason M. Bills         nlohmann::json rawPECICmd;
955e1f26343SJason M. Bills         if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
9561da66f75SEd Tanous         {
9571da66f75SEd Tanous             return;
9581da66f75SEd Tanous         }
9591da66f75SEd Tanous         // Get the Client Address from the request
960e1f26343SJason M. Bills         nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
961e1f26343SJason M. Bills         if (caIt == rawPECICmd.end())
9621da66f75SEd Tanous         {
963a08b46ccSJason M. Bills             messages::propertyMissing(asyncResp->res, "ClientAddress");
9641da66f75SEd Tanous             return;
9651da66f75SEd Tanous         }
9661da66f75SEd Tanous         const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
9671da66f75SEd Tanous         if (ca == nullptr)
9681da66f75SEd Tanous         {
969f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, caIt->dump(),
970a08b46ccSJason M. Bills                                              "ClientAddress");
9711da66f75SEd Tanous             return;
9721da66f75SEd Tanous         }
9731da66f75SEd Tanous         // Get the Read Length from the request
9741da66f75SEd Tanous         const uint8_t clientAddress = static_cast<uint8_t>(*ca);
975e1f26343SJason M. Bills         nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
976e1f26343SJason M. Bills         if (rlIt == rawPECICmd.end())
9771da66f75SEd Tanous         {
978a08b46ccSJason M. Bills             messages::propertyMissing(asyncResp->res, "ReadLength");
9791da66f75SEd Tanous             return;
9801da66f75SEd Tanous         }
9811da66f75SEd Tanous         const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
9821da66f75SEd Tanous         if (rl == nullptr)
9831da66f75SEd Tanous         {
984f12894f8SJason M. Bills             messages::propertyValueTypeError(asyncResp->res, rlIt->dump(),
985a08b46ccSJason M. Bills                                              "ReadLength");
9861da66f75SEd Tanous             return;
9871da66f75SEd Tanous         }
9881da66f75SEd Tanous         // Get the PECI Command from the request
9891da66f75SEd Tanous         const uint32_t readLength = static_cast<uint32_t>(*rl);
990e1f26343SJason M. Bills         nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
991e1f26343SJason M. Bills         if (pcIt == rawPECICmd.end())
9921da66f75SEd Tanous         {
993a08b46ccSJason M. Bills             messages::propertyMissing(asyncResp->res, "PECICommand");
9941da66f75SEd Tanous             return;
9951da66f75SEd Tanous         }
9961da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
9971da66f75SEd Tanous         for (auto pc : *pcIt)
9981da66f75SEd Tanous         {
9991da66f75SEd Tanous             const uint64_t *val = pc.get_ptr<const uint64_t *>();
10001da66f75SEd Tanous             if (val == nullptr)
10011da66f75SEd Tanous             {
10021da66f75SEd Tanous                 messages::propertyValueTypeError(
1003f12894f8SJason M. Bills                     asyncResp->res, pc.dump(),
1004a08b46ccSJason M. Bills                     "PECICommand/" + std::to_string(peciCommand.size()));
10051da66f75SEd Tanous                 return;
10061da66f75SEd Tanous             }
10071da66f75SEd Tanous             peciCommand.push_back(static_cast<uint8_t>(*val));
10081da66f75SEd Tanous         }
10091da66f75SEd Tanous         // Callback to return the Raw PECI response
1010e1f26343SJason M. Bills         auto sendRawPECICallback =
1011e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
10121da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
10131da66f75SEd Tanous                 if (ec)
10141da66f75SEd Tanous                 {
10151da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
10161da66f75SEd Tanous                                      << ec.message();
1017f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
10181da66f75SEd Tanous                     return;
10191da66f75SEd Tanous                 }
1020e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
10211da66f75SEd Tanous                                             {"PECIResponse", resp}};
10221da66f75SEd Tanous             };
10231da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
10241da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1025e1f26343SJason M. Bills             std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
1026e1f26343SJason M. Bills             cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
10274ed77cd5SEd Tanous             peciCommand);
10281da66f75SEd Tanous     }
10291da66f75SEd Tanous };
10301da66f75SEd Tanous 
10311da66f75SEd Tanous } // namespace redfish
1032