blob: 6aea6242acc5fba3e7706eaed68da52d94814fe7 [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
James Feistf6150402019-01-08 10:36:20 -080018#include "filesystem.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070019#include "node.hpp"
20
Jason M. Billse1f26342018-07-18 12:12:00 -070021#include <systemd/sd-journal.h>
22
Ed Tanous1da66f72018-07-27 16:13:37 -070023#include <boost/container/flat_map.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070024#include <error_messages.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080025#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070026
27namespace redfish
28{
29
Ed Tanous4ed77cd2018-10-15 08:08:07 -070030constexpr char const *cpuLogObject = "com.intel.CpuDebugLog";
31constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog";
Jason M. Bills48e46392019-02-13 12:58:37 -080032constexpr char const *cpuLogOnDemandPath = "/com/intel/CpuDebugLog/OnDemand";
Ed Tanous4ed77cd2018-10-15 08:08:07 -070033constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
Jason M. Bills48e46392019-02-13 12:58:37 -080034constexpr char const *cpuLogOnDemandInterface =
35 "com.intel.CpuDebugLog.OnDemand";
Jason M. Billse1f26342018-07-18 12:12:00 -070036constexpr char const *cpuLogRawPECIInterface =
Ed Tanous1da66f72018-07-27 16:13:37 -070037 "com.intel.CpuDebugLog.SendRawPeci";
38
James Feistf6150402019-01-08 10:36:20 -080039namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070040
Andrew Geisslercb92c032018-08-17 07:56:14 -070041using GetManagedPropertyType = boost::container::flat_map<
42 std::string,
43 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
44 int32_t, uint32_t, int64_t, uint64_t, double>>;
45
46using GetManagedObjectsType = boost::container::flat_map<
47 sdbusplus::message::object_path,
48 boost::container::flat_map<std::string, GetManagedPropertyType>>;
49
50inline std::string translateSeverityDbusToRedfish(const std::string &s)
51{
52 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
53 {
54 return "Critical";
55 }
56 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
57 {
58 return "Critical";
59 }
60 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
61 {
62 return "OK";
63 }
64 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
65 {
66 return "Critical";
67 }
68 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
69 {
70 return "Critical";
71 }
72 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
73 {
74 return "OK";
75 }
76 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
77 {
78 return "OK";
79 }
80 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
81 {
82 return "Warning";
83 }
84 return "";
85}
86
Jason M. Bills16428a12018-11-02 12:42:29 -070087static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -080088 const std::string_view &field,
89 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -070090{
91 const char *data = nullptr;
92 size_t length = 0;
93 int ret = 0;
94 // Get the metadata from the requested field of the journal entry
Ed Tanousb01bf292019-03-25 19:25:26 +000095 ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
96 &length);
Jason M. Bills16428a12018-11-02 12:42:29 -070097 if (ret < 0)
98 {
99 return ret;
100 }
Ed Tanous39e77502019-03-04 17:35:53 -0800101 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700102 // Only use the content after the "=" character.
103 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
104 return ret;
105}
106
107static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800108 const std::string_view &field, const int &base,
Jason M. Bills16428a12018-11-02 12:42:29 -0700109 int &contents)
110{
111 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800112 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700113 // Get the metadata from the requested field of the journal entry
114 ret = getJournalMetadata(journal, field, metadata);
115 if (ret < 0)
116 {
117 return ret;
118 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000119 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700120 return ret;
121}
122
123static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
124{
125 int ret = 0;
126 uint64_t timestamp = 0;
127 ret = sd_journal_get_realtime_usec(journal, &timestamp);
128 if (ret < 0)
129 {
130 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
131 << strerror(-ret);
132 return false;
133 }
134 time_t t =
135 static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
136 struct tm *loctime = localtime(&t);
137 char entryTime[64] = {};
138 if (NULL != loctime)
139 {
140 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
141 }
142 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800143 std::string_view t1(entryTime);
144 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700145 if (t1.size() > 2 && t2.size() > 2)
146 {
147 t1.remove_suffix(2);
148 t2.remove_prefix(t2.size() - 2);
149 }
Ed Tanous39e77502019-03-04 17:35:53 -0800150 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700151 return true;
152}
153
154static bool getSkipParam(crow::Response &res, const crow::Request &req,
155 long &skip)
156{
157 char *skipParam = req.urlParams.get("$skip");
158 if (skipParam != nullptr)
159 {
160 char *ptr = nullptr;
161 skip = std::strtol(skipParam, &ptr, 10);
162 if (*skipParam == '\0' || *ptr != '\0')
163 {
164
165 messages::queryParameterValueTypeError(res, std::string(skipParam),
166 "$skip");
167 return false;
168 }
169 if (skip < 0)
170 {
171
172 messages::queryParameterOutOfRange(res, std::to_string(skip),
173 "$skip", "greater than 0");
174 return false;
175 }
176 }
177 return true;
178}
179
180static constexpr const long maxEntriesPerPage = 1000;
181static bool getTopParam(crow::Response &res, const crow::Request &req,
182 long &top)
183{
184 char *topParam = req.urlParams.get("$top");
185 if (topParam != nullptr)
186 {
187 char *ptr = nullptr;
188 top = std::strtol(topParam, &ptr, 10);
189 if (*topParam == '\0' || *ptr != '\0')
190 {
191 messages::queryParameterValueTypeError(res, std::string(topParam),
192 "$top");
193 return false;
194 }
195 if (top < 1 || top > maxEntriesPerPage)
196 {
197
198 messages::queryParameterOutOfRange(
199 res, std::to_string(top), "$top",
200 "1-" + std::to_string(maxEntriesPerPage));
201 return false;
202 }
203 }
204 return true;
205}
206
207static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
208{
209 int ret = 0;
210 static uint64_t prevTs = 0;
211 static int index = 0;
212 // Get the entry timestamp
213 uint64_t curTs = 0;
214 ret = sd_journal_get_realtime_usec(journal, &curTs);
215 if (ret < 0)
216 {
217 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
218 << strerror(-ret);
219 return false;
220 }
221 // If the timestamp isn't unique, increment the index
222 if (curTs == prevTs)
223 {
224 index++;
225 }
226 else
227 {
228 // Otherwise, reset it
229 index = 0;
230 }
231 // Save the timestamp
232 prevTs = curTs;
233
234 entryID = std::to_string(curTs);
235 if (index > 0)
236 {
237 entryID += "_" + std::to_string(index);
238 }
239 return true;
240}
241
242static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
243 uint64_t &timestamp, uint16_t &index)
244{
245 if (entryID.empty())
246 {
247 return false;
248 }
249 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800250 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700251
252 auto underscorePos = tsStr.find("_");
253 if (underscorePos != tsStr.npos)
254 {
255 // Timestamp has an index
256 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800257 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700258 indexStr.remove_prefix(underscorePos + 1);
259 std::size_t pos;
260 try
261 {
Ed Tanous39e77502019-03-04 17:35:53 -0800262 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700263 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000264 catch (std::invalid_argument)
Jason M. Bills16428a12018-11-02 12:42:29 -0700265 {
266 messages::resourceMissingAtURI(res, entryID);
267 return false;
268 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000269 catch (std::out_of_range)
Jason M. Bills16428a12018-11-02 12:42:29 -0700270 {
271 messages::resourceMissingAtURI(res, entryID);
272 return false;
273 }
274 if (pos != indexStr.size())
275 {
276 messages::resourceMissingAtURI(res, entryID);
277 return false;
278 }
279 }
280 // Timestamp has no index
281 std::size_t pos;
282 try
283 {
Ed Tanous39e77502019-03-04 17:35:53 -0800284 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700285 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000286 catch (std::invalid_argument)
Jason M. Bills16428a12018-11-02 12:42:29 -0700287 {
288 messages::resourceMissingAtURI(res, entryID);
289 return false;
290 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000291 catch (std::out_of_range)
Jason M. Bills16428a12018-11-02 12:42:29 -0700292 {
293 messages::resourceMissingAtURI(res, entryID);
294 return false;
295 }
296 if (pos != tsStr.size())
297 {
298 messages::resourceMissingAtURI(res, entryID);
299 return false;
300 }
301 return true;
302}
303
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800304class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700305{
306 public:
307 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800308 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800309 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800310 {
311 entityPrivileges = {
312 {boost::beast::http::verb::get, {{"Login"}}},
313 {boost::beast::http::verb::head, {{"Login"}}},
314 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
315 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
316 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
317 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
318 }
319
320 private:
321 /**
322 * Functions triggers appropriate requests on DBus
323 */
324 void doGet(crow::Response &res, const crow::Request &req,
325 const std::vector<std::string> &params) override
326 {
327 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800328 // Collections don't include the static data added by SubRoute because
329 // it has a duplicate entry for members
330 asyncResp->res.jsonValue["@odata.type"] =
331 "#LogServiceCollection.LogServiceCollection";
332 asyncResp->res.jsonValue["@odata.context"] =
333 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
334 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800335 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800336 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
337 asyncResp->res.jsonValue["Description"] =
338 "Collection of LogServices for this Computer System";
339 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
340 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800341 logServiceArray.push_back(
342 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800343#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
344 logServiceArray.push_back(
Andrew Geisslercb92c032018-08-17 07:56:14 -0700345 {{ "@odata.id",
346 "/redfish/v1/Systems/system/LogServices/CpuLog" }});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800347#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800348 asyncResp->res.jsonValue["Members@odata.count"] =
349 logServiceArray.size();
350 }
351};
352
353class EventLogService : public Node
354{
355 public:
356 template <typename CrowApp>
357 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800358 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800359 {
360 entityPrivileges = {
361 {boost::beast::http::verb::get, {{"Login"}}},
362 {boost::beast::http::verb::head, {{"Login"}}},
363 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
364 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
365 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
366 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
367 }
368
369 private:
370 void doGet(crow::Response &res, const crow::Request &req,
371 const std::vector<std::string> &params) override
372 {
373 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
374
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800375 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800376 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800377 asyncResp->res.jsonValue["@odata.type"] =
378 "#LogService.v1_1_0.LogService";
379 asyncResp->res.jsonValue["@odata.context"] =
380 "/redfish/v1/$metadata#LogService.LogService";
381 asyncResp->res.jsonValue["Name"] = "Event Log Service";
382 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
383 asyncResp->res.jsonValue["Id"] = "Event Log";
384 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
385 asyncResp->res.jsonValue["Entries"] = {
386 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800387 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800388 }
389};
390
Ed Tanous029573d2019-02-01 10:57:49 -0800391static int fillEventLogEntryJson(const std::string &bmcLogEntryID,
Ed Tanous39e77502019-03-04 17:35:53 -0800392 const std::string_view &messageID,
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800393 sd_journal *journal,
394 nlohmann::json &bmcLogEntryJson)
395{
396 // Get the Log Entry contents
397 int ret = 0;
398
Ed Tanous39e77502019-03-04 17:35:53 -0800399 std::string_view msg;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800400 ret = getJournalMetadata(journal, "MESSAGE", msg);
401 if (ret < 0)
402 {
403 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
404 return 1;
405 }
406
407 // Get the severity from the PRIORITY field
408 int severity = 8; // Default to an invalid priority
409 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
410 if (ret < 0)
411 {
412 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
413 return 1;
414 }
415
416 // Get the MessageArgs from the journal entry by finding all of the
417 // REDFISH_MESSAGE_ARG_x fields
418 const void *data;
419 size_t length;
420 std::vector<std::string> messageArgs;
421 SD_JOURNAL_FOREACH_DATA(journal, data, length)
422 {
Ed Tanous39e77502019-03-04 17:35:53 -0800423 std::string_view field(static_cast<const char *>(data), length);
424 if (boost::starts_with(field, "REDFISH_MESSAGE_ARG_"))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800425 {
426 // Get the Arg number from the field name
427 field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
428 if (field.empty())
429 {
430 continue;
431 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000432 int argNum = std::strtoul(field.data(), nullptr, 10);
433 if (argNum == 0)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800434 {
435 continue;
436 }
437 // Get the Arg value after the "=" character.
438 field.remove_prefix(std::min(field.find("=") + 1, field.size()));
439 // Make sure we have enough space in messageArgs
440 if (argNum > messageArgs.size())
441 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000442 messageArgs.resize(argNum);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800443 }
Ed Tanous39e77502019-03-04 17:35:53 -0800444 messageArgs[argNum - 1] = std::string(field);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800445 }
446 }
447
448 // Get the Created time from the timestamp
449 std::string entryTimeStr;
450 if (!getEntryTimestamp(journal, entryTimeStr))
451 {
452 return 1;
453 }
454
455 // Fill in the log entry with the gathered data
456 bmcLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700457 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800458 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800459 {"@odata.id",
460 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
461 bmcLogEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800462 {"Name", "System Event Log Entry"},
463 {"Id", bmcLogEntryID},
464 {"Message", msg},
465 {"MessageId", messageID},
466 {"MessageArgs", std::move(messageArgs)},
467 {"EntryType", "Event"},
468 {"Severity",
469 severity <= 2 ? "Critical"
470 : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
471 {"Created", std::move(entryTimeStr)}};
472 return 0;
473}
474
475class EventLogEntryCollection : public Node
476{
477 public:
478 template <typename CrowApp>
479 EventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800480 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800481 {
482 entityPrivileges = {
483 {boost::beast::http::verb::get, {{"Login"}}},
484 {boost::beast::http::verb::head, {{"Login"}}},
485 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
486 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
487 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
488 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
489 }
490
491 private:
492 void doGet(crow::Response &res, const crow::Request &req,
493 const std::vector<std::string> &params) override
494 {
495 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
496 long skip = 0;
497 long top = maxEntriesPerPage; // Show max entries by default
498 if (!getSkipParam(asyncResp->res, req, skip))
499 {
500 return;
501 }
502 if (!getTopParam(asyncResp->res, req, top))
503 {
504 return;
505 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800506 // Collections don't include the static data added by SubRoute because
507 // it has a duplicate entry for members
508 asyncResp->res.jsonValue["@odata.type"] =
509 "#LogEntryCollection.LogEntryCollection";
510 asyncResp->res.jsonValue["@odata.context"] =
511 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
512 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800513 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800514 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
515 asyncResp->res.jsonValue["Description"] =
516 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700517
518#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800519 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
520 logEntryArray = nlohmann::json::array();
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800521 // Go through the journal and create a unique ID for each entry
522 sd_journal *journalTmp = nullptr;
523 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
524 if (ret < 0)
525 {
526 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
527 messages::internalError(asyncResp->res);
528 return;
529 }
530 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
531 journalTmp, sd_journal_close);
532 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +0000533 uint64_t entryCount = 0;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800534 SD_JOURNAL_FOREACH(journal.get())
535 {
536 // Look for only journal entries that contain a REDFISH_MESSAGE_ID
537 // field
Ed Tanous39e77502019-03-04 17:35:53 -0800538 std::string_view messageID;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800539 ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID",
540 messageID);
541 if (ret < 0)
542 {
543 continue;
544 }
545
546 entryCount++;
547 // Handle paging using skip (number of entries to skip from the
548 // start) and top (number of entries to display)
549 if (entryCount <= skip || entryCount > skip + top)
550 {
551 continue;
552 }
553
554 std::string idStr;
555 if (!getUniqueEntryID(journal.get(), idStr))
556 {
557 continue;
558 }
559
560 logEntryArray.push_back({});
561 nlohmann::json &bmcLogEntry = logEntryArray.back();
Ed Tanous029573d2019-02-01 10:57:49 -0800562 if (fillEventLogEntryJson(idStr, messageID, journal.get(),
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800563 bmcLogEntry) != 0)
564 {
565 messages::internalError(asyncResp->res);
566 return;
567 }
568 }
569 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
570 if (skip + top < entryCount)
571 {
572 asyncResp->res.jsonValue["Members@odata.nextLink"] =
573 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
574 std::to_string(skip + top);
575 }
Andrew Geisslercb92c032018-08-17 07:56:14 -0700576#else
577 // DBus implementation of EventLog/Entries
578 // Make call to Logging Service to find all log entry objects
579 crow::connections::systemBus->async_method_call(
580 [asyncResp](const boost::system::error_code ec,
581 GetManagedObjectsType &resp) {
582 if (ec)
583 {
584 // TODO Handle for specific error code
585 BMCWEB_LOG_ERROR
586 << "getLogEntriesIfaceData resp_handler got error "
587 << ec;
588 messages::internalError(asyncResp->res);
589 return;
590 }
591 nlohmann::json &entriesArray =
592 asyncResp->res.jsonValue["Members"];
593 entriesArray = nlohmann::json::array();
594 for (auto &objectPath : resp)
595 {
596 for (auto &interfaceMap : objectPath.second)
597 {
598 if (interfaceMap.first !=
599 "xyz.openbmc_project.Logging.Entry")
600 {
601 BMCWEB_LOG_DEBUG << "Bailing early on "
602 << interfaceMap.first;
603 continue;
604 }
605 entriesArray.push_back({});
606 nlohmann::json &thisEntry = entriesArray.back();
607 uint32_t *id;
608 std::time_t timestamp;
609 std::string *severity, *message;
610 bool *resolved;
611 for (auto &propertyMap : interfaceMap.second)
612 {
613 if (propertyMap.first == "Id")
614 {
615 id = sdbusplus::message::variant_ns::get_if<
616 uint32_t>(&propertyMap.second);
617 if (id == nullptr)
618 {
619 messages::propertyMissing(asyncResp->res,
620 "Id");
621 }
622 }
623 else if (propertyMap.first == "Timestamp")
624 {
625 const uint64_t *millisTimeStamp =
626 std::get_if<uint64_t>(&propertyMap.second);
627 if (millisTimeStamp == nullptr)
628 {
629 messages::propertyMissing(asyncResp->res,
630 "Timestamp");
631 }
632 // Retrieve Created property with format:
633 // yyyy-mm-ddThh:mm:ss
634 std::chrono::milliseconds chronoTimeStamp(
635 *millisTimeStamp);
636 timestamp =
637 std::chrono::duration_cast<
638 std::chrono::seconds>(chronoTimeStamp)
639 .count();
640 }
641 else if (propertyMap.first == "Severity")
642 {
643 severity = std::get_if<std::string>(
644 &propertyMap.second);
645 if (severity == nullptr)
646 {
647 messages::propertyMissing(asyncResp->res,
648 "Severity");
649 }
650 }
651 else if (propertyMap.first == "Message")
652 {
653 message = std::get_if<std::string>(
654 &propertyMap.second);
655 if (message == nullptr)
656 {
657 messages::propertyMissing(asyncResp->res,
658 "Message");
659 }
660 }
661 }
662 thisEntry = {
663 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
664 {"@odata.context", "/redfish/v1/"
665 "$metadata#LogEntry.LogEntry"},
666 {"@odata.id",
667 "/redfish/v1/Systems/system/LogServices/EventLog/"
668 "Entries/" +
669 std::to_string(*id)},
670 {"Name", "System DBus Event Log Entry"},
671 {"Id", std::to_string(*id)},
672 {"Message", *message},
673 {"EntryType", "Event"},
674 {"Severity",
675 translateSeverityDbusToRedfish(*severity)},
676 {"Created", crow::utility::getDateTime(timestamp)}};
677 }
678 }
679 std::sort(entriesArray.begin(), entriesArray.end(),
680 [](const nlohmann::json &left,
681 const nlohmann::json &right) {
682 return (left["Id"] <= right["Id"]);
683 });
684 asyncResp->res.jsonValue["Members@odata.count"] =
685 entriesArray.size();
686 },
687 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
688 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
689#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800690 }
691};
692
693class EventLogEntry : public Node
694{
695 public:
696 EventLogEntry(CrowApp &app) :
697 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -0800698 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
699 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800700 {
701 entityPrivileges = {
702 {boost::beast::http::verb::get, {{"Login"}}},
703 {boost::beast::http::verb::head, {{"Login"}}},
704 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
705 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
706 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
707 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
708 }
709
710 private:
711 void doGet(crow::Response &res, const crow::Request &req,
712 const std::vector<std::string> &params) override
713 {
714 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -0800715 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800716 {
717 messages::internalError(asyncResp->res);
718 return;
719 }
Ed Tanous029573d2019-02-01 10:57:49 -0800720 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -0700721
722#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800723 // Convert the unique ID back to a timestamp to find the entry
724 uint64_t ts = 0;
725 uint16_t index = 0;
726 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
727 {
728 return;
729 }
730
731 sd_journal *journalTmp = nullptr;
732 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
733 if (ret < 0)
734 {
735 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
736 messages::internalError(asyncResp->res);
737 return;
738 }
739 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
740 journalTmp, sd_journal_close);
741 journalTmp = nullptr;
742 // Go to the timestamp in the log and move to the entry at the index
743 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
744 for (int i = 0; i <= index; i++)
745 {
746 sd_journal_next(journal.get());
747 }
748 // Confirm that the entry ID matches what was requested
749 std::string idStr;
750 if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
751 {
752 messages::resourceMissingAtURI(asyncResp->res, entryID);
753 return;
754 }
755
Jason M. Billscd50aa42019-02-12 17:09:02 -0800756 // only use journal entries that contain a REDFISH_MESSAGE_ID field
Ed Tanous39e77502019-03-04 17:35:53 -0800757 std::string_view messageID;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800758 ret =
759 getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
760 if (ret < 0)
761 {
Ed Tanous029573d2019-02-01 10:57:49 -0800762 messages::resourceNotFound(asyncResp->res, "LogEntry", "system");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800763 return;
764 }
765
Ed Tanous029573d2019-02-01 10:57:49 -0800766 if (fillEventLogEntryJson(entryID, messageID, journal.get(),
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800767 asyncResp->res.jsonValue) != 0)
768 {
769 messages::internalError(asyncResp->res);
770 return;
771 }
Andrew Geisslercb92c032018-08-17 07:56:14 -0700772#else
773 // DBus implementation of EventLog/Entries
774 // Make call to Logging Service to find all log entry objects
775 crow::connections::systemBus->async_method_call(
776 [asyncResp, entryID](const boost::system::error_code ec,
777 GetManagedPropertyType &resp) {
778 if (ec)
779 {
780 BMCWEB_LOG_ERROR
781 << "EventLogEntry (DBus) resp_handler got error " << ec;
782 messages::internalError(asyncResp->res);
783 return;
784 }
785 uint32_t *id;
786 std::time_t timestamp;
787 std::string *severity, *message;
788 bool *resolved;
789 for (auto &propertyMap : resp)
790 {
791 if (propertyMap.first == "Id")
792 {
793 id = std::get_if<uint32_t>(&propertyMap.second);
794 if (id == nullptr)
795 {
796 messages::propertyMissing(asyncResp->res, "Id");
797 }
798 }
799 else if (propertyMap.first == "Timestamp")
800 {
801 const uint64_t *millisTimeStamp =
802 std::get_if<uint64_t>(&propertyMap.second);
803 if (millisTimeStamp == nullptr)
804 {
805 messages::propertyMissing(asyncResp->res,
806 "Timestamp");
807 }
808 // Retrieve Created property with format:
809 // yyyy-mm-ddThh:mm:ss
810 std::chrono::milliseconds chronoTimeStamp(
811 *millisTimeStamp);
812 timestamp =
813 std::chrono::duration_cast<std::chrono::seconds>(
814 chronoTimeStamp)
815 .count();
816 }
817 else if (propertyMap.first == "Severity")
818 {
819 severity =
820 std::get_if<std::string>(&propertyMap.second);
821 if (severity == nullptr)
822 {
823 messages::propertyMissing(asyncResp->res,
824 "Severity");
825 }
826 }
827 else if (propertyMap.first == "Message")
828 {
829 message = std::get_if<std::string>(&propertyMap.second);
830 if (message == nullptr)
831 {
832 messages::propertyMissing(asyncResp->res,
833 "Message");
834 }
835 }
836 }
837 asyncResp->res.jsonValue = {
838 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
839 {"@odata.context", "/redfish/v1/"
840 "$metadata#LogEntry.LogEntry"},
841 {"@odata.id",
842 "/redfish/v1/Systems/system/LogServices/EventLog/"
843 "Entries/" +
844 std::to_string(*id)},
845 {"Name", "System DBus Event Log Entry"},
846 {"Id", std::to_string(*id)},
847 {"Message", *message},
848 {"EntryType", "Event"},
849 {"Severity", translateSeverityDbusToRedfish(*severity)},
850 {"Created", crow::utility::getDateTime(timestamp)}};
851 },
852 "xyz.openbmc_project.Logging",
853 "/xyz/openbmc_project/logging/entry/" + entryID,
854 "org.freedesktop.DBus.Properties", "GetAll",
855 "xyz.openbmc_project.Logging.Entry");
856#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800857 }
858};
859
860class BMCLogServiceCollection : public Node
861{
862 public:
863 template <typename CrowApp>
864 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700865 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700866 {
Ed Tanous1da66f72018-07-27 16:13:37 -0700867 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700868 {boost::beast::http::verb::get, {{"Login"}}},
869 {boost::beast::http::verb::head, {{"Login"}}},
870 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
871 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
872 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
873 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700874 }
875
876 private:
877 /**
878 * Functions triggers appropriate requests on DBus
879 */
880 void doGet(crow::Response &res, const crow::Request &req,
881 const std::vector<std::string> &params) override
882 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700883 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700884 // Collections don't include the static data added by SubRoute because
885 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -0700886 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -0700887 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -0700888 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800889 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -0700890 asyncResp->res.jsonValue["@odata.id"] =
891 "/redfish/v1/Managers/bmc/LogServices";
892 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
893 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -0700894 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800895 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
896 logServiceArray = nlohmann::json::array();
897#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
898 logServiceArray.push_back(
Andrew Geisslercb92c032018-08-17 07:56:14 -0700899 {{ "@odata.id",
900 "/redfish/v1/Managers/bmc/LogServices/Journal" }});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800901#endif
Jason M. Billse1f26342018-07-18 12:12:00 -0700902 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800903 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -0700904 }
905};
906
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800907class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700908{
909 public:
910 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800911 BMCJournalLogService(CrowApp &app) :
912 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -0700913 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700914 entityPrivileges = {
915 {boost::beast::http::verb::get, {{"Login"}}},
916 {boost::beast::http::verb::head, {{"Login"}}},
917 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
918 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
919 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
920 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
921 }
922
923 private:
924 void doGet(crow::Response &res, const crow::Request &req,
925 const std::vector<std::string> &params) override
926 {
927 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700928 asyncResp->res.jsonValue["@odata.type"] =
929 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -0800930 asyncResp->res.jsonValue["@odata.id"] =
931 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -0700932 asyncResp->res.jsonValue["@odata.context"] =
933 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800934 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
935 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
936 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -0700937 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -0800938 asyncResp->res.jsonValue["Entries"] = {
939 {"@odata.id",
940 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}};
Jason M. Billse1f26342018-07-18 12:12:00 -0700941 }
942};
943
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800944static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
945 sd_journal *journal,
946 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -0700947{
948 // Get the Log Entry contents
949 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -0700950
Ed Tanous39e77502019-03-04 17:35:53 -0800951 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -0700952 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -0700953 if (ret < 0)
954 {
955 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
956 return 1;
957 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700958
959 // Get the severity from the PRIORITY field
Jason M. Billse1f26342018-07-18 12:12:00 -0700960 int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -0700961 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -0700962 if (ret < 0)
963 {
964 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
965 return 1;
966 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700967
968 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -0700969 std::string entryTimeStr;
970 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -0700971 {
Jason M. Bills16428a12018-11-02 12:42:29 -0700972 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -0700973 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700974
975 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800976 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700977 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse1f26342018-07-18 12:12:00 -0700978 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800979 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
980 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -0700981 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800982 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -0700983 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -0700984 {"EntryType", "Oem"},
985 {"Severity",
986 severity <= 2 ? "Critical"
987 : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
988 {"OemRecordFormat", "Intel BMC Journal Entry"},
989 {"Created", std::move(entryTimeStr)}};
990 return 0;
991}
992
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800993class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -0700994{
995 public:
996 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800997 BMCJournalLogEntryCollection(CrowApp &app) :
998 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -0700999 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001000 entityPrivileges = {
1001 {boost::beast::http::verb::get, {{"Login"}}},
1002 {boost::beast::http::verb::head, {{"Login"}}},
1003 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1004 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1005 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1006 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1007 }
1008
1009 private:
1010 void doGet(crow::Response &res, const crow::Request &req,
1011 const std::vector<std::string> &params) override
1012 {
1013 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001014 static constexpr const long maxEntriesPerPage = 1000;
1015 long skip = 0;
1016 long top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001017 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001018 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001019 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001020 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001021 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001022 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001023 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001024 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001025 // Collections don't include the static data added by SubRoute because
1026 // it has a duplicate entry for members
1027 asyncResp->res.jsonValue["@odata.type"] =
1028 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001029 asyncResp->res.jsonValue["@odata.id"] =
1030 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001031 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001032 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001033 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001034 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001035 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1036 asyncResp->res.jsonValue["Description"] =
1037 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001038 asyncResp->res.jsonValue["@odata.id"] =
1039 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001040 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1041 logEntryArray = nlohmann::json::array();
1042
1043 // Go through the journal and use the timestamp to create a unique ID
1044 // for each entry
1045 sd_journal *journalTmp = nullptr;
1046 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1047 if (ret < 0)
1048 {
1049 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001050 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001051 return;
1052 }
1053 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1054 journalTmp, sd_journal_close);
1055 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001056 uint64_t entryCount = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001057 SD_JOURNAL_FOREACH(journal.get())
1058 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001059 entryCount++;
1060 // Handle paging using skip (number of entries to skip from the
1061 // start) and top (number of entries to display)
1062 if (entryCount <= skip || entryCount > skip + top)
1063 {
1064 continue;
1065 }
1066
Jason M. Bills16428a12018-11-02 12:42:29 -07001067 std::string idStr;
1068 if (!getUniqueEntryID(journal.get(), idStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001069 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001070 continue;
1071 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001072
Jason M. Billse1f26342018-07-18 12:12:00 -07001073 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001074 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1075 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1076 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001077 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001078 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001079 return;
1080 }
1081 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001082 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1083 if (skip + top < entryCount)
1084 {
1085 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001086 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001087 std::to_string(skip + top);
1088 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001089 }
1090};
1091
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001092class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001093{
1094 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001095 BMCJournalLogEntry(CrowApp &app) :
1096 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001097 std::string())
1098 {
1099 entityPrivileges = {
1100 {boost::beast::http::verb::get, {{"Login"}}},
1101 {boost::beast::http::verb::head, {{"Login"}}},
1102 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1103 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1104 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1105 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1106 }
1107
1108 private:
1109 void doGet(crow::Response &res, const crow::Request &req,
1110 const std::vector<std::string> &params) override
1111 {
1112 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1113 if (params.size() != 1)
1114 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001115 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001116 return;
1117 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001118 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001119 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001120 uint64_t ts = 0;
1121 uint16_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001122 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001123 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001124 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001125 }
1126
1127 sd_journal *journalTmp = nullptr;
1128 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1129 if (ret < 0)
1130 {
1131 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001132 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001133 return;
1134 }
1135 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1136 journalTmp, sd_journal_close);
1137 journalTmp = nullptr;
1138 // Go to the timestamp in the log and move to the entry at the index
1139 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1140 for (int i = 0; i <= index; i++)
1141 {
1142 sd_journal_next(journal.get());
1143 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001144 // Confirm that the entry ID matches what was requested
1145 std::string idStr;
1146 if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
1147 {
1148 messages::resourceMissingAtURI(asyncResp->res, entryID);
1149 return;
1150 }
1151
1152 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1153 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001154 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001155 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001156 return;
1157 }
1158 }
1159};
1160
1161class CPULogService : public Node
1162{
1163 public:
1164 template <typename CrowApp>
1165 CPULogService(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001166 Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001167 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001168 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001169 {boost::beast::http::verb::get, {{"Login"}}},
1170 {boost::beast::http::verb::head, {{"Login"}}},
1171 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1172 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1173 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1174 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001175 }
1176
1177 private:
1178 /**
1179 * Functions triggers appropriate requests on DBus
1180 */
1181 void doGet(crow::Response &res, const crow::Request &req,
1182 const std::vector<std::string> &params) override
1183 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001184 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001185 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001186 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsd53dd412019-02-12 17:16:22 -08001187 "/redfish/v1/Systems/system/LogServices/CpuLog";
Jason M. Billse1f26342018-07-18 12:12:00 -07001188 asyncResp->res.jsonValue["@odata.type"] =
1189 "#LogService.v1_1_0.LogService";
1190 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001191 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Billse1f26342018-07-18 12:12:00 -07001192 asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
1193 asyncResp->res.jsonValue["Description"] = "CPU Log Service";
1194 asyncResp->res.jsonValue["Id"] = "CPU Log";
1195 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1196 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001197 asyncResp->res.jsonValue["Entries"] = {
1198 {"@odata.id",
Jason M. Billsf9fc2df2019-02-26 16:00:37 -08001199 "/redfish/v1/Systems/system/LogServices/CpuLog/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001200 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -07001201 {"Oem",
Jason M. Bills48e46392019-02-13 12:58:37 -08001202 {{"#CpuLog.OnDemand",
Jason M. Billsd53dd412019-02-12 17:16:22 -08001203 {{"target", "/redfish/v1/Systems/system/LogServices/CpuLog/"
Jason M. Bills48e46392019-02-13 12:58:37 -08001204 "Actions/Oem/CpuLog.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001205
1206#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001207 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Ed Tanous1da66f72018-07-27 16:13:37 -07001208 {"#CpuLog.SendRawPeci",
Andrew Geisslercb92c032018-08-17 07:56:14 -07001209 { { "target",
1210 "/redfish/v1/Systems/system/LogServices/CpuLog/"
1211 "Actions/Oem/CpuLog.SendRawPeci" } }});
Ed Tanous1da66f72018-07-27 16:13:37 -07001212#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001213 }
1214};
1215
Jason M. Billse1f26342018-07-18 12:12:00 -07001216class CPULogEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001217{
1218 public:
1219 template <typename CrowApp>
Jason M. Billse1f26342018-07-18 12:12:00 -07001220 CPULogEntryCollection(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001221 Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001222 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001223 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001224 {boost::beast::http::verb::get, {{"Login"}}},
1225 {boost::beast::http::verb::head, {{"Login"}}},
1226 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1227 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1228 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1229 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001230 }
1231
1232 private:
1233 /**
1234 * Functions triggers appropriate requests on DBus
1235 */
1236 void doGet(crow::Response &res, const crow::Request &req,
1237 const std::vector<std::string> &params) override
1238 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001239 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001240 // Collections don't include the static data added by SubRoute because
1241 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001242 auto getLogEntriesCallback = [asyncResp](
1243 const boost::system::error_code ec,
1244 const std::vector<std::string> &resp) {
1245 if (ec)
1246 {
1247 if (ec.value() !=
1248 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001249 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001250 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1251 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001252 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001253 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001254 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001255 }
1256 asyncResp->res.jsonValue["@odata.type"] =
1257 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001258 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsd53dd412019-02-12 17:16:22 -08001259 "/redfish/v1/Systems/system/LogServices/CpuLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001260 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsd53dd412019-02-12 17:16:22 -08001261 "/redfish/v1/"
1262 "$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001263 asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
1264 asyncResp->res.jsonValue["Description"] =
1265 "Collection of CPU Log Entries";
1266 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1267 logEntryArray = nlohmann::json::array();
1268 for (const std::string &objpath : resp)
1269 {
Jason M. Bills48e46392019-02-13 12:58:37 -08001270 // Don't list the on-demand log
1271 if (objpath.compare(cpuLogOnDemandPath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -07001272 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001273 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -07001274 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001275 std::size_t lastPos = objpath.rfind("/");
1276 if (lastPos != std::string::npos)
1277 {
1278 logEntryArray.push_back(
Jason M. Billsd53dd412019-02-12 17:16:22 -08001279 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
Jason M. Billse1f26342018-07-18 12:12:00 -07001280 "CpuLog/Entries/" +
1281 objpath.substr(lastPos + 1)}});
1282 }
1283 }
1284 asyncResp->res.jsonValue["Members@odata.count"] =
1285 logEntryArray.size();
1286 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001287 crow::connections::systemBus->async_method_call(
1288 std::move(getLogEntriesCallback),
1289 "xyz.openbmc_project.ObjectMapper",
1290 "/xyz/openbmc_project/object_mapper",
1291 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001292 std::array<const char *, 1>{cpuLogInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001293 }
1294};
1295
1296std::string getLogCreatedTime(const nlohmann::json &cpuLog)
1297{
Jason M. Billsc4d00432019-02-12 17:17:48 -08001298 nlohmann::json::const_iterator cdIt = cpuLog.find("crashlog_data");
1299 if (cdIt != cpuLog.end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001300 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001301 nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1302 if (siIt != cdIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001303 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001304 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1305 if (tsIt != siIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001306 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001307 const std::string *logTime =
1308 tsIt->get_ptr<const std::string *>();
1309 if (logTime != nullptr)
1310 {
1311 return *logTime;
1312 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001313 }
1314 }
1315 }
1316 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
1317
1318 return std::string();
1319}
1320
Jason M. Billse1f26342018-07-18 12:12:00 -07001321class CPULogEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001322{
1323 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001324 CPULogEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001325 Node(app,
1326 "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001327 std::string())
1328 {
1329 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001330 {boost::beast::http::verb::get, {{"Login"}}},
1331 {boost::beast::http::verb::head, {{"Login"}}},
1332 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1333 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1334 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1335 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001336 }
1337
1338 private:
1339 void doGet(crow::Response &res, const crow::Request &req,
1340 const std::vector<std::string> &params) override
1341 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001342 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001343 if (params.size() != 1)
1344 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001345 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001346 return;
1347 }
Ed Tanousb01bf292019-03-25 19:25:26 +00001348 const uint8_t logId = std::atoi(params[0].c_str());
Ed Tanousabf2add2019-01-22 16:40:12 -08001349 auto getStoredLogCallback = [asyncResp, logId](
1350 const boost::system::error_code ec,
1351 const std::variant<std::string> &resp) {
1352 if (ec)
1353 {
1354 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1355 messages::internalError(asyncResp->res);
1356 return;
1357 }
1358 const std::string *log = std::get_if<std::string>(&resp);
1359 if (log == nullptr)
1360 {
1361 messages::internalError(asyncResp->res);
1362 return;
1363 }
1364 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1365 if (j.is_discarded())
1366 {
1367 messages::internalError(asyncResp->res);
1368 return;
1369 }
1370 std::string t = getLogCreatedTime(j);
1371 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001372 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001373 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1374 {"@odata.id",
Jason M. Billsd53dd412019-02-12 17:16:22 -08001375 "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/" +
Ed Tanousabf2add2019-01-22 16:40:12 -08001376 std::to_string(logId)},
1377 {"Name", "CPU Debug Log"},
1378 {"Id", logId},
1379 {"EntryType", "Oem"},
1380 {"OemRecordFormat", "Intel CPU Log"},
1381 {"Oem", {{"Intel", std::move(j)}}},
1382 {"Created", std::move(t)}};
1383 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001384 crow::connections::systemBus->async_method_call(
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001385 std::move(getStoredLogCallback), cpuLogObject,
1386 cpuLogPath + std::string("/") + std::to_string(logId),
1387 "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001388 }
1389};
1390
Jason M. Bills48e46392019-02-13 12:58:37 -08001391class OnDemandCPULog : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001392{
1393 public:
Jason M. Bills48e46392019-02-13 12:58:37 -08001394 OnDemandCPULog(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001395 Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Actions/Oem/"
Jason M. Bills48e46392019-02-13 12:58:37 -08001396 "CpuLog.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001397 {
1398 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001399 {boost::beast::http::verb::get, {{"Login"}}},
1400 {boost::beast::http::verb::head, {{"Login"}}},
1401 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1402 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1403 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1404 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001405 }
1406
1407 private:
1408 void doPost(crow::Response &res, const crow::Request &req,
1409 const std::vector<std::string> &params) override
1410 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001411 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001412 static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
Ed Tanous1da66f72018-07-27 16:13:37 -07001413
Jason M. Bills48e46392019-02-13 12:58:37 -08001414 // Only allow one OnDemand Log request at a time
1415 if (onDemandLogMatcher != nullptr)
Ed Tanous1da66f72018-07-27 16:13:37 -07001416 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001417 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -07001418 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -07001419 return;
1420 }
1421 // Make this static so it survives outside this method
1422 static boost::asio::deadline_timer timeout(*req.ioService);
1423
1424 timeout.expires_from_now(boost::posix_time::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -07001425 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Jason M. Bills48e46392019-02-13 12:58:37 -08001426 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001427 if (ec)
1428 {
1429 // operation_aborted is expected if timer is canceled before
1430 // completion.
1431 if (ec != boost::asio::error::operation_aborted)
1432 {
1433 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
1434 }
1435 return;
1436 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001437 BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
Ed Tanous1da66f72018-07-27 16:13:37 -07001438
Jason M. Billsf12894f2018-10-09 12:45:45 -07001439 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001440 });
1441
Jason M. Bills48e46392019-02-13 12:58:37 -08001442 auto onDemandLogMatcherCallback = [asyncResp](
1443 sdbusplus::message::message &m) {
1444 BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
Ed Tanous1da66f72018-07-27 16:13:37 -07001445 boost::system::error_code ec;
1446 timeout.cancel(ec);
1447 if (ec)
1448 {
1449 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
1450 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001451 sdbusplus::message::object_path objPath;
Ed Tanous1da66f72018-07-27 16:13:37 -07001452 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -08001453 std::string, boost::container::flat_map<
1454 std::string, std::variant<std::string>>>
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001455 interfacesAdded;
1456 m.read(objPath, interfacesAdded);
Ed Tanousabf2add2019-01-22 16:40:12 -08001457 const std::string *log = std::get_if<std::string>(
1458 &interfacesAdded[cpuLogInterface]["Log"]);
Ed Tanous1da66f72018-07-27 16:13:37 -07001459 if (log == nullptr)
1460 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001461 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001462 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001463 // match object inside which this lambda is executing. Once it
1464 // is set to nullptr, the match object will be destroyed and the
1465 // lambda will lose its context, including res, so it needs to
1466 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001467 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001468 return;
1469 }
1470 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1471 if (j.is_discarded())
1472 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001473 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001474 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001475 // match object inside which this lambda is executing. Once it
1476 // is set to nullptr, the match object will be destroyed and the
1477 // lambda will lose its context, including res, so it needs to
1478 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001479 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001480 return;
1481 }
1482 std::string t = getLogCreatedTime(j);
Jason M. Billse1f26342018-07-18 12:12:00 -07001483 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001484 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001485 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1486 {"Name", "CPU Debug Log"},
1487 {"EntryType", "Oem"},
1488 {"OemRecordFormat", "Intel CPU Log"},
1489 {"Oem", {{"Intel", std::move(j)}}},
1490 {"Created", std::move(t)}};
Jason M. Bills48e46392019-02-13 12:58:37 -08001491 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001492 // match object inside which this lambda is executing. Once it is
1493 // set to nullptr, the match object will be destroyed and the lambda
1494 // will lose its context, including res, so it needs to be the last
1495 // thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001496 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001497 };
Jason M. Bills48e46392019-02-13 12:58:37 -08001498 onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
Ed Tanous1da66f72018-07-27 16:13:37 -07001499 *crow::connections::systemBus,
1500 sdbusplus::bus::match::rules::interfacesAdded() +
Jason M. Bills48e46392019-02-13 12:58:37 -08001501 sdbusplus::bus::match::rules::argNpath(0, cpuLogOnDemandPath),
1502 std::move(onDemandLogMatcherCallback));
Ed Tanous1da66f72018-07-27 16:13:37 -07001503
Jason M. Bills48e46392019-02-13 12:58:37 -08001504 auto generateonDemandLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -07001505 [asyncResp](const boost::system::error_code ec,
1506 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -07001507 if (ec)
1508 {
1509 if (ec.value() ==
1510 boost::system::errc::operation_not_supported)
1511 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001512 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001513 }
1514 else
1515 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001516 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001517 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001518 boost::system::error_code timeoutec;
1519 timeout.cancel(timeoutec);
1520 if (timeoutec)
1521 {
1522 BMCWEB_LOG_ERROR << "error canceling timer "
1523 << timeoutec;
1524 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001525 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001526 return;
1527 }
1528 };
1529 crow::connections::systemBus->async_method_call(
Jason M. Bills48e46392019-02-13 12:58:37 -08001530 std::move(generateonDemandLogCallback), cpuLogObject, cpuLogPath,
1531 cpuLogOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001532 }
1533};
1534
Jason M. Billse1f26342018-07-18 12:12:00 -07001535class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001536{
1537 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001538 SendRawPECI(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001539 Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Actions/Oem/"
Jason M. Billse1f26342018-07-18 12:12:00 -07001540 "CpuLog.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001541 {
1542 entityPrivileges = {
1543 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1544 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1545 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1546 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1547 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1548 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1549 }
1550
1551 private:
1552 void doPost(crow::Response &res, const crow::Request &req,
1553 const std::vector<std::string> &params) override
1554 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001555 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousb1556422018-10-16 14:09:17 -07001556 uint8_t clientAddress = 0;
1557 uint8_t readLength = 0;
Ed Tanous1da66f72018-07-27 16:13:37 -07001558 std::vector<uint8_t> peciCommand;
Ed Tanousb1556422018-10-16 14:09:17 -07001559 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1560 "ReadLength", readLength, "PECICommand",
1561 peciCommand))
Ed Tanous1da66f72018-07-27 16:13:37 -07001562 {
Ed Tanousb1556422018-10-16 14:09:17 -07001563 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001564 }
Ed Tanousb1556422018-10-16 14:09:17 -07001565
Ed Tanous1da66f72018-07-27 16:13:37 -07001566 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001567 auto sendRawPECICallback =
1568 [asyncResp](const boost::system::error_code ec,
1569 const std::vector<uint8_t> &resp) {
1570 if (ec)
1571 {
1572 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
1573 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001574 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001575 return;
1576 }
1577 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1578 {"PECIResponse", resp}};
1579 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001580 // Call the SendRawPECI command with the provided data
1581 crow::connections::systemBus->async_method_call(
Jason M. Billse1f26342018-07-18 12:12:00 -07001582 std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
1583 cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001584 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -07001585 }
1586};
1587
Andrew Geisslercb92c032018-08-17 07:56:14 -07001588/**
1589 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1590 */
1591class DBusLogServiceActionsClear : public Node
1592{
1593 public:
1594 DBusLogServiceActionsClear(CrowApp &app) :
1595 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1596 "LogService.Reset")
1597 {
1598 entityPrivileges = {
1599 {boost::beast::http::verb::get, {{"Login"}}},
1600 {boost::beast::http::verb::head, {{"Login"}}},
1601 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1602 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1603 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1604 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1605 }
1606
1607 private:
1608 /**
1609 * Function handles POST method request.
1610 * The Clear Log actions does not require any parameter.The action deletes
1611 * all entries found in the Entries collection for this Log Service.
1612 */
1613 void doPost(crow::Response &res, const crow::Request &req,
1614 const std::vector<std::string> &params) override
1615 {
1616 BMCWEB_LOG_DEBUG << "Do delete all entries.";
1617
1618 auto asyncResp = std::make_shared<AsyncResp>(res);
1619 // Process response from Logging service.
1620 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1621 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1622 if (ec)
1623 {
1624 // TODO Handle for specific error code
1625 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1626 asyncResp->res.result(
1627 boost::beast::http::status::internal_server_error);
1628 return;
1629 }
1630
1631 asyncResp->res.result(boost::beast::http::status::no_content);
1632 };
1633
1634 // Make call to Logging service to request Clear Log
1635 crow::connections::systemBus->async_method_call(
1636 resp_handler, "xyz.openbmc_project.Logging",
1637 "/xyz/openbmc_project/logging",
1638 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1639 }
1640};
Ed Tanous1da66f72018-07-27 16:13:37 -07001641} // namespace redfish