blob: 90182a106864177d90079b63936b58b6af6e185e [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"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070022
Jason M. Billse1f26342018-07-18 12:12:00 -070023#include <systemd/sd-journal.h>
24
Jason M. Bills4851d452019-03-28 11:27:48 -070025#include <boost/algorithm/string/split.hpp>
26#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070027#include <boost/container/flat_map.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070028#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070029#include <filesystem>
Ed Tanousabf2add2019-01-22 16:40:12 -080030#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070031
32namespace redfish
33{
34
Jason M. Bills424c4172019-03-21 13:50:33 -070035constexpr char const *CrashdumpObject = "com.intel.crashdump";
36constexpr char const *CrashdumpPath = "/com/intel/crashdump";
37constexpr char const *CrashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
38constexpr char const *CrashdumpInterface = "com.intel.crashdump";
39constexpr char const *CrashdumpOnDemandInterface =
40 "com.intel.crashdump.OnDemand";
41constexpr char const *CrashdumpRawPECIInterface =
42 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070043
Jason M. Bills4851d452019-03-28 11:27:48 -070044namespace message_registries
45{
46static const Message *getMessageFromRegistry(
47 const std::string &messageKey,
48 const boost::beast::span<const MessageEntry> registry)
49{
50 boost::beast::span<const MessageEntry>::const_iterator messageIt =
51 std::find_if(registry.cbegin(), registry.cend(),
52 [&messageKey](const MessageEntry &messageEntry) {
53 return !std::strcmp(messageEntry.first,
54 messageKey.c_str());
55 });
56 if (messageIt != registry.cend())
57 {
58 return &messageIt->second;
59 }
60
61 return nullptr;
62}
63
64static const Message *getMessage(const std::string_view &messageID)
65{
66 // Redfish MessageIds are in the form
67 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
68 // the right Message
69 std::vector<std::string> fields;
70 fields.reserve(4);
71 boost::split(fields, messageID, boost::is_any_of("."));
72 std::string &registryName = fields[0];
73 std::string &messageKey = fields[3];
74
75 // Find the right registry and check it for the MessageKey
76 if (std::string(base::header.registryPrefix) == registryName)
77 {
78 return getMessageFromRegistry(
79 messageKey, boost::beast::span<const MessageEntry>(base::registry));
80 }
81 if (std::string(openbmc::header.registryPrefix) == registryName)
82 {
83 return getMessageFromRegistry(
84 messageKey,
85 boost::beast::span<const MessageEntry>(openbmc::registry));
86 }
87 return nullptr;
88}
89} // namespace message_registries
90
James Feistf6150402019-01-08 10:36:20 -080091namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070092
Andrew Geisslercb92c032018-08-17 07:56:14 -070093using GetManagedPropertyType = boost::container::flat_map<
94 std::string,
95 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
96 int32_t, uint32_t, int64_t, uint64_t, double>>;
97
98using GetManagedObjectsType = boost::container::flat_map<
99 sdbusplus::message::object_path,
100 boost::container::flat_map<std::string, GetManagedPropertyType>>;
101
102inline std::string translateSeverityDbusToRedfish(const std::string &s)
103{
104 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
105 {
106 return "Critical";
107 }
108 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
109 {
110 return "Critical";
111 }
112 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
113 {
114 return "OK";
115 }
116 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
117 {
118 return "Critical";
119 }
120 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
121 {
122 return "Critical";
123 }
124 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
125 {
126 return "OK";
127 }
128 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
129 {
130 return "OK";
131 }
132 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
133 {
134 return "Warning";
135 }
136 return "";
137}
138
Jason M. Bills16428a12018-11-02 12:42:29 -0700139static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800140 const std::string_view &field,
141 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700142{
143 const char *data = nullptr;
144 size_t length = 0;
145 int ret = 0;
146 // Get the metadata from the requested field of the journal entry
Ed Tanousb01bf292019-03-25 19:25:26 +0000147 ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
148 &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700149 if (ret < 0)
150 {
151 return ret;
152 }
Ed Tanous39e77502019-03-04 17:35:53 -0800153 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700154 // Only use the content after the "=" character.
155 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
156 return ret;
157}
158
159static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800160 const std::string_view &field, const int &base,
Jason M. Bills16428a12018-11-02 12:42:29 -0700161 int &contents)
162{
163 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800164 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700165 // Get the metadata from the requested field of the journal entry
166 ret = getJournalMetadata(journal, field, metadata);
167 if (ret < 0)
168 {
169 return ret;
170 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000171 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700172 return ret;
173}
174
175static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
176{
177 int ret = 0;
178 uint64_t timestamp = 0;
179 ret = sd_journal_get_realtime_usec(journal, &timestamp);
180 if (ret < 0)
181 {
182 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
183 << strerror(-ret);
184 return false;
185 }
186 time_t t =
187 static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
188 struct tm *loctime = localtime(&t);
189 char entryTime[64] = {};
190 if (NULL != loctime)
191 {
192 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
193 }
194 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800195 std::string_view t1(entryTime);
196 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700197 if (t1.size() > 2 && t2.size() > 2)
198 {
199 t1.remove_suffix(2);
200 t2.remove_prefix(t2.size() - 2);
201 }
Ed Tanous39e77502019-03-04 17:35:53 -0800202 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700203 return true;
204}
205
206static bool getSkipParam(crow::Response &res, const crow::Request &req,
207 long &skip)
208{
209 char *skipParam = req.urlParams.get("$skip");
210 if (skipParam != nullptr)
211 {
212 char *ptr = nullptr;
213 skip = std::strtol(skipParam, &ptr, 10);
214 if (*skipParam == '\0' || *ptr != '\0')
215 {
216
217 messages::queryParameterValueTypeError(res, std::string(skipParam),
218 "$skip");
219 return false;
220 }
221 if (skip < 0)
222 {
223
224 messages::queryParameterOutOfRange(res, std::to_string(skip),
225 "$skip", "greater than 0");
226 return false;
227 }
228 }
229 return true;
230}
231
232static constexpr const long maxEntriesPerPage = 1000;
233static bool getTopParam(crow::Response &res, const crow::Request &req,
234 long &top)
235{
236 char *topParam = req.urlParams.get("$top");
237 if (topParam != nullptr)
238 {
239 char *ptr = nullptr;
240 top = std::strtol(topParam, &ptr, 10);
241 if (*topParam == '\0' || *ptr != '\0')
242 {
243 messages::queryParameterValueTypeError(res, std::string(topParam),
244 "$top");
245 return false;
246 }
247 if (top < 1 || top > maxEntriesPerPage)
248 {
249
250 messages::queryParameterOutOfRange(
251 res, std::to_string(top), "$top",
252 "1-" + std::to_string(maxEntriesPerPage));
253 return false;
254 }
255 }
256 return true;
257}
258
259static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
260{
261 int ret = 0;
262 static uint64_t prevTs = 0;
263 static int index = 0;
264 // Get the entry timestamp
265 uint64_t curTs = 0;
266 ret = sd_journal_get_realtime_usec(journal, &curTs);
267 if (ret < 0)
268 {
269 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
270 << strerror(-ret);
271 return false;
272 }
273 // If the timestamp isn't unique, increment the index
274 if (curTs == prevTs)
275 {
276 index++;
277 }
278 else
279 {
280 // Otherwise, reset it
281 index = 0;
282 }
283 // Save the timestamp
284 prevTs = curTs;
285
286 entryID = std::to_string(curTs);
287 if (index > 0)
288 {
289 entryID += "_" + std::to_string(index);
290 }
291 return true;
292}
293
294static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
295 uint64_t &timestamp, uint16_t &index)
296{
297 if (entryID.empty())
298 {
299 return false;
300 }
301 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800302 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700303
304 auto underscorePos = tsStr.find("_");
305 if (underscorePos != tsStr.npos)
306 {
307 // Timestamp has an index
308 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800309 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700310 indexStr.remove_prefix(underscorePos + 1);
311 std::size_t pos;
312 try
313 {
Ed Tanous39e77502019-03-04 17:35:53 -0800314 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700315 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000316 catch (std::invalid_argument)
Jason M. Bills16428a12018-11-02 12:42:29 -0700317 {
318 messages::resourceMissingAtURI(res, entryID);
319 return false;
320 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000321 catch (std::out_of_range)
Jason M. Bills16428a12018-11-02 12:42:29 -0700322 {
323 messages::resourceMissingAtURI(res, entryID);
324 return false;
325 }
326 if (pos != indexStr.size())
327 {
328 messages::resourceMissingAtURI(res, entryID);
329 return false;
330 }
331 }
332 // Timestamp has no index
333 std::size_t pos;
334 try
335 {
Ed Tanous39e77502019-03-04 17:35:53 -0800336 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700337 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000338 catch (std::invalid_argument)
Jason M. Bills16428a12018-11-02 12:42:29 -0700339 {
340 messages::resourceMissingAtURI(res, entryID);
341 return false;
342 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000343 catch (std::out_of_range)
Jason M. Bills16428a12018-11-02 12:42:29 -0700344 {
345 messages::resourceMissingAtURI(res, entryID);
346 return false;
347 }
348 if (pos != tsStr.size())
349 {
350 messages::resourceMissingAtURI(res, entryID);
351 return false;
352 }
353 return true;
354}
355
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800356class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700357{
358 public:
359 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800360 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800361 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800362 {
363 entityPrivileges = {
364 {boost::beast::http::verb::get, {{"Login"}}},
365 {boost::beast::http::verb::head, {{"Login"}}},
366 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
367 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
368 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
369 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
370 }
371
372 private:
373 /**
374 * Functions triggers appropriate requests on DBus
375 */
376 void doGet(crow::Response &res, const crow::Request &req,
377 const std::vector<std::string> &params) override
378 {
379 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800380 // Collections don't include the static data added by SubRoute because
381 // it has a duplicate entry for members
382 asyncResp->res.jsonValue["@odata.type"] =
383 "#LogServiceCollection.LogServiceCollection";
384 asyncResp->res.jsonValue["@odata.context"] =
385 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
386 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800387 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800388 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
389 asyncResp->res.jsonValue["Description"] =
390 "Collection of LogServices for this Computer System";
391 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
392 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800393 logServiceArray.push_back(
394 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800395#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
396 logServiceArray.push_back(
Andrew Geisslercb92c032018-08-17 07:56:14 -0700397 {{ "@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -0700398 "/redfish/v1/Systems/system/LogServices/Crashdump" }});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800399#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800400 asyncResp->res.jsonValue["Members@odata.count"] =
401 logServiceArray.size();
402 }
403};
404
405class EventLogService : public Node
406{
407 public:
408 template <typename CrowApp>
409 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800410 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800411 {
412 entityPrivileges = {
413 {boost::beast::http::verb::get, {{"Login"}}},
414 {boost::beast::http::verb::head, {{"Login"}}},
415 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
416 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
417 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
418 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
419 }
420
421 private:
422 void doGet(crow::Response &res, const crow::Request &req,
423 const std::vector<std::string> &params) override
424 {
425 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
426
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800427 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800428 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800429 asyncResp->res.jsonValue["@odata.type"] =
430 "#LogService.v1_1_0.LogService";
431 asyncResp->res.jsonValue["@odata.context"] =
432 "/redfish/v1/$metadata#LogService.LogService";
433 asyncResp->res.jsonValue["Name"] = "Event Log Service";
434 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
435 asyncResp->res.jsonValue["Id"] = "Event Log";
436 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
437 asyncResp->res.jsonValue["Entries"] = {
438 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800439 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800440 }
441};
442
Ed Tanous029573d2019-02-01 10:57:49 -0800443static int fillEventLogEntryJson(const std::string &bmcLogEntryID,
Ed Tanous39e77502019-03-04 17:35:53 -0800444 const std::string_view &messageID,
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800445 sd_journal *journal,
446 nlohmann::json &bmcLogEntryJson)
447{
Jason M. Bills4851d452019-03-28 11:27:48 -0700448 // Get the Message from the MessageRegistry
449 const message_registries::Message *message =
450 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800451
Jason M. Bills4851d452019-03-28 11:27:48 -0700452 std::string msg;
453 std::string severity;
454 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800455 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700456 msg = message->message;
457 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800458 }
459
460 // Get the MessageArgs from the journal entry by finding all of the
461 // REDFISH_MESSAGE_ARG_x fields
462 const void *data;
463 size_t length;
464 std::vector<std::string> messageArgs;
465 SD_JOURNAL_FOREACH_DATA(journal, data, length)
466 {
Ed Tanous39e77502019-03-04 17:35:53 -0800467 std::string_view field(static_cast<const char *>(data), length);
468 if (boost::starts_with(field, "REDFISH_MESSAGE_ARG_"))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800469 {
470 // Get the Arg number from the field name
471 field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1);
472 if (field.empty())
473 {
474 continue;
475 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000476 int argNum = std::strtoul(field.data(), nullptr, 10);
477 if (argNum == 0)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800478 {
479 continue;
480 }
481 // Get the Arg value after the "=" character.
482 field.remove_prefix(std::min(field.find("=") + 1, field.size()));
483 // Make sure we have enough space in messageArgs
484 if (argNum > messageArgs.size())
485 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000486 messageArgs.resize(argNum);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800487 }
Ed Tanous39e77502019-03-04 17:35:53 -0800488 messageArgs[argNum - 1] = std::string(field);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800489 }
490 }
491
Jason M. Bills4851d452019-03-28 11:27:48 -0700492 // Fill the MessageArgs into the Message
493 for (size_t i = 0; i < messageArgs.size(); i++)
494 {
495 std::string argStr = "%" + std::to_string(i + 1);
496 size_t argPos = msg.find(argStr);
497 if (argPos != std::string::npos)
498 {
499 msg.replace(argPos, argStr.length(), messageArgs[i]);
500 }
501 }
502
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800503 // Get the Created time from the timestamp
504 std::string entryTimeStr;
505 if (!getEntryTimestamp(journal, entryTimeStr))
506 {
507 return 1;
508 }
509
510 // Fill in the log entry with the gathered data
511 bmcLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700512 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800513 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800514 {"@odata.id",
515 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
516 bmcLogEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800517 {"Name", "System Event Log Entry"},
518 {"Id", bmcLogEntryID},
519 {"Message", msg},
520 {"MessageId", messageID},
521 {"MessageArgs", std::move(messageArgs)},
522 {"EntryType", "Event"},
Jason M. Bills4851d452019-03-28 11:27:48 -0700523 {"Severity", severity},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800524 {"Created", std::move(entryTimeStr)}};
525 return 0;
526}
527
528class EventLogEntryCollection : public Node
529{
530 public:
531 template <typename CrowApp>
532 EventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800533 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800534 {
535 entityPrivileges = {
536 {boost::beast::http::verb::get, {{"Login"}}},
537 {boost::beast::http::verb::head, {{"Login"}}},
538 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
539 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
540 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
541 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
542 }
543
544 private:
545 void doGet(crow::Response &res, const crow::Request &req,
546 const std::vector<std::string> &params) override
547 {
548 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
549 long skip = 0;
550 long top = maxEntriesPerPage; // Show max entries by default
551 if (!getSkipParam(asyncResp->res, req, skip))
552 {
553 return;
554 }
555 if (!getTopParam(asyncResp->res, req, top))
556 {
557 return;
558 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800559 // Collections don't include the static data added by SubRoute because
560 // it has a duplicate entry for members
561 asyncResp->res.jsonValue["@odata.type"] =
562 "#LogEntryCollection.LogEntryCollection";
563 asyncResp->res.jsonValue["@odata.context"] =
564 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
565 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800566 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800567 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
568 asyncResp->res.jsonValue["Description"] =
569 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700570
571#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800572 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
573 logEntryArray = nlohmann::json::array();
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800574 // Go through the journal and create a unique ID for each entry
575 sd_journal *journalTmp = nullptr;
576 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
577 if (ret < 0)
578 {
579 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
580 messages::internalError(asyncResp->res);
581 return;
582 }
583 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
584 journalTmp, sd_journal_close);
585 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +0000586 uint64_t entryCount = 0;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800587 SD_JOURNAL_FOREACH(journal.get())
588 {
589 // Look for only journal entries that contain a REDFISH_MESSAGE_ID
590 // field
Ed Tanous39e77502019-03-04 17:35:53 -0800591 std::string_view messageID;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800592 ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID",
593 messageID);
594 if (ret < 0)
595 {
596 continue;
597 }
598
599 entryCount++;
600 // Handle paging using skip (number of entries to skip from the
601 // start) and top (number of entries to display)
602 if (entryCount <= skip || entryCount > skip + top)
603 {
604 continue;
605 }
606
607 std::string idStr;
608 if (!getUniqueEntryID(journal.get(), idStr))
609 {
610 continue;
611 }
612
613 logEntryArray.push_back({});
614 nlohmann::json &bmcLogEntry = logEntryArray.back();
Ed Tanous029573d2019-02-01 10:57:49 -0800615 if (fillEventLogEntryJson(idStr, messageID, journal.get(),
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800616 bmcLogEntry) != 0)
617 {
618 messages::internalError(asyncResp->res);
619 return;
620 }
621 }
622 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
623 if (skip + top < entryCount)
624 {
625 asyncResp->res.jsonValue["Members@odata.nextLink"] =
626 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
627 std::to_string(skip + top);
628 }
Andrew Geisslercb92c032018-08-17 07:56:14 -0700629#else
630 // DBus implementation of EventLog/Entries
631 // Make call to Logging Service to find all log entry objects
632 crow::connections::systemBus->async_method_call(
633 [asyncResp](const boost::system::error_code ec,
634 GetManagedObjectsType &resp) {
635 if (ec)
636 {
637 // TODO Handle for specific error code
638 BMCWEB_LOG_ERROR
639 << "getLogEntriesIfaceData resp_handler got error "
640 << ec;
641 messages::internalError(asyncResp->res);
642 return;
643 }
644 nlohmann::json &entriesArray =
645 asyncResp->res.jsonValue["Members"];
646 entriesArray = nlohmann::json::array();
647 for (auto &objectPath : resp)
648 {
649 for (auto &interfaceMap : objectPath.second)
650 {
651 if (interfaceMap.first !=
652 "xyz.openbmc_project.Logging.Entry")
653 {
654 BMCWEB_LOG_DEBUG << "Bailing early on "
655 << interfaceMap.first;
656 continue;
657 }
658 entriesArray.push_back({});
659 nlohmann::json &thisEntry = entriesArray.back();
660 uint32_t *id;
661 std::time_t timestamp;
662 std::string *severity, *message;
663 bool *resolved;
664 for (auto &propertyMap : interfaceMap.second)
665 {
666 if (propertyMap.first == "Id")
667 {
668 id = sdbusplus::message::variant_ns::get_if<
669 uint32_t>(&propertyMap.second);
670 if (id == nullptr)
671 {
672 messages::propertyMissing(asyncResp->res,
673 "Id");
674 }
675 }
676 else if (propertyMap.first == "Timestamp")
677 {
678 const uint64_t *millisTimeStamp =
679 std::get_if<uint64_t>(&propertyMap.second);
680 if (millisTimeStamp == nullptr)
681 {
682 messages::propertyMissing(asyncResp->res,
683 "Timestamp");
684 }
685 // Retrieve Created property with format:
686 // yyyy-mm-ddThh:mm:ss
687 std::chrono::milliseconds chronoTimeStamp(
688 *millisTimeStamp);
689 timestamp =
690 std::chrono::duration_cast<
691 std::chrono::seconds>(chronoTimeStamp)
692 .count();
693 }
694 else if (propertyMap.first == "Severity")
695 {
696 severity = std::get_if<std::string>(
697 &propertyMap.second);
698 if (severity == nullptr)
699 {
700 messages::propertyMissing(asyncResp->res,
701 "Severity");
702 }
703 }
704 else if (propertyMap.first == "Message")
705 {
706 message = std::get_if<std::string>(
707 &propertyMap.second);
708 if (message == nullptr)
709 {
710 messages::propertyMissing(asyncResp->res,
711 "Message");
712 }
713 }
714 }
715 thisEntry = {
716 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
717 {"@odata.context", "/redfish/v1/"
718 "$metadata#LogEntry.LogEntry"},
719 {"@odata.id",
720 "/redfish/v1/Systems/system/LogServices/EventLog/"
721 "Entries/" +
722 std::to_string(*id)},
723 {"Name", "System DBus Event Log Entry"},
724 {"Id", std::to_string(*id)},
725 {"Message", *message},
726 {"EntryType", "Event"},
727 {"Severity",
728 translateSeverityDbusToRedfish(*severity)},
729 {"Created", crow::utility::getDateTime(timestamp)}};
730 }
731 }
732 std::sort(entriesArray.begin(), entriesArray.end(),
733 [](const nlohmann::json &left,
734 const nlohmann::json &right) {
735 return (left["Id"] <= right["Id"]);
736 });
737 asyncResp->res.jsonValue["Members@odata.count"] =
738 entriesArray.size();
739 },
740 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
741 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
742#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800743 }
744};
745
746class EventLogEntry : public Node
747{
748 public:
749 EventLogEntry(CrowApp &app) :
750 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -0800751 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
752 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800753 {
754 entityPrivileges = {
755 {boost::beast::http::verb::get, {{"Login"}}},
756 {boost::beast::http::verb::head, {{"Login"}}},
757 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
758 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
759 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
760 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
761 }
762
763 private:
764 void doGet(crow::Response &res, const crow::Request &req,
765 const std::vector<std::string> &params) override
766 {
767 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -0800768 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800769 {
770 messages::internalError(asyncResp->res);
771 return;
772 }
Ed Tanous029573d2019-02-01 10:57:49 -0800773 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -0700774
775#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800776 // Convert the unique ID back to a timestamp to find the entry
777 uint64_t ts = 0;
778 uint16_t index = 0;
779 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
780 {
781 return;
782 }
783
784 sd_journal *journalTmp = nullptr;
785 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
786 if (ret < 0)
787 {
788 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
789 messages::internalError(asyncResp->res);
790 return;
791 }
792 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
793 journalTmp, sd_journal_close);
794 journalTmp = nullptr;
795 // Go to the timestamp in the log and move to the entry at the index
796 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
797 for (int i = 0; i <= index; i++)
798 {
799 sd_journal_next(journal.get());
800 }
801 // Confirm that the entry ID matches what was requested
802 std::string idStr;
803 if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
804 {
805 messages::resourceMissingAtURI(asyncResp->res, entryID);
806 return;
807 }
808
Jason M. Billscd50aa42019-02-12 17:09:02 -0800809 // only use journal entries that contain a REDFISH_MESSAGE_ID field
Ed Tanous39e77502019-03-04 17:35:53 -0800810 std::string_view messageID;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800811 ret =
812 getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID);
813 if (ret < 0)
814 {
Ed Tanous029573d2019-02-01 10:57:49 -0800815 messages::resourceNotFound(asyncResp->res, "LogEntry", "system");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800816 return;
817 }
818
Ed Tanous029573d2019-02-01 10:57:49 -0800819 if (fillEventLogEntryJson(entryID, messageID, journal.get(),
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800820 asyncResp->res.jsonValue) != 0)
821 {
822 messages::internalError(asyncResp->res);
823 return;
824 }
Andrew Geisslercb92c032018-08-17 07:56:14 -0700825#else
826 // DBus implementation of EventLog/Entries
827 // Make call to Logging Service to find all log entry objects
828 crow::connections::systemBus->async_method_call(
829 [asyncResp, entryID](const boost::system::error_code ec,
830 GetManagedPropertyType &resp) {
831 if (ec)
832 {
833 BMCWEB_LOG_ERROR
834 << "EventLogEntry (DBus) resp_handler got error " << ec;
835 messages::internalError(asyncResp->res);
836 return;
837 }
838 uint32_t *id;
839 std::time_t timestamp;
840 std::string *severity, *message;
841 bool *resolved;
842 for (auto &propertyMap : resp)
843 {
844 if (propertyMap.first == "Id")
845 {
846 id = std::get_if<uint32_t>(&propertyMap.second);
847 if (id == nullptr)
848 {
849 messages::propertyMissing(asyncResp->res, "Id");
850 }
851 }
852 else if (propertyMap.first == "Timestamp")
853 {
854 const uint64_t *millisTimeStamp =
855 std::get_if<uint64_t>(&propertyMap.second);
856 if (millisTimeStamp == nullptr)
857 {
858 messages::propertyMissing(asyncResp->res,
859 "Timestamp");
860 }
861 // Retrieve Created property with format:
862 // yyyy-mm-ddThh:mm:ss
863 std::chrono::milliseconds chronoTimeStamp(
864 *millisTimeStamp);
865 timestamp =
866 std::chrono::duration_cast<std::chrono::seconds>(
867 chronoTimeStamp)
868 .count();
869 }
870 else if (propertyMap.first == "Severity")
871 {
872 severity =
873 std::get_if<std::string>(&propertyMap.second);
874 if (severity == nullptr)
875 {
876 messages::propertyMissing(asyncResp->res,
877 "Severity");
878 }
879 }
880 else if (propertyMap.first == "Message")
881 {
882 message = std::get_if<std::string>(&propertyMap.second);
883 if (message == nullptr)
884 {
885 messages::propertyMissing(asyncResp->res,
886 "Message");
887 }
888 }
889 }
890 asyncResp->res.jsonValue = {
891 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
892 {"@odata.context", "/redfish/v1/"
893 "$metadata#LogEntry.LogEntry"},
894 {"@odata.id",
895 "/redfish/v1/Systems/system/LogServices/EventLog/"
896 "Entries/" +
897 std::to_string(*id)},
898 {"Name", "System DBus Event Log Entry"},
899 {"Id", std::to_string(*id)},
900 {"Message", *message},
901 {"EntryType", "Event"},
902 {"Severity", translateSeverityDbusToRedfish(*severity)},
903 {"Created", crow::utility::getDateTime(timestamp)}};
904 },
905 "xyz.openbmc_project.Logging",
906 "/xyz/openbmc_project/logging/entry/" + entryID,
907 "org.freedesktop.DBus.Properties", "GetAll",
908 "xyz.openbmc_project.Logging.Entry");
909#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800910 }
911};
912
913class BMCLogServiceCollection : public Node
914{
915 public:
916 template <typename CrowApp>
917 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700918 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700919 {
Ed Tanous1da66f72018-07-27 16:13:37 -0700920 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700921 {boost::beast::http::verb::get, {{"Login"}}},
922 {boost::beast::http::verb::head, {{"Login"}}},
923 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
924 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
925 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
926 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700927 }
928
929 private:
930 /**
931 * Functions triggers appropriate requests on DBus
932 */
933 void doGet(crow::Response &res, const crow::Request &req,
934 const std::vector<std::string> &params) override
935 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700936 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700937 // Collections don't include the static data added by SubRoute because
938 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -0700939 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -0700940 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -0700941 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800942 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -0700943 asyncResp->res.jsonValue["@odata.id"] =
944 "/redfish/v1/Managers/bmc/LogServices";
945 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
946 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -0700947 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800948 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
949 logServiceArray = nlohmann::json::array();
950#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
951 logServiceArray.push_back(
Andrew Geisslercb92c032018-08-17 07:56:14 -0700952 {{ "@odata.id",
953 "/redfish/v1/Managers/bmc/LogServices/Journal" }});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800954#endif
Jason M. Billse1f26342018-07-18 12:12:00 -0700955 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800956 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -0700957 }
958};
959
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800960class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700961{
962 public:
963 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800964 BMCJournalLogService(CrowApp &app) :
965 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -0700966 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700967 entityPrivileges = {
968 {boost::beast::http::verb::get, {{"Login"}}},
969 {boost::beast::http::verb::head, {{"Login"}}},
970 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
971 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
972 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
973 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
974 }
975
976 private:
977 void doGet(crow::Response &res, const crow::Request &req,
978 const std::vector<std::string> &params) override
979 {
980 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700981 asyncResp->res.jsonValue["@odata.type"] =
982 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -0800983 asyncResp->res.jsonValue["@odata.id"] =
984 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -0700985 asyncResp->res.jsonValue["@odata.context"] =
986 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800987 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
988 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
989 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -0700990 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -0800991 asyncResp->res.jsonValue["Entries"] = {
992 {"@odata.id",
993 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}};
Jason M. Billse1f26342018-07-18 12:12:00 -0700994 }
995};
996
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800997static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
998 sd_journal *journal,
999 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001000{
1001 // Get the Log Entry contents
1002 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001003
Ed Tanous39e77502019-03-04 17:35:53 -08001004 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001005 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001006 if (ret < 0)
1007 {
1008 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1009 return 1;
1010 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001011
1012 // Get the severity from the PRIORITY field
Jason M. Billse1f26342018-07-18 12:12:00 -07001013 int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001014 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001015 if (ret < 0)
1016 {
1017 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
1018 return 1;
1019 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001020
1021 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001022 std::string entryTimeStr;
1023 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001024 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001025 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001026 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001027
1028 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001029 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001030 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001031 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001032 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1033 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001034 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001035 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001036 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001037 {"EntryType", "Oem"},
1038 {"Severity",
1039 severity <= 2 ? "Critical"
1040 : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
1041 {"OemRecordFormat", "Intel BMC Journal Entry"},
1042 {"Created", std::move(entryTimeStr)}};
1043 return 0;
1044}
1045
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001046class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001047{
1048 public:
1049 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001050 BMCJournalLogEntryCollection(CrowApp &app) :
1051 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001052 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001053 entityPrivileges = {
1054 {boost::beast::http::verb::get, {{"Login"}}},
1055 {boost::beast::http::verb::head, {{"Login"}}},
1056 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1057 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1058 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1059 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1060 }
1061
1062 private:
1063 void doGet(crow::Response &res, const crow::Request &req,
1064 const std::vector<std::string> &params) override
1065 {
1066 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001067 static constexpr const long maxEntriesPerPage = 1000;
1068 long skip = 0;
1069 long top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001070 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001071 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001072 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001073 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001074 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001075 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001076 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001077 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001078 // Collections don't include the static data added by SubRoute because
1079 // it has a duplicate entry for members
1080 asyncResp->res.jsonValue["@odata.type"] =
1081 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001082 asyncResp->res.jsonValue["@odata.id"] =
1083 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001084 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001085 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001086 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001087 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001088 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1089 asyncResp->res.jsonValue["Description"] =
1090 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001091 asyncResp->res.jsonValue["@odata.id"] =
1092 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001093 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1094 logEntryArray = nlohmann::json::array();
1095
1096 // Go through the journal and use the timestamp to create a unique ID
1097 // for each entry
1098 sd_journal *journalTmp = nullptr;
1099 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1100 if (ret < 0)
1101 {
1102 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001103 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001104 return;
1105 }
1106 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1107 journalTmp, sd_journal_close);
1108 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001109 uint64_t entryCount = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001110 SD_JOURNAL_FOREACH(journal.get())
1111 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001112 entryCount++;
1113 // Handle paging using skip (number of entries to skip from the
1114 // start) and top (number of entries to display)
1115 if (entryCount <= skip || entryCount > skip + top)
1116 {
1117 continue;
1118 }
1119
Jason M. Bills16428a12018-11-02 12:42:29 -07001120 std::string idStr;
1121 if (!getUniqueEntryID(journal.get(), idStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001122 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001123 continue;
1124 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001125
Jason M. Billse1f26342018-07-18 12:12:00 -07001126 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001127 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1128 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1129 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001130 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001131 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001132 return;
1133 }
1134 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001135 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1136 if (skip + top < entryCount)
1137 {
1138 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001139 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001140 std::to_string(skip + top);
1141 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001142 }
1143};
1144
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001145class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001146{
1147 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001148 BMCJournalLogEntry(CrowApp &app) :
1149 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001150 std::string())
1151 {
1152 entityPrivileges = {
1153 {boost::beast::http::verb::get, {{"Login"}}},
1154 {boost::beast::http::verb::head, {{"Login"}}},
1155 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1156 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1157 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1158 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1159 }
1160
1161 private:
1162 void doGet(crow::Response &res, const crow::Request &req,
1163 const std::vector<std::string> &params) override
1164 {
1165 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1166 if (params.size() != 1)
1167 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001168 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001169 return;
1170 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001171 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001172 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001173 uint64_t ts = 0;
1174 uint16_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001175 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001176 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001177 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001178 }
1179
1180 sd_journal *journalTmp = nullptr;
1181 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1182 if (ret < 0)
1183 {
1184 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001185 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001186 return;
1187 }
1188 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1189 journalTmp, sd_journal_close);
1190 journalTmp = nullptr;
1191 // Go to the timestamp in the log and move to the entry at the index
1192 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1193 for (int i = 0; i <= index; i++)
1194 {
1195 sd_journal_next(journal.get());
1196 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001197 // Confirm that the entry ID matches what was requested
1198 std::string idStr;
1199 if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
1200 {
1201 messages::resourceMissingAtURI(asyncResp->res, entryID);
1202 return;
1203 }
1204
1205 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1206 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001207 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001208 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001209 return;
1210 }
1211 }
1212};
1213
Jason M. Bills424c4172019-03-21 13:50:33 -07001214class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001215{
1216 public:
1217 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001218 CrashdumpService(CrowApp &app) :
1219 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001220 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001221 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001222 {boost::beast::http::verb::get, {{"Login"}}},
1223 {boost::beast::http::verb::head, {{"Login"}}},
1224 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1225 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1226 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1227 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001228 }
1229
1230 private:
1231 /**
1232 * Functions triggers appropriate requests on DBus
1233 */
1234 void doGet(crow::Response &res, const crow::Request &req,
1235 const std::vector<std::string> &params) override
1236 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001237 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001238 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001239 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001240 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001241 asyncResp->res.jsonValue["@odata.type"] =
1242 "#LogService.v1_1_0.LogService";
1243 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001244 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Bills424c4172019-03-21 13:50:33 -07001245 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1246 asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1247 asyncResp->res.jsonValue["Id"] = "Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001248 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1249 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001250 asyncResp->res.jsonValue["Entries"] = {
1251 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001252 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001253 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -07001254 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001255 {{"#Crashdump.OnDemand",
1256 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1257 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001258
1259#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001260 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001261 {"#Crashdump.SendRawPeci",
Andrew Geisslercb92c032018-08-17 07:56:14 -07001262 { { "target",
Jason M. Bills424c4172019-03-21 13:50:33 -07001263 "/redfish/v1/Systems/system/LogServices/Crashdump/"
1264 "Actions/Oem/Crashdump.SendRawPeci" } }});
Ed Tanous1da66f72018-07-27 16:13:37 -07001265#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001266 }
1267};
1268
Jason M. Bills424c4172019-03-21 13:50:33 -07001269class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001270{
1271 public:
1272 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001273 CrashdumpEntryCollection(CrowApp &app) :
1274 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001275 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001276 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001277 {boost::beast::http::verb::get, {{"Login"}}},
1278 {boost::beast::http::verb::head, {{"Login"}}},
1279 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1280 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1281 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1282 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001283 }
1284
1285 private:
1286 /**
1287 * Functions triggers appropriate requests on DBus
1288 */
1289 void doGet(crow::Response &res, const crow::Request &req,
1290 const std::vector<std::string> &params) override
1291 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001292 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001293 // Collections don't include the static data added by SubRoute because
1294 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001295 auto getLogEntriesCallback = [asyncResp](
1296 const boost::system::error_code ec,
1297 const std::vector<std::string> &resp) {
1298 if (ec)
1299 {
1300 if (ec.value() !=
1301 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001302 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001303 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1304 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001305 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001306 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001307 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001308 }
1309 asyncResp->res.jsonValue["@odata.type"] =
1310 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001311 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001312 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001313 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsd53dd412019-02-12 17:16:22 -08001314 "/redfish/v1/"
1315 "$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Bills424c4172019-03-21 13:50:33 -07001316 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001317 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001318 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001319 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1320 logEntryArray = nlohmann::json::array();
1321 for (const std::string &objpath : resp)
1322 {
Jason M. Bills48e46392019-02-13 12:58:37 -08001323 // Don't list the on-demand log
Jason M. Bills424c4172019-03-21 13:50:33 -07001324 if (objpath.compare(CrashdumpOnDemandPath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -07001325 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001326 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -07001327 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001328 std::size_t lastPos = objpath.rfind("/");
1329 if (lastPos != std::string::npos)
1330 {
1331 logEntryArray.push_back(
Jason M. Billsd53dd412019-02-12 17:16:22 -08001332 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
Jason M. Bills424c4172019-03-21 13:50:33 -07001333 "Crashdump/Entries/" +
Jason M. Billse1f26342018-07-18 12:12:00 -07001334 objpath.substr(lastPos + 1)}});
1335 }
1336 }
1337 asyncResp->res.jsonValue["Members@odata.count"] =
1338 logEntryArray.size();
1339 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001340 crow::connections::systemBus->async_method_call(
1341 std::move(getLogEntriesCallback),
1342 "xyz.openbmc_project.ObjectMapper",
1343 "/xyz/openbmc_project/object_mapper",
1344 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills424c4172019-03-21 13:50:33 -07001345 std::array<const char *, 1>{CrashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001346 }
1347};
1348
Jason M. Bills424c4172019-03-21 13:50:33 -07001349std::string getLogCreatedTime(const nlohmann::json &Crashdump)
Ed Tanous1da66f72018-07-27 16:13:37 -07001350{
Jason M. Bills424c4172019-03-21 13:50:33 -07001351 nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1352 if (cdIt != Crashdump.end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001353 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001354 nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1355 if (siIt != cdIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001356 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001357 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1358 if (tsIt != siIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001359 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001360 const std::string *logTime =
1361 tsIt->get_ptr<const std::string *>();
1362 if (logTime != nullptr)
1363 {
1364 return *logTime;
1365 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001366 }
1367 }
1368 }
1369 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
1370
1371 return std::string();
1372}
1373
Jason M. Bills424c4172019-03-21 13:50:33 -07001374class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001375{
1376 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001377 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001378 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001379 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001380 std::string())
1381 {
1382 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001383 {boost::beast::http::verb::get, {{"Login"}}},
1384 {boost::beast::http::verb::head, {{"Login"}}},
1385 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1386 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1387 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1388 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001389 }
1390
1391 private:
1392 void doGet(crow::Response &res, const crow::Request &req,
1393 const std::vector<std::string> &params) override
1394 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001395 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001396 if (params.size() != 1)
1397 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001398 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001399 return;
1400 }
Ed Tanousb01bf292019-03-25 19:25:26 +00001401 const uint8_t logId = std::atoi(params[0].c_str());
Ed Tanousabf2add2019-01-22 16:40:12 -08001402 auto getStoredLogCallback = [asyncResp, logId](
1403 const boost::system::error_code ec,
1404 const std::variant<std::string> &resp) {
1405 if (ec)
1406 {
1407 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1408 messages::internalError(asyncResp->res);
1409 return;
1410 }
1411 const std::string *log = std::get_if<std::string>(&resp);
1412 if (log == nullptr)
1413 {
1414 messages::internalError(asyncResp->res);
1415 return;
1416 }
1417 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1418 if (j.is_discarded())
1419 {
1420 messages::internalError(asyncResp->res);
1421 return;
1422 }
1423 std::string t = getLogCreatedTime(j);
1424 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001425 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001426 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1427 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001428 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
Ed Tanousabf2add2019-01-22 16:40:12 -08001429 std::to_string(logId)},
Jason M. Bills424c4172019-03-21 13:50:33 -07001430 {"Name", "CPU Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001431 {"Id", logId},
1432 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001433 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001434 {"Oem", {{"Intel", std::move(j)}}},
1435 {"Created", std::move(t)}};
1436 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001437 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001438 std::move(getStoredLogCallback), CrashdumpObject,
1439 CrashdumpPath + std::string("/") + std::to_string(logId),
1440 "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1441 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001442 }
1443};
1444
Jason M. Bills424c4172019-03-21 13:50:33 -07001445class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001446{
1447 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001448 OnDemandCrashdump(CrowApp &app) :
1449 Node(app,
1450 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1451 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001452 {
1453 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001454 {boost::beast::http::verb::get, {{"Login"}}},
1455 {boost::beast::http::verb::head, {{"Login"}}},
1456 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1457 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1458 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1459 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001460 }
1461
1462 private:
1463 void doPost(crow::Response &res, const crow::Request &req,
1464 const std::vector<std::string> &params) override
1465 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001466 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001467 static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
Ed Tanous1da66f72018-07-27 16:13:37 -07001468
Jason M. Bills48e46392019-02-13 12:58:37 -08001469 // Only allow one OnDemand Log request at a time
1470 if (onDemandLogMatcher != nullptr)
Ed Tanous1da66f72018-07-27 16:13:37 -07001471 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001472 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -07001473 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -07001474 return;
1475 }
1476 // Make this static so it survives outside this method
1477 static boost::asio::deadline_timer timeout(*req.ioService);
1478
1479 timeout.expires_from_now(boost::posix_time::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -07001480 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Jason M. Bills48e46392019-02-13 12:58:37 -08001481 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001482 if (ec)
1483 {
1484 // operation_aborted is expected if timer is canceled before
1485 // completion.
1486 if (ec != boost::asio::error::operation_aborted)
1487 {
1488 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
1489 }
1490 return;
1491 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001492 BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
Ed Tanous1da66f72018-07-27 16:13:37 -07001493
Jason M. Billsf12894f2018-10-09 12:45:45 -07001494 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001495 });
1496
Jason M. Bills48e46392019-02-13 12:58:37 -08001497 auto onDemandLogMatcherCallback = [asyncResp](
1498 sdbusplus::message::message &m) {
1499 BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
Ed Tanous1da66f72018-07-27 16:13:37 -07001500 boost::system::error_code ec;
1501 timeout.cancel(ec);
1502 if (ec)
1503 {
1504 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
1505 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001506 sdbusplus::message::object_path objPath;
Ed Tanous1da66f72018-07-27 16:13:37 -07001507 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -08001508 std::string, boost::container::flat_map<
1509 std::string, std::variant<std::string>>>
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001510 interfacesAdded;
1511 m.read(objPath, interfacesAdded);
Ed Tanousabf2add2019-01-22 16:40:12 -08001512 const std::string *log = std::get_if<std::string>(
Jason M. Bills424c4172019-03-21 13:50:33 -07001513 &interfacesAdded[CrashdumpInterface]["Log"]);
Ed Tanous1da66f72018-07-27 16:13:37 -07001514 if (log == nullptr)
1515 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001516 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001517 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001518 // match object inside which this lambda is executing. Once it
1519 // is set to nullptr, the match object will be destroyed and the
1520 // lambda will lose its context, including res, so it needs to
1521 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001522 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001523 return;
1524 }
1525 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1526 if (j.is_discarded())
1527 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001528 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001529 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001530 // match object inside which this lambda is executing. Once it
1531 // is set to nullptr, the match object will be destroyed and the
1532 // lambda will lose its context, including res, so it needs to
1533 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001534 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001535 return;
1536 }
1537 std::string t = getLogCreatedTime(j);
Jason M. Billse1f26342018-07-18 12:12:00 -07001538 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001539 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001540 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001541 {"Name", "CPU Crashdump"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001542 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001543 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001544 {"Oem", {{"Intel", std::move(j)}}},
1545 {"Created", std::move(t)}};
Jason M. Bills48e46392019-02-13 12:58:37 -08001546 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001547 // match object inside which this lambda is executing. Once it is
1548 // set to nullptr, the match object will be destroyed and the lambda
1549 // will lose its context, including res, so it needs to be the last
1550 // thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001551 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001552 };
Jason M. Bills48e46392019-02-13 12:58:37 -08001553 onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
Ed Tanous1da66f72018-07-27 16:13:37 -07001554 *crow::connections::systemBus,
1555 sdbusplus::bus::match::rules::interfacesAdded() +
Jason M. Bills424c4172019-03-21 13:50:33 -07001556 sdbusplus::bus::match::rules::argNpath(0,
1557 CrashdumpOnDemandPath),
Jason M. Bills48e46392019-02-13 12:58:37 -08001558 std::move(onDemandLogMatcherCallback));
Ed Tanous1da66f72018-07-27 16:13:37 -07001559
Jason M. Bills48e46392019-02-13 12:58:37 -08001560 auto generateonDemandLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -07001561 [asyncResp](const boost::system::error_code ec,
1562 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -07001563 if (ec)
1564 {
1565 if (ec.value() ==
1566 boost::system::errc::operation_not_supported)
1567 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001568 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001569 }
1570 else
1571 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001572 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001573 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001574 boost::system::error_code timeoutec;
1575 timeout.cancel(timeoutec);
1576 if (timeoutec)
1577 {
1578 BMCWEB_LOG_ERROR << "error canceling timer "
1579 << timeoutec;
1580 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001581 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001582 return;
1583 }
1584 };
1585 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001586 std::move(generateonDemandLogCallback), CrashdumpObject,
1587 CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001588 }
1589};
1590
Jason M. Billse1f26342018-07-18 12:12:00 -07001591class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001592{
1593 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001594 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001595 Node(app,
1596 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1597 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001598 {
1599 entityPrivileges = {
1600 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1601 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1602 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1603 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1604 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1605 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1606 }
1607
1608 private:
1609 void doPost(crow::Response &res, const crow::Request &req,
1610 const std::vector<std::string> &params) override
1611 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001612 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousb1556422018-10-16 14:09:17 -07001613 uint8_t clientAddress = 0;
1614 uint8_t readLength = 0;
Ed Tanous1da66f72018-07-27 16:13:37 -07001615 std::vector<uint8_t> peciCommand;
Ed Tanousb1556422018-10-16 14:09:17 -07001616 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1617 "ReadLength", readLength, "PECICommand",
1618 peciCommand))
Ed Tanous1da66f72018-07-27 16:13:37 -07001619 {
Ed Tanousb1556422018-10-16 14:09:17 -07001620 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001621 }
Ed Tanousb1556422018-10-16 14:09:17 -07001622
Ed Tanous1da66f72018-07-27 16:13:37 -07001623 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001624 auto sendRawPECICallback =
1625 [asyncResp](const boost::system::error_code ec,
1626 const std::vector<uint8_t> &resp) {
1627 if (ec)
1628 {
1629 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
1630 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001631 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001632 return;
1633 }
1634 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1635 {"PECIResponse", resp}};
1636 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001637 // Call the SendRawPECI command with the provided data
1638 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001639 std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1640 CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001641 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -07001642 }
1643};
1644
Andrew Geisslercb92c032018-08-17 07:56:14 -07001645/**
1646 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1647 */
1648class DBusLogServiceActionsClear : public Node
1649{
1650 public:
1651 DBusLogServiceActionsClear(CrowApp &app) :
1652 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1653 "LogService.Reset")
1654 {
1655 entityPrivileges = {
1656 {boost::beast::http::verb::get, {{"Login"}}},
1657 {boost::beast::http::verb::head, {{"Login"}}},
1658 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1659 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1660 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1661 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1662 }
1663
1664 private:
1665 /**
1666 * Function handles POST method request.
1667 * The Clear Log actions does not require any parameter.The action deletes
1668 * all entries found in the Entries collection for this Log Service.
1669 */
1670 void doPost(crow::Response &res, const crow::Request &req,
1671 const std::vector<std::string> &params) override
1672 {
1673 BMCWEB_LOG_DEBUG << "Do delete all entries.";
1674
1675 auto asyncResp = std::make_shared<AsyncResp>(res);
1676 // Process response from Logging service.
1677 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1678 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1679 if (ec)
1680 {
1681 // TODO Handle for specific error code
1682 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1683 asyncResp->res.result(
1684 boost::beast::http::status::internal_server_error);
1685 return;
1686 }
1687
1688 asyncResp->res.result(boost::beast::http::status::no_content);
1689 };
1690
1691 // Make call to Logging service to request Clear Log
1692 crow::connections::systemBus->async_method_call(
1693 resp_handler, "xyz.openbmc_project.Logging",
1694 "/xyz/openbmc_project/logging",
1695 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1696 }
1697};
Ed Tanous1da66f72018-07-27 16:13:37 -07001698} // namespace redfish