blob: 66a852e906f623ddd97c5a9c02815c35e4f4d95f [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
19
Jason M. Billse1f26342018-07-18 12:12:00 -070020#include <systemd/sd-journal.h>
21
Ed Tanous1da66f72018-07-27 16:13:37 -070022#include <boost/container/flat_map.hpp>
Jason M. Billse1f26342018-07-18 12:12:00 -070023#include <boost/utility/string_view.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070024#include <experimental/filesystem>
25
26namespace redfish
27{
28
Ed Tanous4ed77cd2018-10-15 08:08:07 -070029constexpr char const *cpuLogObject = "com.intel.CpuDebugLog";
30constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog";
31constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate";
32constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
33constexpr char const *cpuLogImmediateInterface =
Ed Tanous1da66f72018-07-27 16:13:37 -070034 "com.intel.CpuDebugLog.Immediate";
Jason M. Billse1f26342018-07-18 12:12:00 -070035constexpr char const *cpuLogRawPECIInterface =
Ed Tanous1da66f72018-07-27 16:13:37 -070036 "com.intel.CpuDebugLog.SendRawPeci";
37
38namespace fs = std::experimental::filesystem;
39
Jason M. Bills16428a12018-11-02 12:42:29 -070040static int getJournalMetadata(sd_journal *journal,
41 const boost::string_view &field,
42 boost::string_view &contents)
43{
44 const char *data = nullptr;
45 size_t length = 0;
46 int ret = 0;
47 // Get the metadata from the requested field of the journal entry
48 ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
49 &length);
50 if (ret < 0)
51 {
52 return ret;
53 }
54 contents = boost::string_view(data, length);
55 // Only use the content after the "=" character.
56 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
57 return ret;
58}
59
60static int getJournalMetadata(sd_journal *journal,
61 const boost::string_view &field, const int &base,
62 int &contents)
63{
64 int ret = 0;
65 boost::string_view metadata;
66 // Get the metadata from the requested field of the journal entry
67 ret = getJournalMetadata(journal, field, metadata);
68 if (ret < 0)
69 {
70 return ret;
71 }
72 contents = strtol(metadata.data(), nullptr, base);
73 return ret;
74}
75
76static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
77{
78 int ret = 0;
79 uint64_t timestamp = 0;
80 ret = sd_journal_get_realtime_usec(journal, &timestamp);
81 if (ret < 0)
82 {
83 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
84 << strerror(-ret);
85 return false;
86 }
87 time_t t =
88 static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
89 struct tm *loctime = localtime(&t);
90 char entryTime[64] = {};
91 if (NULL != loctime)
92 {
93 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
94 }
95 // Insert the ':' into the timezone
96 boost::string_view t1(entryTime);
97 boost::string_view t2(entryTime);
98 if (t1.size() > 2 && t2.size() > 2)
99 {
100 t1.remove_suffix(2);
101 t2.remove_prefix(t2.size() - 2);
102 }
103 entryTimestamp = t1.to_string() + ":" + t2.to_string();
104 return true;
105}
106
107static bool getSkipParam(crow::Response &res, const crow::Request &req,
108 long &skip)
109{
110 char *skipParam = req.urlParams.get("$skip");
111 if (skipParam != nullptr)
112 {
113 char *ptr = nullptr;
114 skip = std::strtol(skipParam, &ptr, 10);
115 if (*skipParam == '\0' || *ptr != '\0')
116 {
117
118 messages::queryParameterValueTypeError(res, std::string(skipParam),
119 "$skip");
120 return false;
121 }
122 if (skip < 0)
123 {
124
125 messages::queryParameterOutOfRange(res, std::to_string(skip),
126 "$skip", "greater than 0");
127 return false;
128 }
129 }
130 return true;
131}
132
133static constexpr const long maxEntriesPerPage = 1000;
134static bool getTopParam(crow::Response &res, const crow::Request &req,
135 long &top)
136{
137 char *topParam = req.urlParams.get("$top");
138 if (topParam != nullptr)
139 {
140 char *ptr = nullptr;
141 top = std::strtol(topParam, &ptr, 10);
142 if (*topParam == '\0' || *ptr != '\0')
143 {
144 messages::queryParameterValueTypeError(res, std::string(topParam),
145 "$top");
146 return false;
147 }
148 if (top < 1 || top > maxEntriesPerPage)
149 {
150
151 messages::queryParameterOutOfRange(
152 res, std::to_string(top), "$top",
153 "1-" + std::to_string(maxEntriesPerPage));
154 return false;
155 }
156 }
157 return true;
158}
159
160static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
161{
162 int ret = 0;
163 static uint64_t prevTs = 0;
164 static int index = 0;
165 // Get the entry timestamp
166 uint64_t curTs = 0;
167 ret = sd_journal_get_realtime_usec(journal, &curTs);
168 if (ret < 0)
169 {
170 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
171 << strerror(-ret);
172 return false;
173 }
174 // If the timestamp isn't unique, increment the index
175 if (curTs == prevTs)
176 {
177 index++;
178 }
179 else
180 {
181 // Otherwise, reset it
182 index = 0;
183 }
184 // Save the timestamp
185 prevTs = curTs;
186
187 entryID = std::to_string(curTs);
188 if (index > 0)
189 {
190 entryID += "_" + std::to_string(index);
191 }
192 return true;
193}
194
195static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
196 uint64_t &timestamp, uint16_t &index)
197{
198 if (entryID.empty())
199 {
200 return false;
201 }
202 // Convert the unique ID back to a timestamp to find the entry
203 boost::string_view tsStr(entryID);
204
205 auto underscorePos = tsStr.find("_");
206 if (underscorePos != tsStr.npos)
207 {
208 // Timestamp has an index
209 tsStr.remove_suffix(tsStr.size() - underscorePos);
210 boost::string_view indexStr(entryID);
211 indexStr.remove_prefix(underscorePos + 1);
212 std::size_t pos;
213 try
214 {
215 index = std::stoul(indexStr.to_string(), &pos);
216 }
217 catch (std::invalid_argument)
218 {
219 messages::resourceMissingAtURI(res, entryID);
220 return false;
221 }
222 catch (std::out_of_range)
223 {
224 messages::resourceMissingAtURI(res, entryID);
225 return false;
226 }
227 if (pos != indexStr.size())
228 {
229 messages::resourceMissingAtURI(res, entryID);
230 return false;
231 }
232 }
233 // Timestamp has no index
234 std::size_t pos;
235 try
236 {
237 timestamp = std::stoull(tsStr.to_string(), &pos);
238 }
239 catch (std::invalid_argument)
240 {
241 messages::resourceMissingAtURI(res, entryID);
242 return false;
243 }
244 catch (std::out_of_range)
245 {
246 messages::resourceMissingAtURI(res, entryID);
247 return false;
248 }
249 if (pos != tsStr.size())
250 {
251 messages::resourceMissingAtURI(res, entryID);
252 return false;
253 }
254 return true;
255}
256
Ed Tanous1da66f72018-07-27 16:13:37 -0700257class LogServiceCollection : public Node
258{
259 public:
260 template <typename CrowApp>
261 LogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700262 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700263 {
264 // Collections use static ID for SubRoute to add to its parent, but only
265 // load dynamic data so the duplicate static members don't get displayed
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700266 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices";
Ed Tanous1da66f72018-07-27 16:13:37 -0700267 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700268 {boost::beast::http::verb::get, {{"Login"}}},
269 {boost::beast::http::verb::head, {{"Login"}}},
270 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
271 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
272 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
273 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700274 }
275
276 private:
277 /**
278 * Functions triggers appropriate requests on DBus
279 */
280 void doGet(crow::Response &res, const crow::Request &req,
281 const std::vector<std::string> &params) override
282 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700283 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700284 // Collections don't include the static data added by SubRoute because
285 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -0700286 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -0700287 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -0700288 asyncResp->res.jsonValue["@odata.context"] =
Ed Tanous1da66f72018-07-27 16:13:37 -0700289 "/redfish/v1/"
290 "$metadata#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -0700291 asyncResp->res.jsonValue["@odata.id"] =
292 "/redfish/v1/Managers/bmc/LogServices";
293 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
294 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -0700295 "Collection of LogServices for this Manager";
Jason M. Billse1f26342018-07-18 12:12:00 -0700296 nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"];
Ed Tanous1da66f72018-07-27 16:13:37 -0700297 logserviceArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -0700298 logserviceArray.push_back(
299 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}});
Ed Tanous1da66f72018-07-27 16:13:37 -0700300#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
301 logserviceArray.push_back(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700302 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
Ed Tanous1da66f72018-07-27 16:13:37 -0700303#endif
Jason M. Billse1f26342018-07-18 12:12:00 -0700304 asyncResp->res.jsonValue["Members@odata.count"] =
305 logserviceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -0700306 }
307};
308
Jason M. Billse1f26342018-07-18 12:12:00 -0700309class BMCLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700310{
311 public:
312 template <typename CrowApp>
Jason M. Billse1f26342018-07-18 12:12:00 -0700313 BMCLogService(CrowApp &app) :
314 Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/")
315 {
316 // Set the id for SubRoute
317 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog";
318 entityPrivileges = {
319 {boost::beast::http::verb::get, {{"Login"}}},
320 {boost::beast::http::verb::head, {{"Login"}}},
321 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
322 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
323 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
324 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
325 }
326
327 private:
328 void doGet(crow::Response &res, const crow::Request &req,
329 const std::vector<std::string> &params) override
330 {
331 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
332 // Copy over the static data to include the entries added by SubRoute
333 asyncResp->res.jsonValue = Node::json;
334 asyncResp->res.jsonValue["@odata.type"] =
335 "#LogService.v1_1_0.LogService";
336 asyncResp->res.jsonValue["@odata.context"] =
337 "/redfish/v1/$metadata#LogService.LogService";
338 asyncResp->res.jsonValue["Name"] = "Open BMC Log Service";
339 asyncResp->res.jsonValue["Description"] = "BMC Log Service";
340 asyncResp->res.jsonValue["Id"] = "BMC Log";
341 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
342 }
343};
344
345static int fillBMCLogEntryJson(const std::string &bmcLogEntryID,
346 sd_journal *journal,
347 nlohmann::json &bmcLogEntryJson)
348{
349 // Get the Log Entry contents
350 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -0700351
Jason M. Bills16428a12018-11-02 12:42:29 -0700352 boost::string_view msg;
353 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -0700354 if (ret < 0)
355 {
356 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
357 return 1;
358 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700359
360 // Get the severity from the PRIORITY field
Jason M. Billse1f26342018-07-18 12:12:00 -0700361 int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -0700362 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -0700363 if (ret < 0)
364 {
365 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
366 return 1;
367 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700368
369 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -0700370 std::string entryTimeStr;
371 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -0700372 {
Jason M. Bills16428a12018-11-02 12:42:29 -0700373 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -0700374 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700375
376 // Fill in the log entry with the gathered data
377 bmcLogEntryJson = {
378 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
379 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
380 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" +
381 bmcLogEntryID},
382 {"Name", "BMC Journal Entry"},
383 {"Id", bmcLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -0700384 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -0700385 {"EntryType", "Oem"},
386 {"Severity",
387 severity <= 2 ? "Critical"
388 : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
389 {"OemRecordFormat", "Intel BMC Journal Entry"},
390 {"Created", std::move(entryTimeStr)}};
391 return 0;
392}
393
394class BMCLogEntryCollection : public Node
395{
396 public:
397 template <typename CrowApp>
398 BMCLogEntryCollection(CrowApp &app) :
399 Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/")
400 {
401 // Collections use static ID for SubRoute to add to its parent, but only
402 // load dynamic data so the duplicate static members don't get displayed
403 Node::json["@odata.id"] =
404 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
405 entityPrivileges = {
406 {boost::beast::http::verb::get, {{"Login"}}},
407 {boost::beast::http::verb::head, {{"Login"}}},
408 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
409 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
410 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
411 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
412 }
413
414 private:
415 void doGet(crow::Response &res, const crow::Request &req,
416 const std::vector<std::string> &params) override
417 {
418 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700419 static constexpr const long maxEntriesPerPage = 1000;
420 long skip = 0;
421 long top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -0700422 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700423 {
Jason M. Bills16428a12018-11-02 12:42:29 -0700424 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700425 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700426 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700427 {
Jason M. Bills16428a12018-11-02 12:42:29 -0700428 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700429 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700430 // Collections don't include the static data added by SubRoute because
431 // it has a duplicate entry for members
432 asyncResp->res.jsonValue["@odata.type"] =
433 "#LogEntryCollection.LogEntryCollection";
434 asyncResp->res.jsonValue["@odata.context"] =
435 "/redfish/v1/"
436 "$metadata#LogEntryCollection.LogEntryCollection";
437 asyncResp->res.jsonValue["@odata.id"] =
438 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
439 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
440 asyncResp->res.jsonValue["Description"] =
441 "Collection of BMC Journal Entries";
442 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
443 logEntryArray = nlohmann::json::array();
444
445 // Go through the journal and use the timestamp to create a unique ID
446 // for each entry
447 sd_journal *journalTmp = nullptr;
448 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
449 if (ret < 0)
450 {
451 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700452 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700453 return;
454 }
455 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
456 journalTmp, sd_journal_close);
457 journalTmp = nullptr;
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700458 uint64_t entryCount = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -0700459 SD_JOURNAL_FOREACH(journal.get())
460 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700461 entryCount++;
462 // Handle paging using skip (number of entries to skip from the
463 // start) and top (number of entries to display)
464 if (entryCount <= skip || entryCount > skip + top)
465 {
466 continue;
467 }
468
Jason M. Bills16428a12018-11-02 12:42:29 -0700469 std::string idStr;
470 if (!getUniqueEntryID(journal.get(), idStr))
Jason M. Billse1f26342018-07-18 12:12:00 -0700471 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700472 continue;
473 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700474
Jason M. Billse1f26342018-07-18 12:12:00 -0700475 logEntryArray.push_back({});
476 nlohmann::json &bmcLogEntry = logEntryArray.back();
477 if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
478 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700479 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700480 return;
481 }
482 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700483 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
484 if (skip + top < entryCount)
485 {
486 asyncResp->res.jsonValue["Members@odata.nextLink"] =
487 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
488 std::to_string(skip + top);
489 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700490 }
491};
492
493class BMCLogEntry : public Node
494{
495 public:
496 BMCLogEntry(CrowApp &app) :
497 Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
498 std::string())
499 {
500 entityPrivileges = {
501 {boost::beast::http::verb::get, {{"Login"}}},
502 {boost::beast::http::verb::head, {{"Login"}}},
503 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
504 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
505 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
506 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
507 }
508
509 private:
510 void doGet(crow::Response &res, const crow::Request &req,
511 const std::vector<std::string> &params) override
512 {
513 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
514 if (params.size() != 1)
515 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700516 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700517 return;
518 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700519 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -0700520 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -0700521 uint64_t ts = 0;
522 uint16_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -0700523 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -0700524 {
Jason M. Bills16428a12018-11-02 12:42:29 -0700525 return;
Jason M. Billse1f26342018-07-18 12:12:00 -0700526 }
527
528 sd_journal *journalTmp = nullptr;
529 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
530 if (ret < 0)
531 {
532 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700533 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700534 return;
535 }
536 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
537 journalTmp, sd_journal_close);
538 journalTmp = nullptr;
539 // Go to the timestamp in the log and move to the entry at the index
540 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
541 for (int i = 0; i <= index; i++)
542 {
543 sd_journal_next(journal.get());
544 }
545 if (fillBMCLogEntryJson(params[0], journal.get(),
546 asyncResp->res.jsonValue) != 0)
547 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700548 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700549 return;
550 }
551 }
552};
553
554class CPULogService : public Node
555{
556 public:
557 template <typename CrowApp>
558 CPULogService(CrowApp &app) :
559 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700560 {
561 // Set the id for SubRoute
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700562 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
Ed Tanous1da66f72018-07-27 16:13:37 -0700563 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700564 {boost::beast::http::verb::get, {{"Login"}}},
565 {boost::beast::http::verb::head, {{"Login"}}},
566 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
567 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
568 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
569 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700570 }
571
572 private:
573 /**
574 * Functions triggers appropriate requests on DBus
575 */
576 void doGet(crow::Response &res, const crow::Request &req,
577 const std::vector<std::string> &params) override
578 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700579 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700580 // Copy over the static data to include the entries added by SubRoute
Jason M. Billse1f26342018-07-18 12:12:00 -0700581 asyncResp->res.jsonValue = Node::json;
582 asyncResp->res.jsonValue["@odata.type"] =
583 "#LogService.v1_1_0.LogService";
584 asyncResp->res.jsonValue["@odata.context"] =
585 "/redfish/v1/"
586 "$metadata#LogService.LogService";
587 asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
588 asyncResp->res.jsonValue["Description"] = "CPU Log Service";
589 asyncResp->res.jsonValue["Id"] = "CPU Log";
590 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
591 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
592 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -0700593 {"Oem",
594 {{"#CpuLog.Immediate",
595 {{"target",
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700596 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Ed Tanous1da66f72018-07-27 16:13:37 -0700597 "CpuLog.Immediate"}}}}}};
598
599#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -0700600 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Ed Tanous1da66f72018-07-27 16:13:37 -0700601 {"#CpuLog.SendRawPeci",
602 {{"target",
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700603 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Ed Tanous1da66f72018-07-27 16:13:37 -0700604 "CpuLog.SendRawPeci"}}});
605#endif
Ed Tanous1da66f72018-07-27 16:13:37 -0700606 }
607};
608
Jason M. Billse1f26342018-07-18 12:12:00 -0700609class CPULogEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700610{
611 public:
612 template <typename CrowApp>
Jason M. Billse1f26342018-07-18 12:12:00 -0700613 CPULogEntryCollection(CrowApp &app) :
614 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700615 {
616 // Collections use static ID for SubRoute to add to its parent, but only
617 // load dynamic data so the duplicate static members don't get displayed
618 Node::json["@odata.id"] =
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700619 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
Ed Tanous1da66f72018-07-27 16:13:37 -0700620 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700621 {boost::beast::http::verb::get, {{"Login"}}},
622 {boost::beast::http::verb::head, {{"Login"}}},
623 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
624 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
625 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
626 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700627 }
628
629 private:
630 /**
631 * Functions triggers appropriate requests on DBus
632 */
633 void doGet(crow::Response &res, const crow::Request &req,
634 const std::vector<std::string> &params) override
635 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700636 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700637 // Collections don't include the static data added by SubRoute because
638 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -0700639 auto getLogEntriesCallback = [asyncResp](
640 const boost::system::error_code ec,
641 const std::vector<std::string> &resp) {
642 if (ec)
643 {
644 if (ec.value() !=
645 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -0700646 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700647 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
648 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700649 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700650 return;
Ed Tanous1da66f72018-07-27 16:13:37 -0700651 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700652 }
653 asyncResp->res.jsonValue["@odata.type"] =
654 "#LogEntryCollection.LogEntryCollection";
655 asyncResp->res.jsonValue["@odata.context"] =
656 "/redfish/v1/"
657 "$metadata#LogEntryCollection.LogEntryCollection";
658 asyncResp->res.jsonValue["@odata.id"] =
659 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
660 asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
661 asyncResp->res.jsonValue["Description"] =
662 "Collection of CPU Log Entries";
663 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
664 logEntryArray = nlohmann::json::array();
665 for (const std::string &objpath : resp)
666 {
667 // Don't list the immediate log
668 if (objpath.compare(cpuLogImmediatePath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -0700669 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700670 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -0700671 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700672 std::size_t lastPos = objpath.rfind("/");
673 if (lastPos != std::string::npos)
674 {
675 logEntryArray.push_back(
676 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
677 "CpuLog/Entries/" +
678 objpath.substr(lastPos + 1)}});
679 }
680 }
681 asyncResp->res.jsonValue["Members@odata.count"] =
682 logEntryArray.size();
683 };
Ed Tanous1da66f72018-07-27 16:13:37 -0700684 crow::connections::systemBus->async_method_call(
685 std::move(getLogEntriesCallback),
686 "xyz.openbmc_project.ObjectMapper",
687 "/xyz/openbmc_project/object_mapper",
688 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700689 std::array<const char *, 1>{cpuLogInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -0700690 }
691};
692
693std::string getLogCreatedTime(const nlohmann::json &cpuLog)
694{
695 nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
696 if (metaIt != cpuLog.end())
697 {
698 nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
699 if (tsIt != metaIt->end())
700 {
701 const std::string *logTime = tsIt->get_ptr<const std::string *>();
702 if (logTime != nullptr)
703 {
704 return *logTime;
705 }
706 }
707 }
708 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
709
710 return std::string();
711}
712
Jason M. Billse1f26342018-07-18 12:12:00 -0700713class CPULogEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700714{
715 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700716 CPULogEntry(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700717 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -0700718 std::string())
719 {
720 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700721 {boost::beast::http::verb::get, {{"Login"}}},
722 {boost::beast::http::verb::head, {{"Login"}}},
723 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
724 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
725 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
726 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700727 }
728
729 private:
730 void doGet(crow::Response &res, const crow::Request &req,
731 const std::vector<std::string> &params) override
732 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700733 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700734 if (params.size() != 1)
735 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700736 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700737 return;
738 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700739 const uint8_t logId = std::atoi(params[0].c_str());
Jason M. Billse1f26342018-07-18 12:12:00 -0700740 auto getStoredLogCallback =
741 [asyncResp,
742 logId](const boost::system::error_code ec,
743 const sdbusplus::message::variant<std::string> &resp) {
744 if (ec)
745 {
746 BMCWEB_LOG_DEBUG << "failed to get log ec: "
747 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700748 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700749 return;
750 }
751 const std::string *log =
752 mapbox::getPtr<const std::string>(resp);
753 if (log == nullptr)
754 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700755 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700756 return;
757 }
758 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
759 if (j.is_discarded())
760 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700761 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700762 return;
763 }
764 std::string t = getLogCreatedTime(j);
765 asyncResp->res.jsonValue = {
766 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
767 {"@odata.context",
768 "/redfish/v1/$metadata#LogEntry.LogEntry"},
769 {"@odata.id",
770 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
771 std::to_string(logId)},
772 {"Name", "CPU Debug Log"},
773 {"Id", logId},
774 {"EntryType", "Oem"},
775 {"OemRecordFormat", "Intel CPU Log"},
776 {"Oem", {{"Intel", std::move(j)}}},
777 {"Created", std::move(t)}};
778 };
Ed Tanous1da66f72018-07-27 16:13:37 -0700779 crow::connections::systemBus->async_method_call(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700780 std::move(getStoredLogCallback), cpuLogObject,
781 cpuLogPath + std::string("/") + std::to_string(logId),
782 "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -0700783 }
784};
785
Jason M. Billse1f26342018-07-18 12:12:00 -0700786class ImmediateCPULog : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700787{
788 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700789 ImmediateCPULog(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700790 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Jason M. Billse1f26342018-07-18 12:12:00 -0700791 "CpuLog.Immediate/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700792 {
793 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700794 {boost::beast::http::verb::get, {{"Login"}}},
795 {boost::beast::http::verb::head, {{"Login"}}},
796 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
797 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
798 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
799 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700800 }
801
802 private:
803 void doPost(crow::Response &res, const crow::Request &req,
804 const std::vector<std::string> &params) override
805 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700806 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700807 static std::unique_ptr<sdbusplus::bus::match::match>
808 immediateLogMatcher;
809
810 // Only allow one Immediate Log request at a time
811 if (immediateLogMatcher != nullptr)
812 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700813 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -0700814 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -0700815 return;
816 }
817 // Make this static so it survives outside this method
818 static boost::asio::deadline_timer timeout(*req.ioService);
819
820 timeout.expires_from_now(boost::posix_time::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -0700821 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Ed Tanous1da66f72018-07-27 16:13:37 -0700822 immediateLogMatcher = nullptr;
823 if (ec)
824 {
825 // operation_aborted is expected if timer is canceled before
826 // completion.
827 if (ec != boost::asio::error::operation_aborted)
828 {
829 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
830 }
831 return;
832 }
833 BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
834
Jason M. Billsf12894f2018-10-09 12:45:45 -0700835 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700836 });
837
Jason M. Billse1f26342018-07-18 12:12:00 -0700838 auto immediateLogMatcherCallback = [asyncResp](
Ed Tanous1da66f72018-07-27 16:13:37 -0700839 sdbusplus::message::message &m) {
840 BMCWEB_LOG_DEBUG << "Immediate log available match fired";
841 boost::system::error_code ec;
842 timeout.cancel(ec);
843 if (ec)
844 {
845 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
846 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700847 sdbusplus::message::object_path objPath;
Ed Tanous1da66f72018-07-27 16:13:37 -0700848 boost::container::flat_map<
849 std::string,
850 boost::container::flat_map<
851 std::string, sdbusplus::message::variant<std::string>>>
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700852 interfacesAdded;
853 m.read(objPath, interfacesAdded);
Ed Tanous1da66f72018-07-27 16:13:37 -0700854 const std::string *log = mapbox::getPtr<const std::string>(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700855 interfacesAdded[cpuLogInterface]["Log"]);
Ed Tanous1da66f72018-07-27 16:13:37 -0700856 if (log == nullptr)
857 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700858 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700859 // Careful with immediateLogMatcher. It is a unique_ptr to the
860 // match object inside which this lambda is executing. Once it
861 // is set to nullptr, the match object will be destroyed and the
862 // lambda will lose its context, including res, so it needs to
863 // be the last thing done.
864 immediateLogMatcher = nullptr;
865 return;
866 }
867 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
868 if (j.is_discarded())
869 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700870 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700871 // Careful with immediateLogMatcher. It is a unique_ptr to the
872 // match object inside which this lambda is executing. Once it
873 // is set to nullptr, the match object will be destroyed and the
874 // lambda will lose its context, including res, so it needs to
875 // be the last thing done.
876 immediateLogMatcher = nullptr;
877 return;
878 }
879 std::string t = getLogCreatedTime(j);
Jason M. Billse1f26342018-07-18 12:12:00 -0700880 asyncResp->res.jsonValue = {
Ed Tanous1da66f72018-07-27 16:13:37 -0700881 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
882 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
883 {"Name", "CPU Debug Log"},
884 {"EntryType", "Oem"},
885 {"OemRecordFormat", "Intel CPU Log"},
886 {"Oem", {{"Intel", std::move(j)}}},
887 {"Created", std::move(t)}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700888 // Careful with immediateLogMatcher. It is a unique_ptr to the
889 // match object inside which this lambda is executing. Once it is
890 // set to nullptr, the match object will be destroyed and the lambda
891 // will lose its context, including res, so it needs to be the last
892 // thing done.
893 immediateLogMatcher = nullptr;
894 };
895 immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
896 *crow::connections::systemBus,
897 sdbusplus::bus::match::rules::interfacesAdded() +
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700898 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
Ed Tanous1da66f72018-07-27 16:13:37 -0700899 std::move(immediateLogMatcherCallback));
900
901 auto generateImmediateLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -0700902 [asyncResp](const boost::system::error_code ec,
903 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -0700904 if (ec)
905 {
906 if (ec.value() ==
907 boost::system::errc::operation_not_supported)
908 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700909 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700910 }
911 else
912 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700913 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700914 }
Ed Tanous1da66f72018-07-27 16:13:37 -0700915 boost::system::error_code timeoutec;
916 timeout.cancel(timeoutec);
917 if (timeoutec)
918 {
919 BMCWEB_LOG_ERROR << "error canceling timer "
920 << timeoutec;
921 }
922 immediateLogMatcher = nullptr;
923 return;
924 }
925 };
926 crow::connections::systemBus->async_method_call(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700927 std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
928 cpuLogImmediateInterface, "GenerateImmediateLog");
Ed Tanous1da66f72018-07-27 16:13:37 -0700929 }
930};
931
Jason M. Billse1f26342018-07-18 12:12:00 -0700932class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700933{
934 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700935 SendRawPECI(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700936 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Jason M. Billse1f26342018-07-18 12:12:00 -0700937 "CpuLog.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700938 {
939 entityPrivileges = {
940 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
941 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
942 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
943 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
944 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
945 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
946 }
947
948 private:
949 void doPost(crow::Response &res, const crow::Request &req,
950 const std::vector<std::string> &params) override
951 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700952 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700953 // Get the Raw PECI command from the request
Jason M. Billse1f26342018-07-18 12:12:00 -0700954 nlohmann::json rawPECICmd;
955 if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
Ed Tanous1da66f72018-07-27 16:13:37 -0700956 {
957 return;
958 }
959 // Get the Client Address from the request
Jason M. Billse1f26342018-07-18 12:12:00 -0700960 nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
961 if (caIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700962 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800963 messages::propertyMissing(asyncResp->res, "ClientAddress");
Ed Tanous1da66f72018-07-27 16:13:37 -0700964 return;
965 }
966 const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
967 if (ca == nullptr)
968 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700969 messages::propertyValueTypeError(asyncResp->res, caIt->dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800970 "ClientAddress");
Ed Tanous1da66f72018-07-27 16:13:37 -0700971 return;
972 }
973 // Get the Read Length from the request
974 const uint8_t clientAddress = static_cast<uint8_t>(*ca);
Jason M. Billse1f26342018-07-18 12:12:00 -0700975 nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
976 if (rlIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700977 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800978 messages::propertyMissing(asyncResp->res, "ReadLength");
Ed Tanous1da66f72018-07-27 16:13:37 -0700979 return;
980 }
981 const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
982 if (rl == nullptr)
983 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700984 messages::propertyValueTypeError(asyncResp->res, rlIt->dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800985 "ReadLength");
Ed Tanous1da66f72018-07-27 16:13:37 -0700986 return;
987 }
988 // Get the PECI Command from the request
989 const uint32_t readLength = static_cast<uint32_t>(*rl);
Jason M. Billse1f26342018-07-18 12:12:00 -0700990 nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
991 if (pcIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700992 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800993 messages::propertyMissing(asyncResp->res, "PECICommand");
Ed Tanous1da66f72018-07-27 16:13:37 -0700994 return;
995 }
996 std::vector<uint8_t> peciCommand;
997 for (auto pc : *pcIt)
998 {
999 const uint64_t *val = pc.get_ptr<const uint64_t *>();
1000 if (val == nullptr)
1001 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001002 messages::propertyValueTypeError(
1003 asyncResp->res, pc.dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001004 "PECICommand/" + std::to_string(peciCommand.size()));
Ed Tanous1da66f72018-07-27 16:13:37 -07001005 return;
1006 }
1007 peciCommand.push_back(static_cast<uint8_t>(*val));
1008 }
1009 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001010 auto sendRawPECICallback =
1011 [asyncResp](const boost::system::error_code ec,
1012 const std::vector<uint8_t> &resp) {
1013 if (ec)
1014 {
1015 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
1016 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001017 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001018 return;
1019 }
1020 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1021 {"PECIResponse", resp}};
1022 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001023 // Call the SendRawPECI command with the provided data
1024 crow::connections::systemBus->async_method_call(
Jason M. Billse1f26342018-07-18 12:12:00 -07001025 std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
1026 cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001027 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -07001028 }
1029};
1030
1031} // namespace redfish